sync
[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     bodyEl : function()
1297     {
1298         if(!this.el || !this.panel.length){
1299             return;
1300         }
1301         
1302         return this.el.select('.panel-body',true).first()
1303     },
1304     
1305     titleEl : function()
1306     {
1307         if(!this.el || !this.panel.length || !this.header.length){
1308             return;
1309         }
1310         
1311         return this.el.select('.panel-title',true).first();
1312     },
1313     
1314     setTitle : function(v)
1315     {
1316         var titleEl = this.titleEl();
1317         
1318         if(!titleEl){
1319             return;
1320         }
1321         
1322         titleEl.dom.innerHTML = v;
1323     },
1324     
1325     getTitle : function()
1326     {
1327         
1328         var titleEl = this.titleEl();
1329         
1330         if(!titleEl){
1331             return '';
1332         }
1333         
1334         return titleEl.dom.innerHTML;
1335     },
1336     
1337     setRightTitle : function(v)
1338     {
1339         var t = this.el.select('.panel-header-right',true).first();
1340         
1341         if(!t){
1342             return;
1343         }
1344         
1345         t.dom.innerHTML = v;
1346     },
1347     
1348     onClick : function(e)
1349     {
1350         e.preventDefault();
1351         
1352         this.fireEvent('click', this, e);
1353     }
1354    
1355 });
1356
1357  /*
1358  * - LGPL
1359  *
1360  * image
1361  * 
1362  */
1363
1364
1365 /**
1366  * @class Roo.bootstrap.Img
1367  * @extends Roo.bootstrap.Component
1368  * Bootstrap Img class
1369  * @cfg {Boolean} imgResponsive false | true
1370  * @cfg {String} border rounded | circle | thumbnail
1371  * @cfg {String} src image source
1372  * @cfg {String} alt image alternative text
1373  * @cfg {String} href a tag href
1374  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1375  * @cfg {String} xsUrl xs image source
1376  * @cfg {String} smUrl sm image source
1377  * @cfg {String} mdUrl md image source
1378  * @cfg {String} lgUrl lg image source
1379  * 
1380  * @constructor
1381  * Create a new Input
1382  * @param {Object} config The config object
1383  */
1384
1385 Roo.bootstrap.Img = function(config){
1386     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1387     
1388     this.addEvents({
1389         // img events
1390         /**
1391          * @event click
1392          * The img click event for the img.
1393          * @param {Roo.EventObject} e
1394          */
1395         "click" : true
1396     });
1397 };
1398
1399 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1400     
1401     imgResponsive: true,
1402     border: '',
1403     src: 'about:blank',
1404     href: false,
1405     target: false,
1406     xsUrl: '',
1407     smUrl: '',
1408     mdUrl: '',
1409     lgUrl: '',
1410
1411     getAutoCreate : function()
1412     {   
1413         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1414             return this.createSingleImg();
1415         }
1416         
1417         var cfg = {
1418             tag: 'div',
1419             cls: 'roo-image-responsive-group',
1420             cn: []
1421         };
1422         var _this = this;
1423         
1424         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1425             
1426             if(!_this[size + 'Url']){
1427                 return;
1428             }
1429             
1430             var img = {
1431                 tag: 'img',
1432                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1433                 html: _this.html || cfg.html,
1434                 src: _this[size + 'Url']
1435             };
1436             
1437             img.cls += ' roo-image-responsive-' + size;
1438             
1439             var s = ['xs', 'sm', 'md', 'lg'];
1440             
1441             s.splice(s.indexOf(size), 1);
1442             
1443             Roo.each(s, function(ss){
1444                 img.cls += ' hidden-' + ss;
1445             });
1446             
1447             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1448                 cfg.cls += ' img-' + _this.border;
1449             }
1450             
1451             if(_this.alt){
1452                 cfg.alt = _this.alt;
1453             }
1454             
1455             if(_this.href){
1456                 var a = {
1457                     tag: 'a',
1458                     href: _this.href,
1459                     cn: [
1460                         img
1461                     ]
1462                 };
1463
1464                 if(this.target){
1465                     a.target = _this.target;
1466                 }
1467             }
1468             
1469             cfg.cn.push((_this.href) ? a : img);
1470             
1471         });
1472         
1473         return cfg;
1474     },
1475     
1476     createSingleImg : function()
1477     {
1478         var cfg = {
1479             tag: 'img',
1480             cls: (this.imgResponsive) ? 'img-responsive' : '',
1481             html : null,
1482             src : 'about:blank'  // just incase src get's set to undefined?!?
1483         };
1484         
1485         cfg.html = this.html || cfg.html;
1486         
1487         cfg.src = this.src || cfg.src;
1488         
1489         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1490             cfg.cls += ' img-' + this.border;
1491         }
1492         
1493         if(this.alt){
1494             cfg.alt = this.alt;
1495         }
1496         
1497         if(this.href){
1498             var a = {
1499                 tag: 'a',
1500                 href: this.href,
1501                 cn: [
1502                     cfg
1503                 ]
1504             };
1505             
1506             if(this.target){
1507                 a.target = this.target;
1508             }
1509             
1510         }
1511         
1512         return (this.href) ? a : cfg;
1513     },
1514     
1515     initEvents: function() 
1516     {
1517         if(!this.href){
1518             this.el.on('click', this.onClick, this);
1519         }
1520         
1521     },
1522     
1523     onClick : function(e)
1524     {
1525         Roo.log('img onclick');
1526         this.fireEvent('click', this, e);
1527     },
1528     /**
1529      * Sets the url of the image - used to update it
1530      * @param {String} url the url of the image
1531      */
1532     
1533     setSrc : function(url)
1534     {
1535         this.src =  url;
1536         
1537         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1538             this.el.dom.src =  url;
1539             return;
1540         }
1541         
1542         this.el.select('img', true).first().dom.src =  url;
1543     }
1544     
1545     
1546    
1547 });
1548
1549  /*
1550  * - LGPL
1551  *
1552  * image
1553  * 
1554  */
1555
1556
1557 /**
1558  * @class Roo.bootstrap.Link
1559  * @extends Roo.bootstrap.Component
1560  * Bootstrap Link Class
1561  * @cfg {String} alt image alternative text
1562  * @cfg {String} href a tag href
1563  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1564  * @cfg {String} html the content of the link.
1565  * @cfg {String} anchor name for the anchor link
1566  * @cfg {String} fa - favicon
1567
1568  * @cfg {Boolean} preventDefault (true | false) default false
1569
1570  * 
1571  * @constructor
1572  * Create a new Input
1573  * @param {Object} config The config object
1574  */
1575
1576 Roo.bootstrap.Link = function(config){
1577     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1578     
1579     this.addEvents({
1580         // img events
1581         /**
1582          * @event click
1583          * The img click event for the img.
1584          * @param {Roo.EventObject} e
1585          */
1586         "click" : true
1587     });
1588 };
1589
1590 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1591     
1592     href: false,
1593     target: false,
1594     preventDefault: false,
1595     anchor : false,
1596     alt : false,
1597     fa: false,
1598
1599
1600     getAutoCreate : function()
1601     {
1602         var html = this.html || '';
1603         
1604         if (this.fa !== false) {
1605             html = '<i class="fa fa-' + this.fa + '"></i>';
1606         }
1607         var cfg = {
1608             tag: 'a'
1609         };
1610         // anchor's do not require html/href...
1611         if (this.anchor === false) {
1612             cfg.html = html;
1613             cfg.href = this.href || '#';
1614         } else {
1615             cfg.name = this.anchor;
1616             if (this.html !== false || this.fa !== false) {
1617                 cfg.html = html;
1618             }
1619             if (this.href !== false) {
1620                 cfg.href = this.href;
1621             }
1622         }
1623         
1624         if(this.alt !== false){
1625             cfg.alt = this.alt;
1626         }
1627         
1628         
1629         if(this.target !== false) {
1630             cfg.target = this.target;
1631         }
1632         
1633         return cfg;
1634     },
1635     
1636     initEvents: function() {
1637         
1638         if(!this.href || this.preventDefault){
1639             this.el.on('click', this.onClick, this);
1640         }
1641     },
1642     
1643     onClick : function(e)
1644     {
1645         if(this.preventDefault){
1646             e.preventDefault();
1647         }
1648         //Roo.log('img onclick');
1649         this.fireEvent('click', this, e);
1650     }
1651    
1652 });
1653
1654  /*
1655  * - LGPL
1656  *
1657  * header
1658  * 
1659  */
1660
1661 /**
1662  * @class Roo.bootstrap.Header
1663  * @extends Roo.bootstrap.Component
1664  * Bootstrap Header class
1665  * @cfg {String} html content of header
1666  * @cfg {Number} level (1|2|3|4|5|6) default 1
1667  * 
1668  * @constructor
1669  * Create a new Header
1670  * @param {Object} config The config object
1671  */
1672
1673
1674 Roo.bootstrap.Header  = function(config){
1675     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1676 };
1677
1678 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1679     
1680     //href : false,
1681     html : false,
1682     level : 1,
1683     
1684     
1685     
1686     getAutoCreate : function(){
1687         
1688         
1689         
1690         var cfg = {
1691             tag: 'h' + (1 *this.level),
1692             html: this.html || ''
1693         } ;
1694         
1695         return cfg;
1696     }
1697    
1698 });
1699
1700  
1701
1702  /*
1703  * Based on:
1704  * Ext JS Library 1.1.1
1705  * Copyright(c) 2006-2007, Ext JS, LLC.
1706  *
1707  * Originally Released Under LGPL - original licence link has changed is not relivant.
1708  *
1709  * Fork - LGPL
1710  * <script type="text/javascript">
1711  */
1712  
1713 /**
1714  * @class Roo.bootstrap.MenuMgr
1715  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1716  * @singleton
1717  */
1718 Roo.bootstrap.MenuMgr = function(){
1719    var menus, active, groups = {}, attached = false, lastShow = new Date();
1720
1721    // private - called when first menu is created
1722    function init(){
1723        menus = {};
1724        active = new Roo.util.MixedCollection();
1725        Roo.get(document).addKeyListener(27, function(){
1726            if(active.length > 0){
1727                hideAll();
1728            }
1729        });
1730    }
1731
1732    // private
1733    function hideAll(){
1734        if(active && active.length > 0){
1735            var c = active.clone();
1736            c.each(function(m){
1737                m.hide();
1738            });
1739        }
1740    }
1741
1742    // private
1743    function onHide(m){
1744        active.remove(m);
1745        if(active.length < 1){
1746            Roo.get(document).un("mouseup", onMouseDown);
1747             
1748            attached = false;
1749        }
1750    }
1751
1752    // private
1753    function onShow(m){
1754        var last = active.last();
1755        lastShow = new Date();
1756        active.add(m);
1757        if(!attached){
1758           Roo.get(document).on("mouseup", onMouseDown);
1759            
1760            attached = true;
1761        }
1762        if(m.parentMenu){
1763           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1764           m.parentMenu.activeChild = m;
1765        }else if(last && last.isVisible()){
1766           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1767        }
1768    }
1769
1770    // private
1771    function onBeforeHide(m){
1772        if(m.activeChild){
1773            m.activeChild.hide();
1774        }
1775        if(m.autoHideTimer){
1776            clearTimeout(m.autoHideTimer);
1777            delete m.autoHideTimer;
1778        }
1779    }
1780
1781    // private
1782    function onBeforeShow(m){
1783        var pm = m.parentMenu;
1784        if(!pm && !m.allowOtherMenus){
1785            hideAll();
1786        }else if(pm && pm.activeChild && active != m){
1787            pm.activeChild.hide();
1788        }
1789    }
1790
1791    // private this should really trigger on mouseup..
1792    function onMouseDown(e){
1793         Roo.log("on Mouse Up");
1794         
1795         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1796             Roo.log("MenuManager hideAll");
1797             hideAll();
1798             e.stopEvent();
1799         }
1800         
1801         
1802    }
1803
1804    // private
1805    function onBeforeCheck(mi, state){
1806        if(state){
1807            var g = groups[mi.group];
1808            for(var i = 0, l = g.length; i < l; i++){
1809                if(g[i] != mi){
1810                    g[i].setChecked(false);
1811                }
1812            }
1813        }
1814    }
1815
1816    return {
1817
1818        /**
1819         * Hides all menus that are currently visible
1820         */
1821        hideAll : function(){
1822             hideAll();  
1823        },
1824
1825        // private
1826        register : function(menu){
1827            if(!menus){
1828                init();
1829            }
1830            menus[menu.id] = menu;
1831            menu.on("beforehide", onBeforeHide);
1832            menu.on("hide", onHide);
1833            menu.on("beforeshow", onBeforeShow);
1834            menu.on("show", onShow);
1835            var g = menu.group;
1836            if(g && menu.events["checkchange"]){
1837                if(!groups[g]){
1838                    groups[g] = [];
1839                }
1840                groups[g].push(menu);
1841                menu.on("checkchange", onCheck);
1842            }
1843        },
1844
1845         /**
1846          * Returns a {@link Roo.menu.Menu} object
1847          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1848          * be used to generate and return a new Menu instance.
1849          */
1850        get : function(menu){
1851            if(typeof menu == "string"){ // menu id
1852                return menus[menu];
1853            }else if(menu.events){  // menu instance
1854                return menu;
1855            }
1856            /*else if(typeof menu.length == 'number'){ // array of menu items?
1857                return new Roo.bootstrap.Menu({items:menu});
1858            }else{ // otherwise, must be a config
1859                return new Roo.bootstrap.Menu(menu);
1860            }
1861            */
1862            return false;
1863        },
1864
1865        // private
1866        unregister : function(menu){
1867            delete menus[menu.id];
1868            menu.un("beforehide", onBeforeHide);
1869            menu.un("hide", onHide);
1870            menu.un("beforeshow", onBeforeShow);
1871            menu.un("show", onShow);
1872            var g = menu.group;
1873            if(g && menu.events["checkchange"]){
1874                groups[g].remove(menu);
1875                menu.un("checkchange", onCheck);
1876            }
1877        },
1878
1879        // private
1880        registerCheckable : function(menuItem){
1881            var g = menuItem.group;
1882            if(g){
1883                if(!groups[g]){
1884                    groups[g] = [];
1885                }
1886                groups[g].push(menuItem);
1887                menuItem.on("beforecheckchange", onBeforeCheck);
1888            }
1889        },
1890
1891        // private
1892        unregisterCheckable : function(menuItem){
1893            var g = menuItem.group;
1894            if(g){
1895                groups[g].remove(menuItem);
1896                menuItem.un("beforecheckchange", onBeforeCheck);
1897            }
1898        }
1899    };
1900 }();/*
1901  * - LGPL
1902  *
1903  * menu
1904  * 
1905  */
1906
1907 /**
1908  * @class Roo.bootstrap.Menu
1909  * @extends Roo.bootstrap.Component
1910  * Bootstrap Menu class - container for MenuItems
1911  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1912  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1913  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1914  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1915  * 
1916  * @constructor
1917  * Create a new Menu
1918  * @param {Object} config The config object
1919  */
1920
1921
1922 Roo.bootstrap.Menu = function(config){
1923     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1924     if (this.registerMenu && this.type != 'treeview')  {
1925         Roo.bootstrap.MenuMgr.register(this);
1926     }
1927     this.addEvents({
1928         /**
1929          * @event beforeshow
1930          * Fires before this menu is displayed
1931          * @param {Roo.menu.Menu} this
1932          */
1933         beforeshow : true,
1934         /**
1935          * @event beforehide
1936          * Fires before this menu is hidden
1937          * @param {Roo.menu.Menu} this
1938          */
1939         beforehide : true,
1940         /**
1941          * @event show
1942          * Fires after this menu is displayed
1943          * @param {Roo.menu.Menu} this
1944          */
1945         show : true,
1946         /**
1947          * @event hide
1948          * Fires after this menu is hidden
1949          * @param {Roo.menu.Menu} this
1950          */
1951         hide : true,
1952         /**
1953          * @event click
1954          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1955          * @param {Roo.menu.Menu} this
1956          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1957          * @param {Roo.EventObject} e
1958          */
1959         click : true,
1960         /**
1961          * @event mouseover
1962          * Fires when the mouse is hovering over this menu
1963          * @param {Roo.menu.Menu} this
1964          * @param {Roo.EventObject} e
1965          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1966          */
1967         mouseover : true,
1968         /**
1969          * @event mouseout
1970          * Fires when the mouse exits this menu
1971          * @param {Roo.menu.Menu} this
1972          * @param {Roo.EventObject} e
1973          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1974          */
1975         mouseout : true,
1976         /**
1977          * @event itemclick
1978          * Fires when a menu item contained in this menu is clicked
1979          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1980          * @param {Roo.EventObject} e
1981          */
1982         itemclick: true
1983     });
1984     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1985 };
1986
1987 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1988     
1989    /// html : false,
1990     //align : '',
1991     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1992     type: false,
1993     /**
1994      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1995      */
1996     registerMenu : true,
1997     
1998     menuItems :false, // stores the menu items..
1999     
2000     hidden:true,
2001         
2002     parentMenu : false,
2003     
2004     stopEvent : true,
2005     
2006     isLink : false,
2007     
2008     getChildContainer : function() {
2009         return this.el;  
2010     },
2011     
2012     getAutoCreate : function(){
2013          
2014         //if (['right'].indexOf(this.align)!==-1) {
2015         //    cfg.cn[1].cls += ' pull-right'
2016         //}
2017         
2018         
2019         var cfg = {
2020             tag : 'ul',
2021             cls : 'dropdown-menu' ,
2022             style : 'z-index:1000'
2023             
2024         };
2025         
2026         if (this.type === 'submenu') {
2027             cfg.cls = 'submenu active';
2028         }
2029         if (this.type === 'treeview') {
2030             cfg.cls = 'treeview-menu';
2031         }
2032         
2033         return cfg;
2034     },
2035     initEvents : function() {
2036         
2037        // Roo.log("ADD event");
2038        // Roo.log(this.triggerEl.dom);
2039         
2040         this.triggerEl.on('click', this.onTriggerClick, this);
2041         
2042         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2043         
2044         this.triggerEl.addClass('dropdown-toggle');
2045         
2046         if (Roo.isTouch) {
2047             this.el.on('touchstart'  , this.onTouch, this);
2048         }
2049         this.el.on('click' , this.onClick, this);
2050
2051         this.el.on("mouseover", this.onMouseOver, this);
2052         this.el.on("mouseout", this.onMouseOut, this);
2053         
2054     },
2055     
2056     findTargetItem : function(e)
2057     {
2058         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2059         if(!t){
2060             return false;
2061         }
2062         //Roo.log(t);         Roo.log(t.id);
2063         if(t && t.id){
2064             //Roo.log(this.menuitems);
2065             return this.menuitems.get(t.id);
2066             
2067             //return this.items.get(t.menuItemId);
2068         }
2069         
2070         return false;
2071     },
2072     
2073     onTouch : function(e) 
2074     {
2075         Roo.log("menu.onTouch");
2076         //e.stopEvent(); this make the user popdown broken
2077         this.onClick(e);
2078     },
2079     
2080     onClick : function(e)
2081     {
2082         Roo.log("menu.onClick");
2083         
2084         var t = this.findTargetItem(e);
2085         if(!t || t.isContainer){
2086             return;
2087         }
2088         Roo.log(e);
2089         /*
2090         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2091             if(t == this.activeItem && t.shouldDeactivate(e)){
2092                 this.activeItem.deactivate();
2093                 delete this.activeItem;
2094                 return;
2095             }
2096             if(t.canActivate){
2097                 this.setActiveItem(t, true);
2098             }
2099             return;
2100             
2101             
2102         }
2103         */
2104        
2105         Roo.log('pass click event');
2106         
2107         t.onClick(e);
2108         
2109         this.fireEvent("click", this, t, e);
2110         
2111         var _this = this;
2112         
2113         (function() { _this.hide(); }).defer(100);
2114     },
2115     
2116     onMouseOver : function(e){
2117         var t  = this.findTargetItem(e);
2118         //Roo.log(t);
2119         //if(t){
2120         //    if(t.canActivate && !t.disabled){
2121         //        this.setActiveItem(t, true);
2122         //    }
2123         //}
2124         
2125         this.fireEvent("mouseover", this, e, t);
2126     },
2127     isVisible : function(){
2128         return !this.hidden;
2129     },
2130      onMouseOut : function(e){
2131         var t  = this.findTargetItem(e);
2132         
2133         //if(t ){
2134         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2135         //        this.activeItem.deactivate();
2136         //        delete this.activeItem;
2137         //    }
2138         //}
2139         this.fireEvent("mouseout", this, e, t);
2140     },
2141     
2142     
2143     /**
2144      * Displays this menu relative to another element
2145      * @param {String/HTMLElement/Roo.Element} element The element to align to
2146      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2147      * the element (defaults to this.defaultAlign)
2148      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2149      */
2150     show : function(el, pos, parentMenu){
2151         this.parentMenu = parentMenu;
2152         if(!this.el){
2153             this.render();
2154         }
2155         this.fireEvent("beforeshow", this);
2156         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2157     },
2158      /**
2159      * Displays this menu at a specific xy position
2160      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2161      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2162      */
2163     showAt : function(xy, parentMenu, /* private: */_e){
2164         this.parentMenu = parentMenu;
2165         if(!this.el){
2166             this.render();
2167         }
2168         if(_e !== false){
2169             this.fireEvent("beforeshow", this);
2170             //xy = this.el.adjustForConstraints(xy);
2171         }
2172         
2173         //this.el.show();
2174         this.hideMenuItems();
2175         this.hidden = false;
2176         this.triggerEl.addClass('open');
2177         
2178         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2179             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2180         }
2181         
2182         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2183             this.el.setXY(xy);
2184         }
2185         
2186         this.focus();
2187         this.fireEvent("show", this);
2188     },
2189     
2190     focus : function(){
2191         return;
2192         if(!this.hidden){
2193             this.doFocus.defer(50, this);
2194         }
2195     },
2196
2197     doFocus : function(){
2198         if(!this.hidden){
2199             this.focusEl.focus();
2200         }
2201     },
2202
2203     /**
2204      * Hides this menu and optionally all parent menus
2205      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2206      */
2207     hide : function(deep)
2208     {
2209         
2210         this.hideMenuItems();
2211         if(this.el && this.isVisible()){
2212             this.fireEvent("beforehide", this);
2213             if(this.activeItem){
2214                 this.activeItem.deactivate();
2215                 this.activeItem = null;
2216             }
2217             this.triggerEl.removeClass('open');;
2218             this.hidden = true;
2219             this.fireEvent("hide", this);
2220         }
2221         if(deep === true && this.parentMenu){
2222             this.parentMenu.hide(true);
2223         }
2224     },
2225     
2226     onTriggerClick : function(e)
2227     {
2228         Roo.log('trigger click');
2229         
2230         var target = e.getTarget();
2231         
2232         Roo.log(target.nodeName.toLowerCase());
2233         
2234         if(target.nodeName.toLowerCase() === 'i'){
2235             e.preventDefault();
2236         }
2237         
2238     },
2239     
2240     onTriggerPress  : function(e)
2241     {
2242         Roo.log('trigger press');
2243         //Roo.log(e.getTarget());
2244        // Roo.log(this.triggerEl.dom);
2245        
2246         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2247         var pel = Roo.get(e.getTarget());
2248         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2249             Roo.log('is treeview or dropdown?');
2250             return;
2251         }
2252         
2253         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2254             return;
2255         }
2256         
2257         if (this.isVisible()) {
2258             Roo.log('hide');
2259             this.hide();
2260         } else {
2261             Roo.log('show');
2262             this.show(this.triggerEl, false, false);
2263         }
2264         
2265         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2266             e.stopEvent();
2267         }
2268         
2269     },
2270        
2271     
2272     hideMenuItems : function()
2273     {
2274         Roo.log("hide Menu Items");
2275         if (!this.el) { 
2276             return;
2277         }
2278         //$(backdrop).remove()
2279         this.el.select('.open',true).each(function(aa) {
2280             
2281             aa.removeClass('open');
2282           //var parent = getParent($(this))
2283           //var relatedTarget = { relatedTarget: this }
2284           
2285            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2286           //if (e.isDefaultPrevented()) return
2287            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2288         });
2289     },
2290     addxtypeChild : function (tree, cntr) {
2291         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2292           
2293         this.menuitems.add(comp);
2294         return comp;
2295
2296     },
2297     getEl : function()
2298     {
2299         Roo.log(this.el);
2300         return this.el;
2301     }
2302 });
2303
2304  
2305  /*
2306  * - LGPL
2307  *
2308  * menu item
2309  * 
2310  */
2311
2312
2313 /**
2314  * @class Roo.bootstrap.MenuItem
2315  * @extends Roo.bootstrap.Component
2316  * Bootstrap MenuItem class
2317  * @cfg {String} html the menu label
2318  * @cfg {String} href the link
2319  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2320  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2321  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2322  * @cfg {String} fa favicon to show on left of menu item.
2323  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2324  * 
2325  * 
2326  * @constructor
2327  * Create a new MenuItem
2328  * @param {Object} config The config object
2329  */
2330
2331
2332 Roo.bootstrap.MenuItem = function(config){
2333     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2334     this.addEvents({
2335         // raw events
2336         /**
2337          * @event click
2338          * The raw click event for the entire grid.
2339          * @param {Roo.bootstrap.MenuItem} this
2340          * @param {Roo.EventObject} e
2341          */
2342         "click" : true
2343     });
2344 };
2345
2346 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2347     
2348     href : false,
2349     html : false,
2350     preventDefault: false,
2351     isContainer : false,
2352     active : false,
2353     fa: false,
2354     
2355     getAutoCreate : function(){
2356         
2357         if(this.isContainer){
2358             return {
2359                 tag: 'li',
2360                 cls: 'dropdown-menu-item'
2361             };
2362         }
2363         var ctag = {
2364             tag: 'span',
2365             html: 'Link'
2366         };
2367         
2368         var anc = {
2369             tag : 'a',
2370             href : '#',
2371             cn : [  ]
2372         };
2373         
2374         if (this.fa !== false) {
2375             anc.cn.push({
2376                 tag : 'i',
2377                 cls : 'fa fa-' + this.fa
2378             });
2379         }
2380         
2381         anc.cn.push(ctag);
2382         
2383         
2384         var cfg= {
2385             tag: 'li',
2386             cls: 'dropdown-menu-item',
2387             cn: [ anc ]
2388         };
2389         if (this.parent().type == 'treeview') {
2390             cfg.cls = 'treeview-menu';
2391         }
2392         if (this.active) {
2393             cfg.cls += ' active';
2394         }
2395         
2396         
2397         
2398         anc.href = this.href || cfg.cn[0].href ;
2399         ctag.html = this.html || cfg.cn[0].html ;
2400         return cfg;
2401     },
2402     
2403     initEvents: function()
2404     {
2405         if (this.parent().type == 'treeview') {
2406             this.el.select('a').on('click', this.onClick, this);
2407         }
2408         if (this.menu) {
2409             this.menu.parentType = this.xtype;
2410             this.menu.triggerEl = this.el;
2411             this.menu = this.addxtype(Roo.apply({}, this.menu));
2412         }
2413         
2414     },
2415     onClick : function(e)
2416     {
2417         Roo.log('item on click ');
2418         
2419         if(this.preventDefault){
2420             e.preventDefault();
2421         }
2422         //this.parent().hideMenuItems();
2423         
2424         this.fireEvent('click', this, e);
2425     },
2426     getEl : function()
2427     {
2428         return this.el;
2429     } 
2430 });
2431
2432  
2433
2434  /*
2435  * - LGPL
2436  *
2437  * menu separator
2438  * 
2439  */
2440
2441
2442 /**
2443  * @class Roo.bootstrap.MenuSeparator
2444  * @extends Roo.bootstrap.Component
2445  * Bootstrap MenuSeparator class
2446  * 
2447  * @constructor
2448  * Create a new MenuItem
2449  * @param {Object} config The config object
2450  */
2451
2452
2453 Roo.bootstrap.MenuSeparator = function(config){
2454     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2455 };
2456
2457 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2458     
2459     getAutoCreate : function(){
2460         var cfg = {
2461             cls: 'divider',
2462             tag : 'li'
2463         };
2464         
2465         return cfg;
2466     }
2467    
2468 });
2469
2470  
2471
2472  
2473 /*
2474 * Licence: LGPL
2475 */
2476
2477 /**
2478  * @class Roo.bootstrap.Modal
2479  * @extends Roo.bootstrap.Component
2480  * Bootstrap Modal class
2481  * @cfg {String} title Title of dialog
2482  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2483  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2484  * @cfg {Boolean} specificTitle default false
2485  * @cfg {Array} buttons Array of buttons or standard button set..
2486  * @cfg {String} buttonPosition (left|right|center) default right
2487  * @cfg {Boolean} animate default true
2488  * @cfg {Boolean} allow_close default true
2489  * @cfg {Boolean} fitwindow default false
2490  * @cfg {String} size (sm|lg) default empty
2491  *
2492  *
2493  * @constructor
2494  * Create a new Modal Dialog
2495  * @param {Object} config The config object
2496  */
2497
2498 Roo.bootstrap.Modal = function(config){
2499     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2500     this.addEvents({
2501         // raw events
2502         /**
2503          * @event btnclick
2504          * The raw btnclick event for the button
2505          * @param {Roo.EventObject} e
2506          */
2507         "btnclick" : true,
2508         /**
2509          * @event resize
2510          * Fire when dialog resize
2511          * @param {Roo.bootstrap.Modal} this
2512          * @param {Roo.EventObject} e
2513          */
2514         "resize" : true
2515     });
2516     this.buttons = this.buttons || [];
2517
2518     if (this.tmpl) {
2519         this.tmpl = Roo.factory(this.tmpl);
2520     }
2521
2522 };
2523
2524 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2525
2526     title : 'test dialog',
2527
2528     buttons : false,
2529
2530     // set on load...
2531
2532     html: false,
2533
2534     tmp: false,
2535
2536     specificTitle: false,
2537
2538     buttonPosition: 'right',
2539
2540     allow_close : true,
2541
2542     animate : true,
2543
2544     fitwindow: false,
2545
2546
2547      // private
2548     dialogEl: false,
2549     bodyEl:  false,
2550     footerEl:  false,
2551     titleEl:  false,
2552     closeEl:  false,
2553
2554     size: '',
2555
2556
2557     onRender : function(ct, position)
2558     {
2559         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2560
2561         if(!this.el){
2562             var cfg = Roo.apply({},  this.getAutoCreate());
2563             cfg.id = Roo.id();
2564             //if(!cfg.name){
2565             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2566             //}
2567             //if (!cfg.name.length) {
2568             //    delete cfg.name;
2569            // }
2570             if (this.cls) {
2571                 cfg.cls += ' ' + this.cls;
2572             }
2573             if (this.style) {
2574                 cfg.style = this.style;
2575             }
2576             this.el = Roo.get(document.body).createChild(cfg, position);
2577         }
2578         //var type = this.el.dom.type;
2579
2580
2581         if(this.tabIndex !== undefined){
2582             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2583         }
2584
2585         this.dialogEl = this.el.select('.modal-dialog',true).first();
2586         this.bodyEl = this.el.select('.modal-body',true).first();
2587         this.closeEl = this.el.select('.modal-header .close', true).first();
2588         this.headerEl = this.el.select('.modal-header',true).first();
2589         this.titleEl = this.el.select('.modal-title',true).first();
2590         this.footerEl = this.el.select('.modal-footer',true).first();
2591
2592         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2593         this.maskEl.enableDisplayMode("block");
2594         this.maskEl.hide();
2595         //this.el.addClass("x-dlg-modal");
2596
2597         if (this.buttons.length) {
2598             Roo.each(this.buttons, function(bb) {
2599                 var b = Roo.apply({}, bb);
2600                 b.xns = b.xns || Roo.bootstrap;
2601                 b.xtype = b.xtype || 'Button';
2602                 if (typeof(b.listeners) == 'undefined') {
2603                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2604                 }
2605
2606                 var btn = Roo.factory(b);
2607
2608                 btn.render(this.el.select('.modal-footer div').first());
2609
2610             },this);
2611         }
2612         // render the children.
2613         var nitems = [];
2614
2615         if(typeof(this.items) != 'undefined'){
2616             var items = this.items;
2617             delete this.items;
2618
2619             for(var i =0;i < items.length;i++) {
2620                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2621             }
2622         }
2623
2624         this.items = nitems;
2625
2626         // where are these used - they used to be body/close/footer
2627
2628
2629         this.initEvents();
2630         //this.el.addClass([this.fieldClass, this.cls]);
2631
2632     },
2633
2634     getAutoCreate : function(){
2635
2636
2637         var bdy = {
2638                 cls : 'modal-body',
2639                 html : this.html || ''
2640         };
2641
2642         var title = {
2643             tag: 'h4',
2644             cls : 'modal-title',
2645             html : this.title
2646         };
2647
2648         if(this.specificTitle){
2649             title = this.title;
2650
2651         };
2652
2653         var header = [];
2654         if (this.allow_close) {
2655             header.push({
2656                 tag: 'button',
2657                 cls : 'close',
2658                 html : '&times'
2659             });
2660         }
2661
2662         header.push(title);
2663
2664         var size = '';
2665
2666         if(this.size.length){
2667             size = 'modal-' + this.size;
2668         }
2669
2670         var modal = {
2671             cls: "modal",
2672             style : 'display: none',
2673             cn : [
2674                 {
2675                     cls: "modal-dialog " + size,
2676                     cn : [
2677                         {
2678                             cls : "modal-content",
2679                             cn : [
2680                                 {
2681                                     cls : 'modal-header',
2682                                     cn : header
2683                                 },
2684                                 bdy,
2685                                 {
2686                                     cls : 'modal-footer',
2687                                     cn : [
2688                                         {
2689                                             tag: 'div',
2690                                             cls: 'btn-' + this.buttonPosition
2691                                         }
2692                                     ]
2693
2694                                 }
2695
2696
2697                             ]
2698
2699                         }
2700                     ]
2701
2702                 }
2703             ]
2704         };
2705
2706         if(this.animate){
2707             modal.cls += ' fade';
2708         }
2709
2710         return modal;
2711
2712     },
2713     getChildContainer : function() {
2714
2715          return this.bodyEl;
2716
2717     },
2718     getButtonContainer : function() {
2719          return this.el.select('.modal-footer div',true).first();
2720
2721     },
2722     initEvents : function()
2723     {
2724         if (this.allow_close) {
2725             this.closeEl.on('click', this.hide, this);
2726         }
2727         Roo.EventManager.onWindowResize(this.resize, this, true);
2728
2729
2730     },
2731
2732     resize : function()
2733     {
2734         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2735         if (this.fitwindow) {
2736             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2737             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2738             this.setSize(w,h);
2739         }
2740     },
2741
2742     setSize : function(w,h)
2743     {
2744         if (!w && !h) {
2745             return;
2746         }
2747         this.resizeTo(w,h);
2748     },
2749
2750     show : function() {
2751
2752         if (!this.rendered) {
2753             this.render();
2754         }
2755
2756         this.el.setStyle('display', 'block');
2757
2758         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2759             var _this = this;
2760             (function(){
2761                 this.el.addClass('in');
2762             }).defer(50, this);
2763         }else{
2764             this.el.addClass('in');
2765
2766         }
2767
2768         // not sure how we can show data in here..
2769         //if (this.tmpl) {
2770         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2771         //}
2772
2773         Roo.get(document.body).addClass("x-body-masked");
2774         
2775         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2776         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2777         this.maskEl.show();
2778         
2779         this.resize();
2780         
2781         this.fireEvent('show', this);
2782
2783         // set zindex here - otherwise it appears to be ignored...
2784         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2785
2786         (function () {
2787             this.items.forEach( function(e) {
2788                 e.layout ? e.layout() : false;
2789
2790             });
2791         }).defer(100,this);
2792
2793     },
2794     hide : function()
2795     {
2796         if(this.fireEvent("beforehide", this) !== false){
2797             this.maskEl.hide();
2798             Roo.get(document.body).removeClass("x-body-masked");
2799             this.el.removeClass('in');
2800             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2801
2802             if(this.animate){ // why
2803                 var _this = this;
2804                 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2805             }else{
2806                 this.el.setStyle('display', 'none');
2807             }
2808             this.fireEvent('hide', this);
2809         }
2810     },
2811
2812     addButton : function(str, cb)
2813     {
2814
2815
2816         var b = Roo.apply({}, { html : str } );
2817         b.xns = b.xns || Roo.bootstrap;
2818         b.xtype = b.xtype || 'Button';
2819         if (typeof(b.listeners) == 'undefined') {
2820             b.listeners = { click : cb.createDelegate(this)  };
2821         }
2822
2823         var btn = Roo.factory(b);
2824
2825         btn.render(this.el.select('.modal-footer div').first());
2826
2827         return btn;
2828
2829     },
2830
2831     setDefaultButton : function(btn)
2832     {
2833         //this.el.select('.modal-footer').()
2834     },
2835     diff : false,
2836
2837     resizeTo: function(w,h)
2838     {
2839         // skip.. ?? why??
2840
2841         this.dialogEl.setWidth(w);
2842         if (this.diff === false) {
2843             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2844         }
2845
2846         this.bodyEl.setHeight(h-this.diff);
2847
2848         this.fireEvent('resize', this);
2849
2850     },
2851     setContentSize  : function(w, h)
2852     {
2853
2854     },
2855     onButtonClick: function(btn,e)
2856     {
2857         //Roo.log([a,b,c]);
2858         this.fireEvent('btnclick', btn.name, e);
2859     },
2860      /**
2861      * Set the title of the Dialog
2862      * @param {String} str new Title
2863      */
2864     setTitle: function(str) {
2865         this.titleEl.dom.innerHTML = str;
2866     },
2867     /**
2868      * Set the body of the Dialog
2869      * @param {String} str new Title
2870      */
2871     setBody: function(str) {
2872         this.bodyEl.dom.innerHTML = str;
2873     },
2874     /**
2875      * Set the body of the Dialog using the template
2876      * @param {Obj} data - apply this data to the template and replace the body contents.
2877      */
2878     applyBody: function(obj)
2879     {
2880         if (!this.tmpl) {
2881             Roo.log("Error - using apply Body without a template");
2882             //code
2883         }
2884         this.tmpl.overwrite(this.bodyEl, obj);
2885     }
2886
2887 });
2888
2889
2890 Roo.apply(Roo.bootstrap.Modal,  {
2891     /**
2892          * Button config that displays a single OK button
2893          * @type Object
2894          */
2895         OK :  [{
2896             name : 'ok',
2897             weight : 'primary',
2898             html : 'OK'
2899         }],
2900         /**
2901          * Button config that displays Yes and No buttons
2902          * @type Object
2903          */
2904         YESNO : [
2905             {
2906                 name  : 'no',
2907                 html : 'No'
2908             },
2909             {
2910                 name  :'yes',
2911                 weight : 'primary',
2912                 html : 'Yes'
2913             }
2914         ],
2915
2916         /**
2917          * Button config that displays OK and Cancel buttons
2918          * @type Object
2919          */
2920         OKCANCEL : [
2921             {
2922                name : 'cancel',
2923                 html : 'Cancel'
2924             },
2925             {
2926                 name : 'ok',
2927                 weight : 'primary',
2928                 html : 'OK'
2929             }
2930         ],
2931         /**
2932          * Button config that displays Yes, No and Cancel buttons
2933          * @type Object
2934          */
2935         YESNOCANCEL : [
2936             {
2937                 name : 'yes',
2938                 weight : 'primary',
2939                 html : 'Yes'
2940             },
2941             {
2942                 name : 'no',
2943                 html : 'No'
2944             },
2945             {
2946                 name : 'cancel',
2947                 html : 'Cancel'
2948             }
2949         ],
2950         
2951         zIndex : 10001
2952 });
2953 /*
2954  * - LGPL
2955  *
2956  * messagebox - can be used as a replace
2957  * 
2958  */
2959 /**
2960  * @class Roo.MessageBox
2961  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2962  * Example usage:
2963  *<pre><code>
2964 // Basic alert:
2965 Roo.Msg.alert('Status', 'Changes saved successfully.');
2966
2967 // Prompt for user data:
2968 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2969     if (btn == 'ok'){
2970         // process text value...
2971     }
2972 });
2973
2974 // Show a dialog using config options:
2975 Roo.Msg.show({
2976    title:'Save Changes?',
2977    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2978    buttons: Roo.Msg.YESNOCANCEL,
2979    fn: processResult,
2980    animEl: 'elId'
2981 });
2982 </code></pre>
2983  * @singleton
2984  */
2985 Roo.bootstrap.MessageBox = function(){
2986     var dlg, opt, mask, waitTimer;
2987     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2988     var buttons, activeTextEl, bwidth;
2989
2990     
2991     // private
2992     var handleButton = function(button){
2993         dlg.hide();
2994         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2995     };
2996
2997     // private
2998     var handleHide = function(){
2999         if(opt && opt.cls){
3000             dlg.el.removeClass(opt.cls);
3001         }
3002         //if(waitTimer){
3003         //    Roo.TaskMgr.stop(waitTimer);
3004         //    waitTimer = null;
3005         //}
3006     };
3007
3008     // private
3009     var updateButtons = function(b){
3010         var width = 0;
3011         if(!b){
3012             buttons["ok"].hide();
3013             buttons["cancel"].hide();
3014             buttons["yes"].hide();
3015             buttons["no"].hide();
3016             //dlg.footer.dom.style.display = 'none';
3017             return width;
3018         }
3019         dlg.footerEl.dom.style.display = '';
3020         for(var k in buttons){
3021             if(typeof buttons[k] != "function"){
3022                 if(b[k]){
3023                     buttons[k].show();
3024                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3025                     width += buttons[k].el.getWidth()+15;
3026                 }else{
3027                     buttons[k].hide();
3028                 }
3029             }
3030         }
3031         return width;
3032     };
3033
3034     // private
3035     var handleEsc = function(d, k, e){
3036         if(opt && opt.closable !== false){
3037             dlg.hide();
3038         }
3039         if(e){
3040             e.stopEvent();
3041         }
3042     };
3043
3044     return {
3045         /**
3046          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3047          * @return {Roo.BasicDialog} The BasicDialog element
3048          */
3049         getDialog : function(){
3050            if(!dlg){
3051                 dlg = new Roo.bootstrap.Modal( {
3052                     //draggable: true,
3053                     //resizable:false,
3054                     //constraintoviewport:false,
3055                     //fixedcenter:true,
3056                     //collapsible : false,
3057                     //shim:true,
3058                     //modal: true,
3059                 //    width: 'auto',
3060                   //  height:100,
3061                     //buttonAlign:"center",
3062                     closeClick : function(){
3063                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3064                             handleButton("no");
3065                         }else{
3066                             handleButton("cancel");
3067                         }
3068                     }
3069                 });
3070                 dlg.render();
3071                 dlg.on("hide", handleHide);
3072                 mask = dlg.mask;
3073                 //dlg.addKeyListener(27, handleEsc);
3074                 buttons = {};
3075                 this.buttons = buttons;
3076                 var bt = this.buttonText;
3077                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3078                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3079                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3080                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3081                 //Roo.log(buttons);
3082                 bodyEl = dlg.bodyEl.createChild({
3083
3084                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3085                         '<textarea class="roo-mb-textarea"></textarea>' +
3086                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3087                 });
3088                 msgEl = bodyEl.dom.firstChild;
3089                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3090                 textboxEl.enableDisplayMode();
3091                 textboxEl.addKeyListener([10,13], function(){
3092                     if(dlg.isVisible() && opt && opt.buttons){
3093                         if(opt.buttons.ok){
3094                             handleButton("ok");
3095                         }else if(opt.buttons.yes){
3096                             handleButton("yes");
3097                         }
3098                     }
3099                 });
3100                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3101                 textareaEl.enableDisplayMode();
3102                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3103                 progressEl.enableDisplayMode();
3104                 
3105                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3106                 //var pf = progressEl.dom.firstChild;
3107                 //if (pf) {
3108                     //pp = Roo.get(pf.firstChild);
3109                     //pp.setHeight(pf.offsetHeight);
3110                 //}
3111                 
3112             }
3113             return dlg;
3114         },
3115
3116         /**
3117          * Updates the message box body text
3118          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3119          * the XHTML-compliant non-breaking space character '&amp;#160;')
3120          * @return {Roo.MessageBox} This message box
3121          */
3122         updateText : function(text)
3123         {
3124             if(!dlg.isVisible() && !opt.width){
3125                 dlg.dialogEl.setWidth(this.maxWidth);
3126                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3127             }
3128             msgEl.innerHTML = text || '&#160;';
3129       
3130             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3131             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3132             var w = Math.max(
3133                     Math.min(opt.width || cw , this.maxWidth), 
3134                     Math.max(opt.minWidth || this.minWidth, bwidth)
3135             );
3136             if(opt.prompt){
3137                 activeTextEl.setWidth(w);
3138             }
3139             if(dlg.isVisible()){
3140                 dlg.fixedcenter = false;
3141             }
3142             // to big, make it scroll. = But as usual stupid IE does not support
3143             // !important..
3144             
3145             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3146                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3147                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3148             } else {
3149                 bodyEl.dom.style.height = '';
3150                 bodyEl.dom.style.overflowY = '';
3151             }
3152             if (cw > w) {
3153                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3154             } else {
3155                 bodyEl.dom.style.overflowX = '';
3156             }
3157             
3158             dlg.setContentSize(w, bodyEl.getHeight());
3159             if(dlg.isVisible()){
3160                 dlg.fixedcenter = true;
3161             }
3162             return this;
3163         },
3164
3165         /**
3166          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3167          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3168          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3169          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3170          * @return {Roo.MessageBox} This message box
3171          */
3172         updateProgress : function(value, text){
3173             if(text){
3174                 this.updateText(text);
3175             }
3176             if (pp) { // weird bug on my firefox - for some reason this is not defined
3177                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3178             }
3179             return this;
3180         },        
3181
3182         /**
3183          * Returns true if the message box is currently displayed
3184          * @return {Boolean} True if the message box is visible, else false
3185          */
3186         isVisible : function(){
3187             return dlg && dlg.isVisible();  
3188         },
3189
3190         /**
3191          * Hides the message box if it is displayed
3192          */
3193         hide : function(){
3194             if(this.isVisible()){
3195                 dlg.hide();
3196             }  
3197         },
3198
3199         /**
3200          * Displays a new message box, or reinitializes an existing message box, based on the config options
3201          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3202          * The following config object properties are supported:
3203          * <pre>
3204 Property    Type             Description
3205 ----------  ---------------  ------------------------------------------------------------------------------------
3206 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3207                                    closes (defaults to undefined)
3208 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3209                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3210 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3211                                    progress and wait dialogs will ignore this property and always hide the
3212                                    close button as they can only be closed programmatically.
3213 cls               String           A custom CSS class to apply to the message box element
3214 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3215                                    displayed (defaults to 75)
3216 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3217                                    function will be btn (the name of the button that was clicked, if applicable,
3218                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3219                                    Progress and wait dialogs will ignore this option since they do not respond to
3220                                    user actions and can only be closed programmatically, so any required function
3221                                    should be called by the same code after it closes the dialog.
3222 icon              String           A CSS class that provides a background image to be used as an icon for
3223                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3224 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3225 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3226 modal             Boolean          False to allow user interaction with the page while the message box is
3227                                    displayed (defaults to true)
3228 msg               String           A string that will replace the existing message box body text (defaults
3229                                    to the XHTML-compliant non-breaking space character '&#160;')
3230 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3231 progress          Boolean          True to display a progress bar (defaults to false)
3232 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3233 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3234 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3235 title             String           The title text
3236 value             String           The string value to set into the active textbox element if displayed
3237 wait              Boolean          True to display a progress bar (defaults to false)
3238 width             Number           The width of the dialog in pixels
3239 </pre>
3240          *
3241          * Example usage:
3242          * <pre><code>
3243 Roo.Msg.show({
3244    title: 'Address',
3245    msg: 'Please enter your address:',
3246    width: 300,
3247    buttons: Roo.MessageBox.OKCANCEL,
3248    multiline: true,
3249    fn: saveAddress,
3250    animEl: 'addAddressBtn'
3251 });
3252 </code></pre>
3253          * @param {Object} config Configuration options
3254          * @return {Roo.MessageBox} This message box
3255          */
3256         show : function(options)
3257         {
3258             
3259             // this causes nightmares if you show one dialog after another
3260             // especially on callbacks..
3261              
3262             if(this.isVisible()){
3263                 
3264                 this.hide();
3265                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3266                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3267                 Roo.log("New Dialog Message:" +  options.msg )
3268                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3269                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3270                 
3271             }
3272             var d = this.getDialog();
3273             opt = options;
3274             d.setTitle(opt.title || "&#160;");
3275             d.closeEl.setDisplayed(opt.closable !== false);
3276             activeTextEl = textboxEl;
3277             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3278             if(opt.prompt){
3279                 if(opt.multiline){
3280                     textboxEl.hide();
3281                     textareaEl.show();
3282                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3283                         opt.multiline : this.defaultTextHeight);
3284                     activeTextEl = textareaEl;
3285                 }else{
3286                     textboxEl.show();
3287                     textareaEl.hide();
3288                 }
3289             }else{
3290                 textboxEl.hide();
3291                 textareaEl.hide();
3292             }
3293             progressEl.setDisplayed(opt.progress === true);
3294             this.updateProgress(0);
3295             activeTextEl.dom.value = opt.value || "";
3296             if(opt.prompt){
3297                 dlg.setDefaultButton(activeTextEl);
3298             }else{
3299                 var bs = opt.buttons;
3300                 var db = null;
3301                 if(bs && bs.ok){
3302                     db = buttons["ok"];
3303                 }else if(bs && bs.yes){
3304                     db = buttons["yes"];
3305                 }
3306                 dlg.setDefaultButton(db);
3307             }
3308             bwidth = updateButtons(opt.buttons);
3309             this.updateText(opt.msg);
3310             if(opt.cls){
3311                 d.el.addClass(opt.cls);
3312             }
3313             d.proxyDrag = opt.proxyDrag === true;
3314             d.modal = opt.modal !== false;
3315             d.mask = opt.modal !== false ? mask : false;
3316             if(!d.isVisible()){
3317                 // force it to the end of the z-index stack so it gets a cursor in FF
3318                 document.body.appendChild(dlg.el.dom);
3319                 d.animateTarget = null;
3320                 d.show(options.animEl);
3321             }
3322             return this;
3323         },
3324
3325         /**
3326          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3327          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3328          * and closing the message box when the process is complete.
3329          * @param {String} title The title bar text
3330          * @param {String} msg The message box body text
3331          * @return {Roo.MessageBox} This message box
3332          */
3333         progress : function(title, msg){
3334             this.show({
3335                 title : title,
3336                 msg : msg,
3337                 buttons: false,
3338                 progress:true,
3339                 closable:false,
3340                 minWidth: this.minProgressWidth,
3341                 modal : true
3342             });
3343             return this;
3344         },
3345
3346         /**
3347          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3348          * If a callback function is passed it will be called after the user clicks the button, and the
3349          * id of the button that was clicked will be passed as the only parameter to the callback
3350          * (could also be the top-right close button).
3351          * @param {String} title The title bar text
3352          * @param {String} msg The message box body text
3353          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3354          * @param {Object} scope (optional) The scope of the callback function
3355          * @return {Roo.MessageBox} This message box
3356          */
3357         alert : function(title, msg, fn, scope)
3358         {
3359             this.show({
3360                 title : title,
3361                 msg : msg,
3362                 buttons: this.OK,
3363                 fn: fn,
3364                 closable : false,
3365                 scope : scope,
3366                 modal : true
3367             });
3368             return this;
3369         },
3370
3371         /**
3372          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3373          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3374          * You are responsible for closing the message box when the process is complete.
3375          * @param {String} msg The message box body text
3376          * @param {String} title (optional) The title bar text
3377          * @return {Roo.MessageBox} This message box
3378          */
3379         wait : function(msg, title){
3380             this.show({
3381                 title : title,
3382                 msg : msg,
3383                 buttons: false,
3384                 closable:false,
3385                 progress:true,
3386                 modal:true,
3387                 width:300,
3388                 wait:true
3389             });
3390             waitTimer = Roo.TaskMgr.start({
3391                 run: function(i){
3392                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3393                 },
3394                 interval: 1000
3395             });
3396             return this;
3397         },
3398
3399         /**
3400          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3401          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3402          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3403          * @param {String} title The title bar text
3404          * @param {String} msg The message box body text
3405          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3406          * @param {Object} scope (optional) The scope of the callback function
3407          * @return {Roo.MessageBox} This message box
3408          */
3409         confirm : function(title, msg, fn, scope){
3410             this.show({
3411                 title : title,
3412                 msg : msg,
3413                 buttons: this.YESNO,
3414                 fn: fn,
3415                 scope : scope,
3416                 modal : true
3417             });
3418             return this;
3419         },
3420
3421         /**
3422          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3423          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3424          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3425          * (could also be the top-right close button) and the text that was entered will be passed as the two
3426          * parameters to the callback.
3427          * @param {String} title The title bar text
3428          * @param {String} msg The message box body text
3429          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3430          * @param {Object} scope (optional) The scope of the callback function
3431          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3432          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3433          * @return {Roo.MessageBox} This message box
3434          */
3435         prompt : function(title, msg, fn, scope, multiline){
3436             this.show({
3437                 title : title,
3438                 msg : msg,
3439                 buttons: this.OKCANCEL,
3440                 fn: fn,
3441                 minWidth:250,
3442                 scope : scope,
3443                 prompt:true,
3444                 multiline: multiline,
3445                 modal : true
3446             });
3447             return this;
3448         },
3449
3450         /**
3451          * Button config that displays a single OK button
3452          * @type Object
3453          */
3454         OK : {ok:true},
3455         /**
3456          * Button config that displays Yes and No buttons
3457          * @type Object
3458          */
3459         YESNO : {yes:true, no:true},
3460         /**
3461          * Button config that displays OK and Cancel buttons
3462          * @type Object
3463          */
3464         OKCANCEL : {ok:true, cancel:true},
3465         /**
3466          * Button config that displays Yes, No and Cancel buttons
3467          * @type Object
3468          */
3469         YESNOCANCEL : {yes:true, no:true, cancel:true},
3470
3471         /**
3472          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3473          * @type Number
3474          */
3475         defaultTextHeight : 75,
3476         /**
3477          * The maximum width in pixels of the message box (defaults to 600)
3478          * @type Number
3479          */
3480         maxWidth : 600,
3481         /**
3482          * The minimum width in pixels of the message box (defaults to 100)
3483          * @type Number
3484          */
3485         minWidth : 100,
3486         /**
3487          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3488          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3489          * @type Number
3490          */
3491         minProgressWidth : 250,
3492         /**
3493          * An object containing the default button text strings that can be overriden for localized language support.
3494          * Supported properties are: ok, cancel, yes and no.
3495          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3496          * @type Object
3497          */
3498         buttonText : {
3499             ok : "OK",
3500             cancel : "Cancel",
3501             yes : "Yes",
3502             no : "No"
3503         }
3504     };
3505 }();
3506
3507 /**
3508  * Shorthand for {@link Roo.MessageBox}
3509  */
3510 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3511 Roo.Msg = Roo.Msg || Roo.MessageBox;
3512 /*
3513  * - LGPL
3514  *
3515  * navbar
3516  * 
3517  */
3518
3519 /**
3520  * @class Roo.bootstrap.Navbar
3521  * @extends Roo.bootstrap.Component
3522  * Bootstrap Navbar class
3523
3524  * @constructor
3525  * Create a new Navbar
3526  * @param {Object} config The config object
3527  */
3528
3529
3530 Roo.bootstrap.Navbar = function(config){
3531     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3532     this.addEvents({
3533         // raw events
3534         /**
3535          * @event beforetoggle
3536          * Fire before toggle the menu
3537          * @param {Roo.EventObject} e
3538          */
3539         "beforetoggle" : true
3540     });
3541 };
3542
3543 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3544     
3545     
3546    
3547     // private
3548     navItems : false,
3549     loadMask : false,
3550     
3551     
3552     getAutoCreate : function(){
3553         
3554         
3555         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3556         
3557     },
3558     
3559     initEvents :function ()
3560     {
3561         //Roo.log(this.el.select('.navbar-toggle',true));
3562         this.el.select('.navbar-toggle',true).on('click', function() {
3563             if(this.fireEvent('beforetoggle', this) !== false){
3564                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3565             }
3566             
3567         }, this);
3568         
3569         var mark = {
3570             tag: "div",
3571             cls:"x-dlg-mask"
3572         };
3573         
3574         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3575         
3576         var size = this.el.getSize();
3577         this.maskEl.setSize(size.width, size.height);
3578         this.maskEl.enableDisplayMode("block");
3579         this.maskEl.hide();
3580         
3581         if(this.loadMask){
3582             this.maskEl.show();
3583         }
3584     },
3585     
3586     
3587     getChildContainer : function()
3588     {
3589         if (this.el.select('.collapse').getCount()) {
3590             return this.el.select('.collapse',true).first();
3591         }
3592         
3593         return this.el;
3594     },
3595     
3596     mask : function()
3597     {
3598         this.maskEl.show();
3599     },
3600     
3601     unmask : function()
3602     {
3603         this.maskEl.hide();
3604     } 
3605     
3606     
3607     
3608     
3609 });
3610
3611
3612
3613  
3614
3615  /*
3616  * - LGPL
3617  *
3618  * navbar
3619  * 
3620  */
3621
3622 /**
3623  * @class Roo.bootstrap.NavSimplebar
3624  * @extends Roo.bootstrap.Navbar
3625  * Bootstrap Sidebar class
3626  *
3627  * @cfg {Boolean} inverse is inverted color
3628  * 
3629  * @cfg {String} type (nav | pills | tabs)
3630  * @cfg {Boolean} arrangement stacked | justified
3631  * @cfg {String} align (left | right) alignment
3632  * 
3633  * @cfg {Boolean} main (true|false) main nav bar? default false
3634  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3635  * 
3636  * @cfg {String} tag (header|footer|nav|div) default is nav 
3637
3638  * 
3639  * 
3640  * 
3641  * @constructor
3642  * Create a new Sidebar
3643  * @param {Object} config The config object
3644  */
3645
3646
3647 Roo.bootstrap.NavSimplebar = function(config){
3648     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3649 };
3650
3651 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3652     
3653     inverse: false,
3654     
3655     type: false,
3656     arrangement: '',
3657     align : false,
3658     
3659     
3660     
3661     main : false,
3662     
3663     
3664     tag : false,
3665     
3666     
3667     getAutoCreate : function(){
3668         
3669         
3670         var cfg = {
3671             tag : this.tag || 'div',
3672             cls : 'navbar'
3673         };
3674           
3675         
3676         cfg.cn = [
3677             {
3678                 cls: 'nav',
3679                 tag : 'ul'
3680             }
3681         ];
3682         
3683          
3684         this.type = this.type || 'nav';
3685         if (['tabs','pills'].indexOf(this.type)!==-1) {
3686             cfg.cn[0].cls += ' nav-' + this.type
3687         
3688         
3689         } else {
3690             if (this.type!=='nav') {
3691                 Roo.log('nav type must be nav/tabs/pills')
3692             }
3693             cfg.cn[0].cls += ' navbar-nav'
3694         }
3695         
3696         
3697         
3698         
3699         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3700             cfg.cn[0].cls += ' nav-' + this.arrangement;
3701         }
3702         
3703         
3704         if (this.align === 'right') {
3705             cfg.cn[0].cls += ' navbar-right';
3706         }
3707         
3708         if (this.inverse) {
3709             cfg.cls += ' navbar-inverse';
3710             
3711         }
3712         
3713         
3714         return cfg;
3715     
3716         
3717     }
3718     
3719     
3720     
3721 });
3722
3723
3724
3725  
3726
3727  
3728        /*
3729  * - LGPL
3730  *
3731  * navbar
3732  * 
3733  */
3734
3735 /**
3736  * @class Roo.bootstrap.NavHeaderbar
3737  * @extends Roo.bootstrap.NavSimplebar
3738  * Bootstrap Sidebar class
3739  *
3740  * @cfg {String} brand what is brand
3741  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3742  * @cfg {String} brand_href href of the brand
3743  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3744  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3745  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3746  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3747  * 
3748  * @constructor
3749  * Create a new Sidebar
3750  * @param {Object} config The config object
3751  */
3752
3753
3754 Roo.bootstrap.NavHeaderbar = function(config){
3755     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3756       
3757 };
3758
3759 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3760     
3761     position: '',
3762     brand: '',
3763     brand_href: false,
3764     srButton : true,
3765     autohide : false,
3766     desktopCenter : false,
3767    
3768     
3769     getAutoCreate : function(){
3770         
3771         var   cfg = {
3772             tag: this.nav || 'nav',
3773             cls: 'navbar',
3774             role: 'navigation',
3775             cn: []
3776         };
3777         
3778         var cn = cfg.cn;
3779         if (this.desktopCenter) {
3780             cn.push({cls : 'container', cn : []});
3781             cn = cn[0].cn;
3782         }
3783         
3784         if(this.srButton){
3785             cn.push({
3786                 tag: 'div',
3787                 cls: 'navbar-header',
3788                 cn: [
3789                     {
3790                         tag: 'button',
3791                         type: 'button',
3792                         cls: 'navbar-toggle',
3793                         'data-toggle': 'collapse',
3794                         cn: [
3795                             {
3796                                 tag: 'span',
3797                                 cls: 'sr-only',
3798                                 html: 'Toggle navigation'
3799                             },
3800                             {
3801                                 tag: 'span',
3802                                 cls: 'icon-bar'
3803                             },
3804                             {
3805                                 tag: 'span',
3806                                 cls: 'icon-bar'
3807                             },
3808                             {
3809                                 tag: 'span',
3810                                 cls: 'icon-bar'
3811                             }
3812                         ]
3813                     }
3814                 ]
3815             });
3816         }
3817         
3818         cn.push({
3819             tag: 'div',
3820             cls: 'collapse navbar-collapse',
3821             cn : []
3822         });
3823         
3824         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3825         
3826         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3827             cfg.cls += ' navbar-' + this.position;
3828             
3829             // tag can override this..
3830             
3831             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3832         }
3833         
3834         if (this.brand !== '') {
3835             cn[0].cn.push({
3836                 tag: 'a',
3837                 href: this.brand_href ? this.brand_href : '#',
3838                 cls: 'navbar-brand',
3839                 cn: [
3840                 this.brand
3841                 ]
3842             });
3843         }
3844         
3845         if(this.main){
3846             cfg.cls += ' main-nav';
3847         }
3848         
3849         
3850         return cfg;
3851
3852         
3853     },
3854     getHeaderChildContainer : function()
3855     {
3856         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3857             return this.el.select('.navbar-header',true).first();
3858         }
3859         
3860         return this.getChildContainer();
3861     },
3862     
3863     
3864     initEvents : function()
3865     {
3866         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3867         
3868         if (this.autohide) {
3869             
3870             var prevScroll = 0;
3871             var ft = this.el;
3872             
3873             Roo.get(document).on('scroll',function(e) {
3874                 var ns = Roo.get(document).getScroll().top;
3875                 var os = prevScroll;
3876                 prevScroll = ns;
3877                 
3878                 if(ns > os){
3879                     ft.removeClass('slideDown');
3880                     ft.addClass('slideUp');
3881                     return;
3882                 }
3883                 ft.removeClass('slideUp');
3884                 ft.addClass('slideDown');
3885                  
3886               
3887           },this);
3888         }
3889     }    
3890     
3891 });
3892
3893
3894
3895  
3896
3897  /*
3898  * - LGPL
3899  *
3900  * navbar
3901  * 
3902  */
3903
3904 /**
3905  * @class Roo.bootstrap.NavSidebar
3906  * @extends Roo.bootstrap.Navbar
3907  * Bootstrap Sidebar class
3908  * 
3909  * @constructor
3910  * Create a new Sidebar
3911  * @param {Object} config The config object
3912  */
3913
3914
3915 Roo.bootstrap.NavSidebar = function(config){
3916     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3917 };
3918
3919 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3920     
3921     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3922     
3923     getAutoCreate : function(){
3924         
3925         
3926         return  {
3927             tag: 'div',
3928             cls: 'sidebar sidebar-nav'
3929         };
3930     
3931         
3932     }
3933     
3934     
3935     
3936 });
3937
3938
3939
3940  
3941
3942  /*
3943  * - LGPL
3944  *
3945  * nav group
3946  * 
3947  */
3948
3949 /**
3950  * @class Roo.bootstrap.NavGroup
3951  * @extends Roo.bootstrap.Component
3952  * Bootstrap NavGroup class
3953  * @cfg {String} align (left|right)
3954  * @cfg {Boolean} inverse
3955  * @cfg {String} type (nav|pills|tab) default nav
3956  * @cfg {String} navId - reference Id for navbar.
3957
3958  * 
3959  * @constructor
3960  * Create a new nav group
3961  * @param {Object} config The config object
3962  */
3963
3964 Roo.bootstrap.NavGroup = function(config){
3965     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3966     this.navItems = [];
3967    
3968     Roo.bootstrap.NavGroup.register(this);
3969      this.addEvents({
3970         /**
3971              * @event changed
3972              * Fires when the active item changes
3973              * @param {Roo.bootstrap.NavGroup} this
3974              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3975              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3976          */
3977         'changed': true
3978      });
3979     
3980 };
3981
3982 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3983     
3984     align: '',
3985     inverse: false,
3986     form: false,
3987     type: 'nav',
3988     navId : '',
3989     // private
3990     
3991     navItems : false, 
3992     
3993     getAutoCreate : function()
3994     {
3995         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3996         
3997         cfg = {
3998             tag : 'ul',
3999             cls: 'nav' 
4000         };
4001         
4002         if (['tabs','pills'].indexOf(this.type)!==-1) {
4003             cfg.cls += ' nav-' + this.type
4004         } else {
4005             if (this.type!=='nav') {
4006                 Roo.log('nav type must be nav/tabs/pills')
4007             }
4008             cfg.cls += ' navbar-nav'
4009         }
4010         
4011         if (this.parent().sidebar) {
4012             cfg = {
4013                 tag: 'ul',
4014                 cls: 'dashboard-menu sidebar-menu'
4015             };
4016             
4017             return cfg;
4018         }
4019         
4020         if (this.form === true) {
4021             cfg = {
4022                 tag: 'form',
4023                 cls: 'navbar-form'
4024             };
4025             
4026             if (this.align === 'right') {
4027                 cfg.cls += ' navbar-right';
4028             } else {
4029                 cfg.cls += ' navbar-left';
4030             }
4031         }
4032         
4033         if (this.align === 'right') {
4034             cfg.cls += ' navbar-right';
4035         }
4036         
4037         if (this.inverse) {
4038             cfg.cls += ' navbar-inverse';
4039             
4040         }
4041         
4042         
4043         return cfg;
4044     },
4045     /**
4046     * sets the active Navigation item
4047     * @param {Roo.bootstrap.NavItem} the new current navitem
4048     */
4049     setActiveItem : function(item)
4050     {
4051         var prev = false;
4052         Roo.each(this.navItems, function(v){
4053             if (v == item) {
4054                 return ;
4055             }
4056             if (v.isActive()) {
4057                 v.setActive(false, true);
4058                 prev = v;
4059                 
4060             }
4061             
4062         });
4063
4064         item.setActive(true, true);
4065         this.fireEvent('changed', this, item, prev);
4066         
4067         
4068     },
4069     /**
4070     * gets the active Navigation item
4071     * @return {Roo.bootstrap.NavItem} the current navitem
4072     */
4073     getActive : function()
4074     {
4075         
4076         var prev = false;
4077         Roo.each(this.navItems, function(v){
4078             
4079             if (v.isActive()) {
4080                 prev = v;
4081                 
4082             }
4083             
4084         });
4085         return prev;
4086     },
4087     
4088     indexOfNav : function()
4089     {
4090         
4091         var prev = false;
4092         Roo.each(this.navItems, function(v,i){
4093             
4094             if (v.isActive()) {
4095                 prev = i;
4096                 
4097             }
4098             
4099         });
4100         return prev;
4101     },
4102     /**
4103     * adds a Navigation item
4104     * @param {Roo.bootstrap.NavItem} the navitem to add
4105     */
4106     addItem : function(cfg)
4107     {
4108         var cn = new Roo.bootstrap.NavItem(cfg);
4109         this.register(cn);
4110         cn.parentId = this.id;
4111         cn.onRender(this.el, null);
4112         return cn;
4113     },
4114     /**
4115     * register a Navigation item
4116     * @param {Roo.bootstrap.NavItem} the navitem to add
4117     */
4118     register : function(item)
4119     {
4120         this.navItems.push( item);
4121         item.navId = this.navId;
4122     
4123     },
4124     
4125     /**
4126     * clear all the Navigation item
4127     */
4128    
4129     clearAll : function()
4130     {
4131         this.navItems = [];
4132         this.el.dom.innerHTML = '';
4133     },
4134     
4135     getNavItem: function(tabId)
4136     {
4137         var ret = false;
4138         Roo.each(this.navItems, function(e) {
4139             if (e.tabId == tabId) {
4140                ret =  e;
4141                return false;
4142             }
4143             return true;
4144             
4145         });
4146         return ret;
4147     },
4148     
4149     setActiveNext : function()
4150     {
4151         var i = this.indexOfNav(this.getActive());
4152         if (i > this.navItems.length) {
4153             return;
4154         }
4155         this.setActiveItem(this.navItems[i+1]);
4156     },
4157     setActivePrev : function()
4158     {
4159         var i = this.indexOfNav(this.getActive());
4160         if (i  < 1) {
4161             return;
4162         }
4163         this.setActiveItem(this.navItems[i-1]);
4164     },
4165     clearWasActive : function(except) {
4166         Roo.each(this.navItems, function(e) {
4167             if (e.tabId != except.tabId && e.was_active) {
4168                e.was_active = false;
4169                return false;
4170             }
4171             return true;
4172             
4173         });
4174     },
4175     getWasActive : function ()
4176     {
4177         var r = false;
4178         Roo.each(this.navItems, function(e) {
4179             if (e.was_active) {
4180                r = e;
4181                return false;
4182             }
4183             return true;
4184             
4185         });
4186         return r;
4187     }
4188     
4189     
4190 });
4191
4192  
4193 Roo.apply(Roo.bootstrap.NavGroup, {
4194     
4195     groups: {},
4196      /**
4197     * register a Navigation Group
4198     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4199     */
4200     register : function(navgrp)
4201     {
4202         this.groups[navgrp.navId] = navgrp;
4203         
4204     },
4205     /**
4206     * fetch a Navigation Group based on the navigation ID
4207     * @param {string} the navgroup to add
4208     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4209     */
4210     get: function(navId) {
4211         if (typeof(this.groups[navId]) == 'undefined') {
4212             return false;
4213             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4214         }
4215         return this.groups[navId] ;
4216     }
4217     
4218     
4219     
4220 });
4221
4222  /*
4223  * - LGPL
4224  *
4225  * row
4226  * 
4227  */
4228
4229 /**
4230  * @class Roo.bootstrap.NavItem
4231  * @extends Roo.bootstrap.Component
4232  * Bootstrap Navbar.NavItem class
4233  * @cfg {String} href  link to
4234  * @cfg {String} html content of button
4235  * @cfg {String} badge text inside badge
4236  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4237  * @cfg {String} glyphicon name of glyphicon
4238  * @cfg {String} icon name of font awesome icon
4239  * @cfg {Boolean} active Is item active
4240  * @cfg {Boolean} disabled Is item disabled
4241  
4242  * @cfg {Boolean} preventDefault (true | false) default false
4243  * @cfg {String} tabId the tab that this item activates.
4244  * @cfg {String} tagtype (a|span) render as a href or span?
4245  * @cfg {Boolean} animateRef (true|false) link to element default false  
4246   
4247  * @constructor
4248  * Create a new Navbar Item
4249  * @param {Object} config The config object
4250  */
4251 Roo.bootstrap.NavItem = function(config){
4252     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4253     this.addEvents({
4254         // raw events
4255         /**
4256          * @event click
4257          * The raw click event for the entire grid.
4258          * @param {Roo.EventObject} e
4259          */
4260         "click" : true,
4261          /**
4262             * @event changed
4263             * Fires when the active item active state changes
4264             * @param {Roo.bootstrap.NavItem} this
4265             * @param {boolean} state the new state
4266              
4267          */
4268         'changed': true,
4269         /**
4270             * @event scrollto
4271             * Fires when scroll to element
4272             * @param {Roo.bootstrap.NavItem} this
4273             * @param {Object} options
4274             * @param {Roo.EventObject} e
4275              
4276          */
4277         'scrollto': true
4278     });
4279    
4280 };
4281
4282 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4283     
4284     href: false,
4285     html: '',
4286     badge: '',
4287     icon: false,
4288     glyphicon: false,
4289     active: false,
4290     preventDefault : false,
4291     tabId : false,
4292     tagtype : 'a',
4293     disabled : false,
4294     animateRef : false,
4295     was_active : false,
4296     
4297     getAutoCreate : function(){
4298          
4299         var cfg = {
4300             tag: 'li',
4301             cls: 'nav-item'
4302             
4303         };
4304         
4305         if (this.active) {
4306             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4307         }
4308         if (this.disabled) {
4309             cfg.cls += ' disabled';
4310         }
4311         
4312         if (this.href || this.html || this.glyphicon || this.icon) {
4313             cfg.cn = [
4314                 {
4315                     tag: this.tagtype,
4316                     href : this.href || "#",
4317                     html: this.html || ''
4318                 }
4319             ];
4320             
4321             if (this.icon) {
4322                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4323             }
4324
4325             if(this.glyphicon) {
4326                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4327             }
4328             
4329             if (this.menu) {
4330                 
4331                 cfg.cn[0].html += " <span class='caret'></span>";
4332              
4333             }
4334             
4335             if (this.badge !== '') {
4336                  
4337                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4338             }
4339         }
4340         
4341         
4342         
4343         return cfg;
4344     },
4345     initEvents: function() 
4346     {
4347         if (typeof (this.menu) != 'undefined') {
4348             this.menu.parentType = this.xtype;
4349             this.menu.triggerEl = this.el;
4350             this.menu = this.addxtype(Roo.apply({}, this.menu));
4351         }
4352         
4353         this.el.select('a',true).on('click', this.onClick, this);
4354         
4355         if(this.tagtype == 'span'){
4356             this.el.select('span',true).on('click', this.onClick, this);
4357         }
4358        
4359         // at this point parent should be available..
4360         this.parent().register(this);
4361     },
4362     
4363     onClick : function(e)
4364     {
4365         if (e.getTarget('.dropdown-menu-item')) {
4366             // did you click on a menu itemm.... - then don't trigger onclick..
4367             return;
4368         }
4369         
4370         if(
4371                 this.preventDefault || 
4372                 this.href == '#' 
4373         ){
4374             Roo.log("NavItem - prevent Default?");
4375             e.preventDefault();
4376         }
4377         
4378         if (this.disabled) {
4379             return;
4380         }
4381         
4382         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4383         if (tg && tg.transition) {
4384             Roo.log("waiting for the transitionend");
4385             return;
4386         }
4387         
4388         
4389         
4390         //Roo.log("fire event clicked");
4391         if(this.fireEvent('click', this, e) === false){
4392             return;
4393         };
4394         
4395         if(this.tagtype == 'span'){
4396             return;
4397         }
4398         
4399         //Roo.log(this.href);
4400         var ael = this.el.select('a',true).first();
4401         //Roo.log(ael);
4402         
4403         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4404             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4405             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4406                 return; // ignore... - it's a 'hash' to another page.
4407             }
4408             Roo.log("NavItem - prevent Default?");
4409             e.preventDefault();
4410             this.scrollToElement(e);
4411         }
4412         
4413         
4414         var p =  this.parent();
4415    
4416         if (['tabs','pills'].indexOf(p.type)!==-1) {
4417             if (typeof(p.setActiveItem) !== 'undefined') {
4418                 p.setActiveItem(this);
4419             }
4420         }
4421         
4422         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4423         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4424             // remove the collapsed menu expand...
4425             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4426         }
4427     },
4428     
4429     isActive: function () {
4430         return this.active
4431     },
4432     setActive : function(state, fire, is_was_active)
4433     {
4434         if (this.active && !state && this.navId) {
4435             this.was_active = true;
4436             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4437             if (nv) {
4438                 nv.clearWasActive(this);
4439             }
4440             
4441         }
4442         this.active = state;
4443         
4444         if (!state ) {
4445             this.el.removeClass('active');
4446         } else if (!this.el.hasClass('active')) {
4447             this.el.addClass('active');
4448         }
4449         if (fire) {
4450             this.fireEvent('changed', this, state);
4451         }
4452         
4453         // show a panel if it's registered and related..
4454         
4455         if (!this.navId || !this.tabId || !state || is_was_active) {
4456             return;
4457         }
4458         
4459         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4460         if (!tg) {
4461             return;
4462         }
4463         var pan = tg.getPanelByName(this.tabId);
4464         if (!pan) {
4465             return;
4466         }
4467         // if we can not flip to new panel - go back to old nav highlight..
4468         if (false == tg.showPanel(pan)) {
4469             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4470             if (nv) {
4471                 var onav = nv.getWasActive();
4472                 if (onav) {
4473                     onav.setActive(true, false, true);
4474                 }
4475             }
4476             
4477         }
4478         
4479         
4480         
4481     },
4482      // this should not be here...
4483     setDisabled : function(state)
4484     {
4485         this.disabled = state;
4486         if (!state ) {
4487             this.el.removeClass('disabled');
4488         } else if (!this.el.hasClass('disabled')) {
4489             this.el.addClass('disabled');
4490         }
4491         
4492     },
4493     
4494     /**
4495      * Fetch the element to display the tooltip on.
4496      * @return {Roo.Element} defaults to this.el
4497      */
4498     tooltipEl : function()
4499     {
4500         return this.el.select('' + this.tagtype + '', true).first();
4501     },
4502     
4503     scrollToElement : function(e)
4504     {
4505         var c = document.body;
4506         
4507         /*
4508          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4509          */
4510         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4511             c = document.documentElement;
4512         }
4513         
4514         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4515         
4516         if(!target){
4517             return;
4518         }
4519
4520         var o = target.calcOffsetsTo(c);
4521         
4522         var options = {
4523             target : target,
4524             value : o[1]
4525         };
4526         
4527         this.fireEvent('scrollto', this, options, e);
4528         
4529         Roo.get(c).scrollTo('top', options.value, true);
4530         
4531         return;
4532     }
4533 });
4534  
4535
4536  /*
4537  * - LGPL
4538  *
4539  * sidebar item
4540  *
4541  *  li
4542  *    <span> icon </span>
4543  *    <span> text </span>
4544  *    <span>badge </span>
4545  */
4546
4547 /**
4548  * @class Roo.bootstrap.NavSidebarItem
4549  * @extends Roo.bootstrap.NavItem
4550  * Bootstrap Navbar.NavSidebarItem class
4551  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4552  * {bool} open is the menu open
4553  * @constructor
4554  * Create a new Navbar Button
4555  * @param {Object} config The config object
4556  */
4557 Roo.bootstrap.NavSidebarItem = function(config){
4558     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4559     this.addEvents({
4560         // raw events
4561         /**
4562          * @event click
4563          * The raw click event for the entire grid.
4564          * @param {Roo.EventObject} e
4565          */
4566         "click" : true,
4567          /**
4568             * @event changed
4569             * Fires when the active item active state changes
4570             * @param {Roo.bootstrap.NavSidebarItem} this
4571             * @param {boolean} state the new state
4572              
4573          */
4574         'changed': true
4575     });
4576    
4577 };
4578
4579 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4580     
4581     badgeWeight : 'default',
4582     
4583     open: false,
4584     
4585     getAutoCreate : function(){
4586         
4587         
4588         var a = {
4589                 tag: 'a',
4590                 href : this.href || '#',
4591                 cls: '',
4592                 html : '',
4593                 cn : []
4594         };
4595         var cfg = {
4596             tag: 'li',
4597             cls: '',
4598             cn: [ a ]
4599         };
4600         var span = {
4601             tag: 'span',
4602             html : this.html || ''
4603         };
4604         
4605         
4606         if (this.active) {
4607             cfg.cls += ' active';
4608         }
4609         
4610         if (this.disabled) {
4611             cfg.cls += ' disabled';
4612         }
4613         if (this.open) {
4614             cfg.cls += ' open x-open';
4615         }
4616         // left icon..
4617         if (this.glyphicon || this.icon) {
4618             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4619             a.cn.push({ tag : 'i', cls : c }) ;
4620         }
4621         // html..
4622         a.cn.push(span);
4623         // then badge..
4624         if (this.badge !== '') {
4625             
4626             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4627         }
4628         // fi
4629         if (this.menu) {
4630             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4631             a.cls += 'dropdown-toggle treeview' ;
4632         }
4633         
4634         return cfg;
4635          
4636            
4637     },
4638     
4639     initEvents : function()
4640     { 
4641         if (typeof (this.menu) != 'undefined') {
4642             this.menu.parentType = this.xtype;
4643             this.menu.triggerEl = this.el;
4644             this.menu = this.addxtype(Roo.apply({}, this.menu));
4645         }
4646         
4647         this.el.on('click', this.onClick, this);
4648        
4649     
4650         if(this.badge !== ''){
4651  
4652             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4653         }
4654         
4655     },
4656     
4657     onClick : function(e)
4658     {
4659         if(this.disabled){
4660             e.preventDefault();
4661             return;
4662         }
4663         
4664         if(this.preventDefault){
4665             e.preventDefault();
4666         }
4667         
4668         this.fireEvent('click', this);
4669     },
4670     
4671     disable : function()
4672     {
4673         this.setDisabled(true);
4674     },
4675     
4676     enable : function()
4677     {
4678         this.setDisabled(false);
4679     },
4680     
4681     setDisabled : function(state)
4682     {
4683         if(this.disabled == state){
4684             return;
4685         }
4686         
4687         this.disabled = state;
4688         
4689         if (state) {
4690             this.el.addClass('disabled');
4691             return;
4692         }
4693         
4694         this.el.removeClass('disabled');
4695         
4696         return;
4697     },
4698     
4699     setActive : function(state)
4700     {
4701         if(this.active == state){
4702             return;
4703         }
4704         
4705         this.active = state;
4706         
4707         if (state) {
4708             this.el.addClass('active');
4709             return;
4710         }
4711         
4712         this.el.removeClass('active');
4713         
4714         return;
4715     },
4716     
4717     isActive: function () 
4718     {
4719         return this.active;
4720     },
4721     
4722     setBadge : function(str)
4723     {
4724         if(!this.badgeEl){
4725             return;
4726         }
4727         
4728         this.badgeEl.dom.innerHTML = str;
4729     }
4730     
4731    
4732      
4733  
4734 });
4735  
4736
4737  /*
4738  * - LGPL
4739  *
4740  * row
4741  * 
4742  */
4743
4744 /**
4745  * @class Roo.bootstrap.Row
4746  * @extends Roo.bootstrap.Component
4747  * Bootstrap Row class (contains columns...)
4748  * 
4749  * @constructor
4750  * Create a new Row
4751  * @param {Object} config The config object
4752  */
4753
4754 Roo.bootstrap.Row = function(config){
4755     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4756 };
4757
4758 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4759     
4760     getAutoCreate : function(){
4761        return {
4762             cls: 'row clearfix'
4763        };
4764     }
4765     
4766     
4767 });
4768
4769  
4770
4771  /*
4772  * - LGPL
4773  *
4774  * element
4775  * 
4776  */
4777
4778 /**
4779  * @class Roo.bootstrap.Element
4780  * @extends Roo.bootstrap.Component
4781  * Bootstrap Element class
4782  * @cfg {String} html contents of the element
4783  * @cfg {String} tag tag of the element
4784  * @cfg {String} cls class of the element
4785  * @cfg {Boolean} preventDefault (true|false) default false
4786  * @cfg {Boolean} clickable (true|false) default false
4787  * 
4788  * @constructor
4789  * Create a new Element
4790  * @param {Object} config The config object
4791  */
4792
4793 Roo.bootstrap.Element = function(config){
4794     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4795     
4796     this.addEvents({
4797         // raw events
4798         /**
4799          * @event click
4800          * When a element is chick
4801          * @param {Roo.bootstrap.Element} this
4802          * @param {Roo.EventObject} e
4803          */
4804         "click" : true
4805     });
4806 };
4807
4808 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4809     
4810     tag: 'div',
4811     cls: '',
4812     html: '',
4813     preventDefault: false, 
4814     clickable: false,
4815     
4816     getAutoCreate : function(){
4817         
4818         var cfg = {
4819             tag: this.tag,
4820             cls: this.cls,
4821             html: this.html
4822         };
4823         
4824         return cfg;
4825     },
4826     
4827     initEvents: function() 
4828     {
4829         Roo.bootstrap.Element.superclass.initEvents.call(this);
4830         
4831         if(this.clickable){
4832             this.el.on('click', this.onClick, this);
4833         }
4834         
4835     },
4836     
4837     onClick : function(e)
4838     {
4839         if(this.preventDefault){
4840             e.preventDefault();
4841         }
4842         
4843         this.fireEvent('click', this, e);
4844     },
4845     
4846     getValue : function()
4847     {
4848         return this.el.dom.innerHTML;
4849     },
4850     
4851     setValue : function(value)
4852     {
4853         this.el.dom.innerHTML = value;
4854     }
4855    
4856 });
4857
4858  
4859
4860  /*
4861  * - LGPL
4862  *
4863  * pagination
4864  * 
4865  */
4866
4867 /**
4868  * @class Roo.bootstrap.Pagination
4869  * @extends Roo.bootstrap.Component
4870  * Bootstrap Pagination class
4871  * @cfg {String} size xs | sm | md | lg
4872  * @cfg {Boolean} inverse false | true
4873  * 
4874  * @constructor
4875  * Create a new Pagination
4876  * @param {Object} config The config object
4877  */
4878
4879 Roo.bootstrap.Pagination = function(config){
4880     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4881 };
4882
4883 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4884     
4885     cls: false,
4886     size: false,
4887     inverse: false,
4888     
4889     getAutoCreate : function(){
4890         var cfg = {
4891             tag: 'ul',
4892                 cls: 'pagination'
4893         };
4894         if (this.inverse) {
4895             cfg.cls += ' inverse';
4896         }
4897         if (this.html) {
4898             cfg.html=this.html;
4899         }
4900         if (this.cls) {
4901             cfg.cls += " " + this.cls;
4902         }
4903         return cfg;
4904     }
4905    
4906 });
4907
4908  
4909
4910  /*
4911  * - LGPL
4912  *
4913  * Pagination item
4914  * 
4915  */
4916
4917
4918 /**
4919  * @class Roo.bootstrap.PaginationItem
4920  * @extends Roo.bootstrap.Component
4921  * Bootstrap PaginationItem class
4922  * @cfg {String} html text
4923  * @cfg {String} href the link
4924  * @cfg {Boolean} preventDefault (true | false) default true
4925  * @cfg {Boolean} active (true | false) default false
4926  * @cfg {Boolean} disabled default false
4927  * 
4928  * 
4929  * @constructor
4930  * Create a new PaginationItem
4931  * @param {Object} config The config object
4932  */
4933
4934
4935 Roo.bootstrap.PaginationItem = function(config){
4936     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4937     this.addEvents({
4938         // raw events
4939         /**
4940          * @event click
4941          * The raw click event for the entire grid.
4942          * @param {Roo.EventObject} e
4943          */
4944         "click" : true
4945     });
4946 };
4947
4948 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4949     
4950     href : false,
4951     html : false,
4952     preventDefault: true,
4953     active : false,
4954     cls : false,
4955     disabled: false,
4956     
4957     getAutoCreate : function(){
4958         var cfg= {
4959             tag: 'li',
4960             cn: [
4961                 {
4962                     tag : 'a',
4963                     href : this.href ? this.href : '#',
4964                     html : this.html ? this.html : ''
4965                 }
4966             ]
4967         };
4968         
4969         if(this.cls){
4970             cfg.cls = this.cls;
4971         }
4972         
4973         if(this.disabled){
4974             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4975         }
4976         
4977         if(this.active){
4978             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4979         }
4980         
4981         return cfg;
4982     },
4983     
4984     initEvents: function() {
4985         
4986         this.el.on('click', this.onClick, this);
4987         
4988     },
4989     onClick : function(e)
4990     {
4991         Roo.log('PaginationItem on click ');
4992         if(this.preventDefault){
4993             e.preventDefault();
4994         }
4995         
4996         if(this.disabled){
4997             return;
4998         }
4999         
5000         this.fireEvent('click', this, e);
5001     }
5002    
5003 });
5004
5005  
5006
5007  /*
5008  * - LGPL
5009  *
5010  * slider
5011  * 
5012  */
5013
5014
5015 /**
5016  * @class Roo.bootstrap.Slider
5017  * @extends Roo.bootstrap.Component
5018  * Bootstrap Slider class
5019  *    
5020  * @constructor
5021  * Create a new Slider
5022  * @param {Object} config The config object
5023  */
5024
5025 Roo.bootstrap.Slider = function(config){
5026     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5027 };
5028
5029 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5030     
5031     getAutoCreate : function(){
5032         
5033         var cfg = {
5034             tag: 'div',
5035             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5036             cn: [
5037                 {
5038                     tag: 'a',
5039                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5040                 }
5041             ]
5042         };
5043         
5044         return cfg;
5045     }
5046    
5047 });
5048
5049  /*
5050  * Based on:
5051  * Ext JS Library 1.1.1
5052  * Copyright(c) 2006-2007, Ext JS, LLC.
5053  *
5054  * Originally Released Under LGPL - original licence link has changed is not relivant.
5055  *
5056  * Fork - LGPL
5057  * <script type="text/javascript">
5058  */
5059  
5060
5061 /**
5062  * @class Roo.grid.ColumnModel
5063  * @extends Roo.util.Observable
5064  * This is the default implementation of a ColumnModel used by the Grid. It defines
5065  * the columns in the grid.
5066  * <br>Usage:<br>
5067  <pre><code>
5068  var colModel = new Roo.grid.ColumnModel([
5069         {header: "Ticker", width: 60, sortable: true, locked: true},
5070         {header: "Company Name", width: 150, sortable: true},
5071         {header: "Market Cap.", width: 100, sortable: true},
5072         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5073         {header: "Employees", width: 100, sortable: true, resizable: false}
5074  ]);
5075  </code></pre>
5076  * <p>
5077  
5078  * The config options listed for this class are options which may appear in each
5079  * individual column definition.
5080  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5081  * @constructor
5082  * @param {Object} config An Array of column config objects. See this class's
5083  * config objects for details.
5084 */
5085 Roo.grid.ColumnModel = function(config){
5086         /**
5087      * The config passed into the constructor
5088      */
5089     this.config = config;
5090     this.lookup = {};
5091
5092     // if no id, create one
5093     // if the column does not have a dataIndex mapping,
5094     // map it to the order it is in the config
5095     for(var i = 0, len = config.length; i < len; i++){
5096         var c = config[i];
5097         if(typeof c.dataIndex == "undefined"){
5098             c.dataIndex = i;
5099         }
5100         if(typeof c.renderer == "string"){
5101             c.renderer = Roo.util.Format[c.renderer];
5102         }
5103         if(typeof c.id == "undefined"){
5104             c.id = Roo.id();
5105         }
5106         if(c.editor && c.editor.xtype){
5107             c.editor  = Roo.factory(c.editor, Roo.grid);
5108         }
5109         if(c.editor && c.editor.isFormField){
5110             c.editor = new Roo.grid.GridEditor(c.editor);
5111         }
5112         this.lookup[c.id] = c;
5113     }
5114
5115     /**
5116      * The width of columns which have no width specified (defaults to 100)
5117      * @type Number
5118      */
5119     this.defaultWidth = 100;
5120
5121     /**
5122      * Default sortable of columns which have no sortable specified (defaults to false)
5123      * @type Boolean
5124      */
5125     this.defaultSortable = false;
5126
5127     this.addEvents({
5128         /**
5129              * @event widthchange
5130              * Fires when the width of a column changes.
5131              * @param {ColumnModel} this
5132              * @param {Number} columnIndex The column index
5133              * @param {Number} newWidth The new width
5134              */
5135             "widthchange": true,
5136         /**
5137              * @event headerchange
5138              * Fires when the text of a header changes.
5139              * @param {ColumnModel} this
5140              * @param {Number} columnIndex The column index
5141              * @param {Number} newText The new header text
5142              */
5143             "headerchange": true,
5144         /**
5145              * @event hiddenchange
5146              * Fires when a column is hidden or "unhidden".
5147              * @param {ColumnModel} this
5148              * @param {Number} columnIndex The column index
5149              * @param {Boolean} hidden true if hidden, false otherwise
5150              */
5151             "hiddenchange": true,
5152             /**
5153          * @event columnmoved
5154          * Fires when a column is moved.
5155          * @param {ColumnModel} this
5156          * @param {Number} oldIndex
5157          * @param {Number} newIndex
5158          */
5159         "columnmoved" : true,
5160         /**
5161          * @event columlockchange
5162          * Fires when a column's locked state is changed
5163          * @param {ColumnModel} this
5164          * @param {Number} colIndex
5165          * @param {Boolean} locked true if locked
5166          */
5167         "columnlockchange" : true
5168     });
5169     Roo.grid.ColumnModel.superclass.constructor.call(this);
5170 };
5171 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5172     /**
5173      * @cfg {String} header The header text to display in the Grid view.
5174      */
5175     /**
5176      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5177      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5178      * specified, the column's index is used as an index into the Record's data Array.
5179      */
5180     /**
5181      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5182      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5183      */
5184     /**
5185      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5186      * Defaults to the value of the {@link #defaultSortable} property.
5187      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5188      */
5189     /**
5190      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5191      */
5192     /**
5193      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5194      */
5195     /**
5196      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5197      */
5198     /**
5199      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5200      */
5201     /**
5202      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5203      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5204      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5205      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5206      */
5207        /**
5208      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5209      */
5210     /**
5211      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5212      */
5213     /**
5214      * @cfg {String} cursor (Optional)
5215      */
5216     /**
5217      * @cfg {String} tooltip (Optional)
5218      */
5219     /**
5220      * @cfg {Number} xs (Optional)
5221      */
5222     /**
5223      * @cfg {Number} sm (Optional)
5224      */
5225     /**
5226      * @cfg {Number} md (Optional)
5227      */
5228     /**
5229      * @cfg {Number} lg (Optional)
5230      */
5231     /**
5232      * Returns the id of the column at the specified index.
5233      * @param {Number} index The column index
5234      * @return {String} the id
5235      */
5236     getColumnId : function(index){
5237         return this.config[index].id;
5238     },
5239
5240     /**
5241      * Returns the column for a specified id.
5242      * @param {String} id The column id
5243      * @return {Object} the column
5244      */
5245     getColumnById : function(id){
5246         return this.lookup[id];
5247     },
5248
5249     
5250     /**
5251      * Returns the column for a specified dataIndex.
5252      * @param {String} dataIndex The column dataIndex
5253      * @return {Object|Boolean} the column or false if not found
5254      */
5255     getColumnByDataIndex: function(dataIndex){
5256         var index = this.findColumnIndex(dataIndex);
5257         return index > -1 ? this.config[index] : false;
5258     },
5259     
5260     /**
5261      * Returns the index for a specified column id.
5262      * @param {String} id The column id
5263      * @return {Number} the index, or -1 if not found
5264      */
5265     getIndexById : function(id){
5266         for(var i = 0, len = this.config.length; i < len; i++){
5267             if(this.config[i].id == id){
5268                 return i;
5269             }
5270         }
5271         return -1;
5272     },
5273     
5274     /**
5275      * Returns the index for a specified column dataIndex.
5276      * @param {String} dataIndex The column dataIndex
5277      * @return {Number} the index, or -1 if not found
5278      */
5279     
5280     findColumnIndex : function(dataIndex){
5281         for(var i = 0, len = this.config.length; i < len; i++){
5282             if(this.config[i].dataIndex == dataIndex){
5283                 return i;
5284             }
5285         }
5286         return -1;
5287     },
5288     
5289     
5290     moveColumn : function(oldIndex, newIndex){
5291         var c = this.config[oldIndex];
5292         this.config.splice(oldIndex, 1);
5293         this.config.splice(newIndex, 0, c);
5294         this.dataMap = null;
5295         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5296     },
5297
5298     isLocked : function(colIndex){
5299         return this.config[colIndex].locked === true;
5300     },
5301
5302     setLocked : function(colIndex, value, suppressEvent){
5303         if(this.isLocked(colIndex) == value){
5304             return;
5305         }
5306         this.config[colIndex].locked = value;
5307         if(!suppressEvent){
5308             this.fireEvent("columnlockchange", this, colIndex, value);
5309         }
5310     },
5311
5312     getTotalLockedWidth : function(){
5313         var totalWidth = 0;
5314         for(var i = 0; i < this.config.length; i++){
5315             if(this.isLocked(i) && !this.isHidden(i)){
5316                 this.totalWidth += this.getColumnWidth(i);
5317             }
5318         }
5319         return totalWidth;
5320     },
5321
5322     getLockedCount : function(){
5323         for(var i = 0, len = this.config.length; i < len; i++){
5324             if(!this.isLocked(i)){
5325                 return i;
5326             }
5327         }
5328         
5329         return this.config.length;
5330     },
5331
5332     /**
5333      * Returns the number of columns.
5334      * @return {Number}
5335      */
5336     getColumnCount : function(visibleOnly){
5337         if(visibleOnly === true){
5338             var c = 0;
5339             for(var i = 0, len = this.config.length; i < len; i++){
5340                 if(!this.isHidden(i)){
5341                     c++;
5342                 }
5343             }
5344             return c;
5345         }
5346         return this.config.length;
5347     },
5348
5349     /**
5350      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5351      * @param {Function} fn
5352      * @param {Object} scope (optional)
5353      * @return {Array} result
5354      */
5355     getColumnsBy : function(fn, scope){
5356         var r = [];
5357         for(var i = 0, len = this.config.length; i < len; i++){
5358             var c = this.config[i];
5359             if(fn.call(scope||this, c, i) === true){
5360                 r[r.length] = c;
5361             }
5362         }
5363         return r;
5364     },
5365
5366     /**
5367      * Returns true if the specified column is sortable.
5368      * @param {Number} col The column index
5369      * @return {Boolean}
5370      */
5371     isSortable : function(col){
5372         if(typeof this.config[col].sortable == "undefined"){
5373             return this.defaultSortable;
5374         }
5375         return this.config[col].sortable;
5376     },
5377
5378     /**
5379      * Returns the rendering (formatting) function defined for the column.
5380      * @param {Number} col The column index.
5381      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5382      */
5383     getRenderer : function(col){
5384         if(!this.config[col].renderer){
5385             return Roo.grid.ColumnModel.defaultRenderer;
5386         }
5387         return this.config[col].renderer;
5388     },
5389
5390     /**
5391      * Sets the rendering (formatting) function for a column.
5392      * @param {Number} col The column index
5393      * @param {Function} fn The function to use to process the cell's raw data
5394      * to return HTML markup for the grid view. The render function is called with
5395      * the following parameters:<ul>
5396      * <li>Data value.</li>
5397      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5398      * <li>css A CSS style string to apply to the table cell.</li>
5399      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5400      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5401      * <li>Row index</li>
5402      * <li>Column index</li>
5403      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5404      */
5405     setRenderer : function(col, fn){
5406         this.config[col].renderer = fn;
5407     },
5408
5409     /**
5410      * Returns the width for the specified column.
5411      * @param {Number} col The column index
5412      * @return {Number}
5413      */
5414     getColumnWidth : function(col){
5415         return this.config[col].width * 1 || this.defaultWidth;
5416     },
5417
5418     /**
5419      * Sets the width for a column.
5420      * @param {Number} col The column index
5421      * @param {Number} width The new width
5422      */
5423     setColumnWidth : function(col, width, suppressEvent){
5424         this.config[col].width = width;
5425         this.totalWidth = null;
5426         if(!suppressEvent){
5427              this.fireEvent("widthchange", this, col, width);
5428         }
5429     },
5430
5431     /**
5432      * Returns the total width of all columns.
5433      * @param {Boolean} includeHidden True to include hidden column widths
5434      * @return {Number}
5435      */
5436     getTotalWidth : function(includeHidden){
5437         if(!this.totalWidth){
5438             this.totalWidth = 0;
5439             for(var i = 0, len = this.config.length; i < len; i++){
5440                 if(includeHidden || !this.isHidden(i)){
5441                     this.totalWidth += this.getColumnWidth(i);
5442                 }
5443             }
5444         }
5445         return this.totalWidth;
5446     },
5447
5448     /**
5449      * Returns the header for the specified column.
5450      * @param {Number} col The column index
5451      * @return {String}
5452      */
5453     getColumnHeader : function(col){
5454         return this.config[col].header;
5455     },
5456
5457     /**
5458      * Sets the header for a column.
5459      * @param {Number} col The column index
5460      * @param {String} header The new header
5461      */
5462     setColumnHeader : function(col, header){
5463         this.config[col].header = header;
5464         this.fireEvent("headerchange", this, col, header);
5465     },
5466
5467     /**
5468      * Returns the tooltip for the specified column.
5469      * @param {Number} col The column index
5470      * @return {String}
5471      */
5472     getColumnTooltip : function(col){
5473             return this.config[col].tooltip;
5474     },
5475     /**
5476      * Sets the tooltip for a column.
5477      * @param {Number} col The column index
5478      * @param {String} tooltip The new tooltip
5479      */
5480     setColumnTooltip : function(col, tooltip){
5481             this.config[col].tooltip = tooltip;
5482     },
5483
5484     /**
5485      * Returns the dataIndex for the specified column.
5486      * @param {Number} col The column index
5487      * @return {Number}
5488      */
5489     getDataIndex : function(col){
5490         return this.config[col].dataIndex;
5491     },
5492
5493     /**
5494      * Sets the dataIndex for a column.
5495      * @param {Number} col The column index
5496      * @param {Number} dataIndex The new dataIndex
5497      */
5498     setDataIndex : function(col, dataIndex){
5499         this.config[col].dataIndex = dataIndex;
5500     },
5501
5502     
5503     
5504     /**
5505      * Returns true if the cell is editable.
5506      * @param {Number} colIndex The column index
5507      * @param {Number} rowIndex The row index - this is nto actually used..?
5508      * @return {Boolean}
5509      */
5510     isCellEditable : function(colIndex, rowIndex){
5511         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5512     },
5513
5514     /**
5515      * Returns the editor defined for the cell/column.
5516      * return false or null to disable editing.
5517      * @param {Number} colIndex The column index
5518      * @param {Number} rowIndex The row index
5519      * @return {Object}
5520      */
5521     getCellEditor : function(colIndex, rowIndex){
5522         return this.config[colIndex].editor;
5523     },
5524
5525     /**
5526      * Sets if a column is editable.
5527      * @param {Number} col The column index
5528      * @param {Boolean} editable True if the column is editable
5529      */
5530     setEditable : function(col, editable){
5531         this.config[col].editable = editable;
5532     },
5533
5534
5535     /**
5536      * Returns true if the column is hidden.
5537      * @param {Number} colIndex The column index
5538      * @return {Boolean}
5539      */
5540     isHidden : function(colIndex){
5541         return this.config[colIndex].hidden;
5542     },
5543
5544
5545     /**
5546      * Returns true if the column width cannot be changed
5547      */
5548     isFixed : function(colIndex){
5549         return this.config[colIndex].fixed;
5550     },
5551
5552     /**
5553      * Returns true if the column can be resized
5554      * @return {Boolean}
5555      */
5556     isResizable : function(colIndex){
5557         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5558     },
5559     /**
5560      * Sets if a column is hidden.
5561      * @param {Number} colIndex The column index
5562      * @param {Boolean} hidden True if the column is hidden
5563      */
5564     setHidden : function(colIndex, hidden){
5565         this.config[colIndex].hidden = hidden;
5566         this.totalWidth = null;
5567         this.fireEvent("hiddenchange", this, colIndex, hidden);
5568     },
5569
5570     /**
5571      * Sets the editor for a column.
5572      * @param {Number} col The column index
5573      * @param {Object} editor The editor object
5574      */
5575     setEditor : function(col, editor){
5576         this.config[col].editor = editor;
5577     }
5578 });
5579
5580 Roo.grid.ColumnModel.defaultRenderer = function(value)
5581 {
5582     if(typeof value == "object") {
5583         return value;
5584     }
5585         if(typeof value == "string" && value.length < 1){
5586             return "&#160;";
5587         }
5588     
5589         return String.format("{0}", value);
5590 };
5591
5592 // Alias for backwards compatibility
5593 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5594 /*
5595  * Based on:
5596  * Ext JS Library 1.1.1
5597  * Copyright(c) 2006-2007, Ext JS, LLC.
5598  *
5599  * Originally Released Under LGPL - original licence link has changed is not relivant.
5600  *
5601  * Fork - LGPL
5602  * <script type="text/javascript">
5603  */
5604  
5605 /**
5606  * @class Roo.LoadMask
5607  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5608  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5609  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5610  * element's UpdateManager load indicator and will be destroyed after the initial load.
5611  * @constructor
5612  * Create a new LoadMask
5613  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5614  * @param {Object} config The config object
5615  */
5616 Roo.LoadMask = function(el, config){
5617     this.el = Roo.get(el);
5618     Roo.apply(this, config);
5619     if(this.store){
5620         this.store.on('beforeload', this.onBeforeLoad, this);
5621         this.store.on('load', this.onLoad, this);
5622         this.store.on('loadexception', this.onLoadException, this);
5623         this.removeMask = false;
5624     }else{
5625         var um = this.el.getUpdateManager();
5626         um.showLoadIndicator = false; // disable the default indicator
5627         um.on('beforeupdate', this.onBeforeLoad, this);
5628         um.on('update', this.onLoad, this);
5629         um.on('failure', this.onLoad, this);
5630         this.removeMask = true;
5631     }
5632 };
5633
5634 Roo.LoadMask.prototype = {
5635     /**
5636      * @cfg {Boolean} removeMask
5637      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5638      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5639      */
5640     /**
5641      * @cfg {String} msg
5642      * The text to display in a centered loading message box (defaults to 'Loading...')
5643      */
5644     msg : 'Loading...',
5645     /**
5646      * @cfg {String} msgCls
5647      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5648      */
5649     msgCls : 'x-mask-loading',
5650
5651     /**
5652      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5653      * @type Boolean
5654      */
5655     disabled: false,
5656
5657     /**
5658      * Disables the mask to prevent it from being displayed
5659      */
5660     disable : function(){
5661        this.disabled = true;
5662     },
5663
5664     /**
5665      * Enables the mask so that it can be displayed
5666      */
5667     enable : function(){
5668         this.disabled = false;
5669     },
5670     
5671     onLoadException : function()
5672     {
5673         Roo.log(arguments);
5674         
5675         if (typeof(arguments[3]) != 'undefined') {
5676             Roo.MessageBox.alert("Error loading",arguments[3]);
5677         } 
5678         /*
5679         try {
5680             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5681                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5682             }   
5683         } catch(e) {
5684             
5685         }
5686         */
5687     
5688         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5689     },
5690     // private
5691     onLoad : function()
5692     {
5693         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5694     },
5695
5696     // private
5697     onBeforeLoad : function(){
5698         if(!this.disabled){
5699             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5700         }
5701     },
5702
5703     // private
5704     destroy : function(){
5705         if(this.store){
5706             this.store.un('beforeload', this.onBeforeLoad, this);
5707             this.store.un('load', this.onLoad, this);
5708             this.store.un('loadexception', this.onLoadException, this);
5709         }else{
5710             var um = this.el.getUpdateManager();
5711             um.un('beforeupdate', this.onBeforeLoad, this);
5712             um.un('update', this.onLoad, this);
5713             um.un('failure', this.onLoad, this);
5714         }
5715     }
5716 };/*
5717  * - LGPL
5718  *
5719  * table
5720  * 
5721  */
5722
5723 /**
5724  * @class Roo.bootstrap.Table
5725  * @extends Roo.bootstrap.Component
5726  * Bootstrap Table class
5727  * @cfg {String} cls table class
5728  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5729  * @cfg {String} bgcolor Specifies the background color for a table
5730  * @cfg {Number} border Specifies whether the table cells should have borders or not
5731  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5732  * @cfg {Number} cellspacing Specifies the space between cells
5733  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5734  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5735  * @cfg {String} sortable Specifies that the table should be sortable
5736  * @cfg {String} summary Specifies a summary of the content of a table
5737  * @cfg {Number} width Specifies the width of a table
5738  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5739  * 
5740  * @cfg {boolean} striped Should the rows be alternative striped
5741  * @cfg {boolean} bordered Add borders to the table
5742  * @cfg {boolean} hover Add hover highlighting
5743  * @cfg {boolean} condensed Format condensed
5744  * @cfg {boolean} responsive Format condensed
5745  * @cfg {Boolean} loadMask (true|false) default false
5746  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5747  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5748  * @cfg {Boolean} rowSelection (true|false) default false
5749  * @cfg {Boolean} cellSelection (true|false) default false
5750  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5751  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5752  
5753  * 
5754  * @constructor
5755  * Create a new Table
5756  * @param {Object} config The config object
5757  */
5758
5759 Roo.bootstrap.Table = function(config){
5760     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5761     
5762   
5763     
5764     // BC...
5765     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5766     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5767     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5768     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5769     
5770     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5771     if (this.sm) {
5772         this.sm.grid = this;
5773         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5774         this.sm = this.selModel;
5775         this.sm.xmodule = this.xmodule || false;
5776     }
5777     
5778     if (this.cm && typeof(this.cm.config) == 'undefined') {
5779         this.colModel = new Roo.grid.ColumnModel(this.cm);
5780         this.cm = this.colModel;
5781         this.cm.xmodule = this.xmodule || false;
5782     }
5783     if (this.store) {
5784         this.store= Roo.factory(this.store, Roo.data);
5785         this.ds = this.store;
5786         this.ds.xmodule = this.xmodule || false;
5787          
5788     }
5789     if (this.footer && this.store) {
5790         this.footer.dataSource = this.ds;
5791         this.footer = Roo.factory(this.footer);
5792     }
5793     
5794     /** @private */
5795     this.addEvents({
5796         /**
5797          * @event cellclick
5798          * Fires when a cell is clicked
5799          * @param {Roo.bootstrap.Table} this
5800          * @param {Roo.Element} el
5801          * @param {Number} rowIndex
5802          * @param {Number} columnIndex
5803          * @param {Roo.EventObject} e
5804          */
5805         "cellclick" : true,
5806         /**
5807          * @event celldblclick
5808          * Fires when a cell is double clicked
5809          * @param {Roo.bootstrap.Table} this
5810          * @param {Roo.Element} el
5811          * @param {Number} rowIndex
5812          * @param {Number} columnIndex
5813          * @param {Roo.EventObject} e
5814          */
5815         "celldblclick" : true,
5816         /**
5817          * @event rowclick
5818          * Fires when a row is clicked
5819          * @param {Roo.bootstrap.Table} this
5820          * @param {Roo.Element} el
5821          * @param {Number} rowIndex
5822          * @param {Roo.EventObject} e
5823          */
5824         "rowclick" : true,
5825         /**
5826          * @event rowdblclick
5827          * Fires when a row is double clicked
5828          * @param {Roo.bootstrap.Table} this
5829          * @param {Roo.Element} el
5830          * @param {Number} rowIndex
5831          * @param {Roo.EventObject} e
5832          */
5833         "rowdblclick" : true,
5834         /**
5835          * @event mouseover
5836          * Fires when a mouseover occur
5837          * @param {Roo.bootstrap.Table} this
5838          * @param {Roo.Element} el
5839          * @param {Number} rowIndex
5840          * @param {Number} columnIndex
5841          * @param {Roo.EventObject} e
5842          */
5843         "mouseover" : true,
5844         /**
5845          * @event mouseout
5846          * Fires when a mouseout occur
5847          * @param {Roo.bootstrap.Table} this
5848          * @param {Roo.Element} el
5849          * @param {Number} rowIndex
5850          * @param {Number} columnIndex
5851          * @param {Roo.EventObject} e
5852          */
5853         "mouseout" : true,
5854         /**
5855          * @event rowclass
5856          * Fires when a row is rendered, so you can change add a style to it.
5857          * @param {Roo.bootstrap.Table} this
5858          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5859          */
5860         'rowclass' : true,
5861           /**
5862          * @event rowsrendered
5863          * Fires when all the  rows have been rendered
5864          * @param {Roo.bootstrap.Table} this
5865          */
5866         'rowsrendered' : true,
5867         /**
5868          * @event contextmenu
5869          * The raw contextmenu event for the entire grid.
5870          * @param {Roo.EventObject} e
5871          */
5872         "contextmenu" : true,
5873         /**
5874          * @event rowcontextmenu
5875          * Fires when a row is right clicked
5876          * @param {Roo.bootstrap.Table} this
5877          * @param {Number} rowIndex
5878          * @param {Roo.EventObject} e
5879          */
5880         "rowcontextmenu" : true,
5881         /**
5882          * @event cellcontextmenu
5883          * Fires when a cell is right clicked
5884          * @param {Roo.bootstrap.Table} this
5885          * @param {Number} rowIndex
5886          * @param {Number} cellIndex
5887          * @param {Roo.EventObject} e
5888          */
5889          "cellcontextmenu" : true,
5890          /**
5891          * @event headercontextmenu
5892          * Fires when a header is right clicked
5893          * @param {Roo.bootstrap.Table} this
5894          * @param {Number} columnIndex
5895          * @param {Roo.EventObject} e
5896          */
5897         "headercontextmenu" : true
5898     });
5899 };
5900
5901 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5902     
5903     cls: false,
5904     align: false,
5905     bgcolor: false,
5906     border: false,
5907     cellpadding: false,
5908     cellspacing: false,
5909     frame: false,
5910     rules: false,
5911     sortable: false,
5912     summary: false,
5913     width: false,
5914     striped : false,
5915     scrollBody : false,
5916     bordered: false,
5917     hover:  false,
5918     condensed : false,
5919     responsive : false,
5920     sm : false,
5921     cm : false,
5922     store : false,
5923     loadMask : false,
5924     footerShow : true,
5925     headerShow : true,
5926   
5927     rowSelection : false,
5928     cellSelection : false,
5929     layout : false,
5930     
5931     // Roo.Element - the tbody
5932     mainBody: false,
5933     // Roo.Element - thead element
5934     mainHead: false,
5935     
5936     container: false, // used by gridpanel...
5937     
5938     getAutoCreate : function()
5939     {
5940         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5941         
5942         cfg = {
5943             tag: 'table',
5944             cls : 'table',
5945             cn : []
5946         };
5947         if (this.scrollBody) {
5948             cfg.cls += ' table-body-fixed';
5949         }    
5950         if (this.striped) {
5951             cfg.cls += ' table-striped';
5952         }
5953         
5954         if (this.hover) {
5955             cfg.cls += ' table-hover';
5956         }
5957         if (this.bordered) {
5958             cfg.cls += ' table-bordered';
5959         }
5960         if (this.condensed) {
5961             cfg.cls += ' table-condensed';
5962         }
5963         if (this.responsive) {
5964             cfg.cls += ' table-responsive';
5965         }
5966         
5967         if (this.cls) {
5968             cfg.cls+=  ' ' +this.cls;
5969         }
5970         
5971         // this lot should be simplifed...
5972         
5973         if (this.align) {
5974             cfg.align=this.align;
5975         }
5976         if (this.bgcolor) {
5977             cfg.bgcolor=this.bgcolor;
5978         }
5979         if (this.border) {
5980             cfg.border=this.border;
5981         }
5982         if (this.cellpadding) {
5983             cfg.cellpadding=this.cellpadding;
5984         }
5985         if (this.cellspacing) {
5986             cfg.cellspacing=this.cellspacing;
5987         }
5988         if (this.frame) {
5989             cfg.frame=this.frame;
5990         }
5991         if (this.rules) {
5992             cfg.rules=this.rules;
5993         }
5994         if (this.sortable) {
5995             cfg.sortable=this.sortable;
5996         }
5997         if (this.summary) {
5998             cfg.summary=this.summary;
5999         }
6000         if (this.width) {
6001             cfg.width=this.width;
6002         }
6003         if (this.layout) {
6004             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6005         }
6006         
6007         if(this.store || this.cm){
6008             if(this.headerShow){
6009                 cfg.cn.push(this.renderHeader());
6010             }
6011             
6012             cfg.cn.push(this.renderBody());
6013             
6014             if(this.footerShow){
6015                 cfg.cn.push(this.renderFooter());
6016             }
6017             // where does this come from?
6018             //cfg.cls+=  ' TableGrid';
6019         }
6020         
6021         return { cn : [ cfg ] };
6022     },
6023     
6024     initEvents : function()
6025     {   
6026         if(!this.store || !this.cm){
6027             return;
6028         }
6029         if (this.selModel) {
6030             this.selModel.initEvents();
6031         }
6032         
6033         
6034         //Roo.log('initEvents with ds!!!!');
6035         
6036         this.mainBody = this.el.select('tbody', true).first();
6037         this.mainHead = this.el.select('thead', true).first();
6038         
6039         
6040         
6041         
6042         var _this = this;
6043         
6044         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6045             e.on('click', _this.sort, _this);
6046         });
6047         
6048         this.mainBody.on("click", this.onClick, this);
6049         this.mainBody.on("dblclick", this.onDblClick, this);
6050         
6051         // why is this done????? = it breaks dialogs??
6052         //this.parent().el.setStyle('position', 'relative');
6053         
6054         
6055         if (this.footer) {
6056             this.footer.parentId = this.id;
6057             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
6058         } 
6059         
6060         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6061         
6062         this.store.on('load', this.onLoad, this);
6063         this.store.on('beforeload', this.onBeforeLoad, this);
6064         this.store.on('update', this.onUpdate, this);
6065         this.store.on('add', this.onAdd, this);
6066         this.store.on("clear", this.clear, this);
6067         
6068         this.el.on("contextmenu", this.onContextMenu, this);
6069         
6070         this.mainBody.on('scroll', this.onBodyScroll, this);
6071         
6072         
6073     },
6074     
6075     onContextMenu : function(e, t)
6076     {
6077         this.processEvent("contextmenu", e);
6078     },
6079     
6080     processEvent : function(name, e)
6081     {
6082         if (name != 'touchstart' ) {
6083             this.fireEvent(name, e);    
6084         }
6085         
6086         var t = e.getTarget();
6087         
6088         var cell = Roo.get(t);
6089         
6090         if(!cell){
6091             return;
6092         }
6093         
6094         if(cell.findParent('tfoot', false, true)){
6095             return;
6096         }
6097         
6098         if(cell.findParent('thead', false, true)){
6099             
6100             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6101                 cell = Roo.get(t).findParent('th', false, true);
6102                 if (!cell) {
6103                     Roo.log("failed to find th in thead?");
6104                     Roo.log(e.getTarget());
6105                     return;
6106                 }
6107             }
6108             
6109             var cellIndex = cell.dom.cellIndex;
6110             
6111             var ename = name == 'touchstart' ? 'click' : name;
6112             this.fireEvent("header" + ename, this, cellIndex, e);
6113             
6114             return;
6115         }
6116         
6117         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6118             cell = Roo.get(t).findParent('td', false, true);
6119             if (!cell) {
6120                 Roo.log("failed to find th in tbody?");
6121                 Roo.log(e.getTarget());
6122                 return;
6123             }
6124         }
6125         
6126         var row = cell.findParent('tr', false, true);
6127         var cellIndex = cell.dom.cellIndex;
6128         var rowIndex = row.dom.rowIndex - 1;
6129         
6130         if(row !== false){
6131             
6132             this.fireEvent("row" + name, this, rowIndex, e);
6133             
6134             if(cell !== false){
6135             
6136                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6137             }
6138         }
6139         
6140     },
6141     
6142     onMouseover : function(e, el)
6143     {
6144         var cell = Roo.get(el);
6145         
6146         if(!cell){
6147             return;
6148         }
6149         
6150         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6151             cell = cell.findParent('td', false, true);
6152         }
6153         
6154         var row = cell.findParent('tr', false, true);
6155         var cellIndex = cell.dom.cellIndex;
6156         var rowIndex = row.dom.rowIndex - 1; // start from 0
6157         
6158         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6159         
6160     },
6161     
6162     onMouseout : function(e, el)
6163     {
6164         var cell = Roo.get(el);
6165         
6166         if(!cell){
6167             return;
6168         }
6169         
6170         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6171             cell = cell.findParent('td', false, true);
6172         }
6173         
6174         var row = cell.findParent('tr', false, true);
6175         var cellIndex = cell.dom.cellIndex;
6176         var rowIndex = row.dom.rowIndex - 1; // start from 0
6177         
6178         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6179         
6180     },
6181     
6182     onClick : function(e, el)
6183     {
6184         var cell = Roo.get(el);
6185         
6186         if(!cell || (!this.cellSelection && !this.rowSelection)){
6187             return;
6188         }
6189         
6190         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6191             cell = cell.findParent('td', false, true);
6192         }
6193         
6194         if(!cell || typeof(cell) == 'undefined'){
6195             return;
6196         }
6197         
6198         var row = cell.findParent('tr', false, true);
6199         
6200         if(!row || typeof(row) == 'undefined'){
6201             return;
6202         }
6203         
6204         var cellIndex = cell.dom.cellIndex;
6205         var rowIndex = this.getRowIndex(row);
6206         
6207         // why??? - should these not be based on SelectionModel?
6208         if(this.cellSelection){
6209             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6210         }
6211         
6212         if(this.rowSelection){
6213             this.fireEvent('rowclick', this, row, rowIndex, e);
6214         }
6215         
6216         
6217     },
6218         
6219     onDblClick : function(e,el)
6220     {
6221         var cell = Roo.get(el);
6222         
6223         if(!cell || (!this.cellSelection && !this.rowSelection)){
6224             return;
6225         }
6226         
6227         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6228             cell = cell.findParent('td', false, true);
6229         }
6230         
6231         if(!cell || typeof(cell) == 'undefined'){
6232             return;
6233         }
6234         
6235         var row = cell.findParent('tr', false, true);
6236         
6237         if(!row || typeof(row) == 'undefined'){
6238             return;
6239         }
6240         
6241         var cellIndex = cell.dom.cellIndex;
6242         var rowIndex = this.getRowIndex(row);
6243         
6244         if(this.cellSelection){
6245             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6246         }
6247         
6248         if(this.rowSelection){
6249             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6250         }
6251     },
6252     
6253     sort : function(e,el)
6254     {
6255         var col = Roo.get(el);
6256         
6257         if(!col.hasClass('sortable')){
6258             return;
6259         }
6260         
6261         var sort = col.attr('sort');
6262         var dir = 'ASC';
6263         
6264         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6265             dir = 'DESC';
6266         }
6267         
6268         this.store.sortInfo = {field : sort, direction : dir};
6269         
6270         if (this.footer) {
6271             Roo.log("calling footer first");
6272             this.footer.onClick('first');
6273         } else {
6274         
6275             this.store.load({ params : { start : 0 } });
6276         }
6277     },
6278     
6279     renderHeader : function()
6280     {
6281         var header = {
6282             tag: 'thead',
6283             cn : []
6284         };
6285         
6286         var cm = this.cm;
6287         this.totalWidth = 0;
6288         
6289         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6290             
6291             var config = cm.config[i];
6292             
6293             var c = {
6294                 tag: 'th',
6295                 style : '',
6296                 html: cm.getColumnHeader(i)
6297             };
6298             
6299             var hh = '';
6300             
6301             if(typeof(config.sortable) != 'undefined' && config.sortable){
6302                 c.cls = 'sortable';
6303                 c.html = '<i class="glyphicon"></i>' + c.html;
6304             }
6305             
6306             if(typeof(config.lgHeader) != 'undefined'){
6307                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6308             }
6309             
6310             if(typeof(config.mdHeader) != 'undefined'){
6311                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6312             }
6313             
6314             if(typeof(config.smHeader) != 'undefined'){
6315                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6316             }
6317             
6318             if(typeof(config.xsHeader) != 'undefined'){
6319                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6320             }
6321             
6322             if(hh.length){
6323                 c.html = hh;
6324             }
6325             
6326             if(typeof(config.tooltip) != 'undefined'){
6327                 c.tooltip = config.tooltip;
6328             }
6329             
6330             if(typeof(config.colspan) != 'undefined'){
6331                 c.colspan = config.colspan;
6332             }
6333             
6334             if(typeof(config.hidden) != 'undefined' && config.hidden){
6335                 c.style += ' display:none;';
6336             }
6337             
6338             if(typeof(config.dataIndex) != 'undefined'){
6339                 c.sort = config.dataIndex;
6340             }
6341             
6342            
6343             
6344             if(typeof(config.align) != 'undefined' && config.align.length){
6345                 c.style += ' text-align:' + config.align + ';';
6346             }
6347             
6348             if(typeof(config.width) != 'undefined'){
6349                 c.style += ' width:' + config.width + 'px;';
6350                 this.totalWidth += config.width;
6351             } else {
6352                 this.totalWidth += 100; // assume minimum of 100 per column?
6353             }
6354             
6355             if(typeof(config.cls) != 'undefined'){
6356                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6357             }
6358             
6359             ['xs','sm','md','lg'].map(function(size){
6360                 
6361                 if(typeof(config[size]) == 'undefined'){
6362                     return;
6363                 }
6364                 
6365                 if (!config[size]) { // 0 = hidden
6366                     c.cls += ' hidden-' + size;
6367                     return;
6368                 }
6369                 
6370                 c.cls += ' col-' + size + '-' + config[size];
6371
6372             });
6373             
6374             header.cn.push(c)
6375         }
6376         
6377         return header;
6378     },
6379     
6380     renderBody : function()
6381     {
6382         var body = {
6383             tag: 'tbody',
6384             cn : [
6385                 {
6386                     tag: 'tr',
6387                     cn : [
6388                         {
6389                             tag : 'td',
6390                             colspan :  this.cm.getColumnCount()
6391                         }
6392                     ]
6393                 }
6394             ]
6395         };
6396         
6397         return body;
6398     },
6399     
6400     renderFooter : function()
6401     {
6402         var footer = {
6403             tag: 'tfoot',
6404             cn : [
6405                 {
6406                     tag: 'tr',
6407                     cn : [
6408                         {
6409                             tag : 'td',
6410                             colspan :  this.cm.getColumnCount()
6411                         }
6412                     ]
6413                 }
6414             ]
6415         };
6416         
6417         return footer;
6418     },
6419     
6420     
6421     
6422     onLoad : function()
6423     {
6424 //        Roo.log('ds onload');
6425         this.clear();
6426         
6427         var _this = this;
6428         var cm = this.cm;
6429         var ds = this.store;
6430         
6431         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6432             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6433             if (_this.store.sortInfo) {
6434                     
6435                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6436                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6437                 }
6438                 
6439                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6440                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6441                 }
6442             }
6443         });
6444         
6445         var tbody =  this.mainBody;
6446               
6447         if(ds.getCount() > 0){
6448             ds.data.each(function(d,rowIndex){
6449                 var row =  this.renderRow(cm, ds, rowIndex);
6450                 
6451                 tbody.createChild(row);
6452                 
6453                 var _this = this;
6454                 
6455                 if(row.cellObjects.length){
6456                     Roo.each(row.cellObjects, function(r){
6457                         _this.renderCellObject(r);
6458                     })
6459                 }
6460                 
6461             }, this);
6462         }
6463         
6464         Roo.each(this.el.select('tbody td', true).elements, function(e){
6465             e.on('mouseover', _this.onMouseover, _this);
6466         });
6467         
6468         Roo.each(this.el.select('tbody td', true).elements, function(e){
6469             e.on('mouseout', _this.onMouseout, _this);
6470         });
6471         this.fireEvent('rowsrendered', this);
6472         //if(this.loadMask){
6473         //    this.maskEl.hide();
6474         //}
6475         
6476         this.autoSize();
6477     },
6478     
6479     
6480     onUpdate : function(ds,record)
6481     {
6482         this.refreshRow(record);
6483         this.autoSize();
6484     },
6485     
6486     onRemove : function(ds, record, index, isUpdate){
6487         if(isUpdate !== true){
6488             this.fireEvent("beforerowremoved", this, index, record);
6489         }
6490         var bt = this.mainBody.dom;
6491         
6492         var rows = this.el.select('tbody > tr', true).elements;
6493         
6494         if(typeof(rows[index]) != 'undefined'){
6495             bt.removeChild(rows[index].dom);
6496         }
6497         
6498 //        if(bt.rows[index]){
6499 //            bt.removeChild(bt.rows[index]);
6500 //        }
6501         
6502         if(isUpdate !== true){
6503             //this.stripeRows(index);
6504             //this.syncRowHeights(index, index);
6505             //this.layout();
6506             this.fireEvent("rowremoved", this, index, record);
6507         }
6508     },
6509     
6510     onAdd : function(ds, records, rowIndex)
6511     {
6512         //Roo.log('on Add called');
6513         // - note this does not handle multiple adding very well..
6514         var bt = this.mainBody.dom;
6515         for (var i =0 ; i < records.length;i++) {
6516             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6517             //Roo.log(records[i]);
6518             //Roo.log(this.store.getAt(rowIndex+i));
6519             this.insertRow(this.store, rowIndex + i, false);
6520             return;
6521         }
6522         
6523     },
6524     
6525     
6526     refreshRow : function(record){
6527         var ds = this.store, index;
6528         if(typeof record == 'number'){
6529             index = record;
6530             record = ds.getAt(index);
6531         }else{
6532             index = ds.indexOf(record);
6533         }
6534         this.insertRow(ds, index, true);
6535         this.autoSize();
6536         this.onRemove(ds, record, index+1, true);
6537         this.autoSize();
6538         //this.syncRowHeights(index, index);
6539         //this.layout();
6540         this.fireEvent("rowupdated", this, index, record);
6541     },
6542     
6543     insertRow : function(dm, rowIndex, isUpdate){
6544         
6545         if(!isUpdate){
6546             this.fireEvent("beforerowsinserted", this, rowIndex);
6547         }
6548             //var s = this.getScrollState();
6549         var row = this.renderRow(this.cm, this.store, rowIndex);
6550         // insert before rowIndex..
6551         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6552         
6553         var _this = this;
6554                 
6555         if(row.cellObjects.length){
6556             Roo.each(row.cellObjects, function(r){
6557                 _this.renderCellObject(r);
6558             })
6559         }
6560             
6561         if(!isUpdate){
6562             this.fireEvent("rowsinserted", this, rowIndex);
6563             //this.syncRowHeights(firstRow, lastRow);
6564             //this.stripeRows(firstRow);
6565             //this.layout();
6566         }
6567         
6568     },
6569     
6570     
6571     getRowDom : function(rowIndex)
6572     {
6573         var rows = this.el.select('tbody > tr', true).elements;
6574         
6575         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6576         
6577     },
6578     // returns the object tree for a tr..
6579   
6580     
6581     renderRow : function(cm, ds, rowIndex) 
6582     {
6583         
6584         var d = ds.getAt(rowIndex);
6585         
6586         var row = {
6587             tag : 'tr',
6588             cn : []
6589         };
6590             
6591         var cellObjects = [];
6592         
6593         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6594             var config = cm.config[i];
6595             
6596             var renderer = cm.getRenderer(i);
6597             var value = '';
6598             var id = false;
6599             
6600             if(typeof(renderer) !== 'undefined'){
6601                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6602             }
6603             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6604             // and are rendered into the cells after the row is rendered - using the id for the element.
6605             
6606             if(typeof(value) === 'object'){
6607                 id = Roo.id();
6608                 cellObjects.push({
6609                     container : id,
6610                     cfg : value 
6611                 })
6612             }
6613             
6614             var rowcfg = {
6615                 record: d,
6616                 rowIndex : rowIndex,
6617                 colIndex : i,
6618                 rowClass : ''
6619             };
6620
6621             this.fireEvent('rowclass', this, rowcfg);
6622             
6623             var td = {
6624                 tag: 'td',
6625                 cls : rowcfg.rowClass,
6626                 style: '',
6627                 html: (typeof(value) === 'object') ? '' : value
6628             };
6629             
6630             if (id) {
6631                 td.id = id;
6632             }
6633             
6634             if(typeof(config.colspan) != 'undefined'){
6635                 td.colspan = config.colspan;
6636             }
6637             
6638             if(typeof(config.hidden) != 'undefined' && config.hidden){
6639                 td.style += ' display:none;';
6640             }
6641             
6642             if(typeof(config.align) != 'undefined' && config.align.length){
6643                 td.style += ' text-align:' + config.align + ';';
6644             }
6645             
6646             if(typeof(config.width) != 'undefined'){
6647                 td.style += ' width:' +  config.width + 'px;';
6648             }
6649             
6650             if(typeof(config.cursor) != 'undefined'){
6651                 td.style += ' cursor:' +  config.cursor + ';';
6652             }
6653             
6654             if(typeof(config.cls) != 'undefined'){
6655                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6656             }
6657             
6658             ['xs','sm','md','lg'].map(function(size){
6659                 
6660                 if(typeof(config[size]) == 'undefined'){
6661                     return;
6662                 }
6663                 
6664                 if (!config[size]) { // 0 = hidden
6665                     td.cls += ' hidden-' + size;
6666                     return;
6667                 }
6668                 
6669                 td.cls += ' col-' + size + '-' + config[size];
6670
6671             });
6672              
6673             row.cn.push(td);
6674            
6675         }
6676         
6677         row.cellObjects = cellObjects;
6678         
6679         return row;
6680           
6681     },
6682     
6683     
6684     
6685     onBeforeLoad : function()
6686     {
6687         //Roo.log('ds onBeforeLoad');
6688         
6689         //this.clear();
6690         
6691         //if(this.loadMask){
6692         //    this.maskEl.show();
6693         //}
6694     },
6695      /**
6696      * Remove all rows
6697      */
6698     clear : function()
6699     {
6700         this.el.select('tbody', true).first().dom.innerHTML = '';
6701     },
6702     /**
6703      * Show or hide a row.
6704      * @param {Number} rowIndex to show or hide
6705      * @param {Boolean} state hide
6706      */
6707     setRowVisibility : function(rowIndex, state)
6708     {
6709         var bt = this.mainBody.dom;
6710         
6711         var rows = this.el.select('tbody > tr', true).elements;
6712         
6713         if(typeof(rows[rowIndex]) == 'undefined'){
6714             return;
6715         }
6716         rows[rowIndex].dom.style.display = state ? '' : 'none';
6717     },
6718     
6719     
6720     getSelectionModel : function(){
6721         if(!this.selModel){
6722             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6723         }
6724         return this.selModel;
6725     },
6726     /*
6727      * Render the Roo.bootstrap object from renderder
6728      */
6729     renderCellObject : function(r)
6730     {
6731         var _this = this;
6732         
6733         var t = r.cfg.render(r.container);
6734         
6735         if(r.cfg.cn){
6736             Roo.each(r.cfg.cn, function(c){
6737                 var child = {
6738                     container: t.getChildContainer(),
6739                     cfg: c
6740                 };
6741                 _this.renderCellObject(child);
6742             })
6743         }
6744     },
6745     
6746     getRowIndex : function(row)
6747     {
6748         var rowIndex = -1;
6749         
6750         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6751             if(el != row){
6752                 return;
6753             }
6754             
6755             rowIndex = index;
6756         });
6757         
6758         return rowIndex;
6759     },
6760      /**
6761      * Returns the grid's underlying element = used by panel.Grid
6762      * @return {Element} The element
6763      */
6764     getGridEl : function(){
6765         return this.el;
6766     },
6767      /**
6768      * Forces a resize - used by panel.Grid
6769      * @return {Element} The element
6770      */
6771     autoSize : function()
6772     {
6773         //var ctr = Roo.get(this.container.dom.parentElement);
6774         var ctr = Roo.get(this.el.dom);
6775         
6776         var thd = this.getGridEl().select('thead',true).first();
6777         var tbd = this.getGridEl().select('tbody', true).first();
6778         var tfd = this.getGridEl().select('tfoot', true).first();
6779         
6780         var cw = ctr.getWidth();
6781         
6782         if (tbd) {
6783             
6784             tbd.setSize(ctr.getWidth(),
6785                         ctr.getHeight() - (thd.getHeight() + (tfd ? tfd.getHeight() : 0))
6786             );
6787             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6788             cw -= barsize;
6789         }
6790         cw = Math.max(cw, this.totalWidth);
6791         this.getGridEl().select('tr',true).setWidth(cw);
6792         // resize 'expandable coloumn?
6793         
6794         return; // we doe not have a view in this design..
6795         
6796     },
6797     onBodyScroll: function()
6798     {
6799         
6800         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6801         this.mainHead.setStyle({
6802                     'position' : 'relative',
6803                     'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6804         });
6805         
6806         
6807     }
6808 });
6809
6810  
6811
6812  /*
6813  * - LGPL
6814  *
6815  * table cell
6816  * 
6817  */
6818
6819 /**
6820  * @class Roo.bootstrap.TableCell
6821  * @extends Roo.bootstrap.Component
6822  * Bootstrap TableCell class
6823  * @cfg {String} html cell contain text
6824  * @cfg {String} cls cell class
6825  * @cfg {String} tag cell tag (td|th) default td
6826  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6827  * @cfg {String} align Aligns the content in a cell
6828  * @cfg {String} axis Categorizes cells
6829  * @cfg {String} bgcolor Specifies the background color of a cell
6830  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6831  * @cfg {Number} colspan Specifies the number of columns a cell should span
6832  * @cfg {String} headers Specifies one or more header cells a cell is related to
6833  * @cfg {Number} height Sets the height of a cell
6834  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6835  * @cfg {Number} rowspan Sets the number of rows a cell should span
6836  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6837  * @cfg {String} valign Vertical aligns the content in a cell
6838  * @cfg {Number} width Specifies the width of a cell
6839  * 
6840  * @constructor
6841  * Create a new TableCell
6842  * @param {Object} config The config object
6843  */
6844
6845 Roo.bootstrap.TableCell = function(config){
6846     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6847 };
6848
6849 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6850     
6851     html: false,
6852     cls: false,
6853     tag: false,
6854     abbr: false,
6855     align: false,
6856     axis: false,
6857     bgcolor: false,
6858     charoff: false,
6859     colspan: false,
6860     headers: false,
6861     height: false,
6862     nowrap: false,
6863     rowspan: false,
6864     scope: false,
6865     valign: false,
6866     width: false,
6867     
6868     
6869     getAutoCreate : function(){
6870         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6871         
6872         cfg = {
6873             tag: 'td'
6874         };
6875         
6876         if(this.tag){
6877             cfg.tag = this.tag;
6878         }
6879         
6880         if (this.html) {
6881             cfg.html=this.html
6882         }
6883         if (this.cls) {
6884             cfg.cls=this.cls
6885         }
6886         if (this.abbr) {
6887             cfg.abbr=this.abbr
6888         }
6889         if (this.align) {
6890             cfg.align=this.align
6891         }
6892         if (this.axis) {
6893             cfg.axis=this.axis
6894         }
6895         if (this.bgcolor) {
6896             cfg.bgcolor=this.bgcolor
6897         }
6898         if (this.charoff) {
6899             cfg.charoff=this.charoff
6900         }
6901         if (this.colspan) {
6902             cfg.colspan=this.colspan
6903         }
6904         if (this.headers) {
6905             cfg.headers=this.headers
6906         }
6907         if (this.height) {
6908             cfg.height=this.height
6909         }
6910         if (this.nowrap) {
6911             cfg.nowrap=this.nowrap
6912         }
6913         if (this.rowspan) {
6914             cfg.rowspan=this.rowspan
6915         }
6916         if (this.scope) {
6917             cfg.scope=this.scope
6918         }
6919         if (this.valign) {
6920             cfg.valign=this.valign
6921         }
6922         if (this.width) {
6923             cfg.width=this.width
6924         }
6925         
6926         
6927         return cfg;
6928     }
6929    
6930 });
6931
6932  
6933
6934  /*
6935  * - LGPL
6936  *
6937  * table row
6938  * 
6939  */
6940
6941 /**
6942  * @class Roo.bootstrap.TableRow
6943  * @extends Roo.bootstrap.Component
6944  * Bootstrap TableRow class
6945  * @cfg {String} cls row class
6946  * @cfg {String} align Aligns the content in a table row
6947  * @cfg {String} bgcolor Specifies a background color for a table row
6948  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6949  * @cfg {String} valign Vertical aligns the content in a table row
6950  * 
6951  * @constructor
6952  * Create a new TableRow
6953  * @param {Object} config The config object
6954  */
6955
6956 Roo.bootstrap.TableRow = function(config){
6957     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6958 };
6959
6960 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6961     
6962     cls: false,
6963     align: false,
6964     bgcolor: false,
6965     charoff: false,
6966     valign: false,
6967     
6968     getAutoCreate : function(){
6969         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6970         
6971         cfg = {
6972             tag: 'tr'
6973         };
6974             
6975         if(this.cls){
6976             cfg.cls = this.cls;
6977         }
6978         if(this.align){
6979             cfg.align = this.align;
6980         }
6981         if(this.bgcolor){
6982             cfg.bgcolor = this.bgcolor;
6983         }
6984         if(this.charoff){
6985             cfg.charoff = this.charoff;
6986         }
6987         if(this.valign){
6988             cfg.valign = this.valign;
6989         }
6990         
6991         return cfg;
6992     }
6993    
6994 });
6995
6996  
6997
6998  /*
6999  * - LGPL
7000  *
7001  * table body
7002  * 
7003  */
7004
7005 /**
7006  * @class Roo.bootstrap.TableBody
7007  * @extends Roo.bootstrap.Component
7008  * Bootstrap TableBody class
7009  * @cfg {String} cls element class
7010  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7011  * @cfg {String} align Aligns the content inside the element
7012  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7013  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7014  * 
7015  * @constructor
7016  * Create a new TableBody
7017  * @param {Object} config The config object
7018  */
7019
7020 Roo.bootstrap.TableBody = function(config){
7021     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7022 };
7023
7024 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7025     
7026     cls: false,
7027     tag: false,
7028     align: false,
7029     charoff: false,
7030     valign: false,
7031     
7032     getAutoCreate : function(){
7033         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7034         
7035         cfg = {
7036             tag: 'tbody'
7037         };
7038             
7039         if (this.cls) {
7040             cfg.cls=this.cls
7041         }
7042         if(this.tag){
7043             cfg.tag = this.tag;
7044         }
7045         
7046         if(this.align){
7047             cfg.align = this.align;
7048         }
7049         if(this.charoff){
7050             cfg.charoff = this.charoff;
7051         }
7052         if(this.valign){
7053             cfg.valign = this.valign;
7054         }
7055         
7056         return cfg;
7057     }
7058     
7059     
7060 //    initEvents : function()
7061 //    {
7062 //        
7063 //        if(!this.store){
7064 //            return;
7065 //        }
7066 //        
7067 //        this.store = Roo.factory(this.store, Roo.data);
7068 //        this.store.on('load', this.onLoad, this);
7069 //        
7070 //        this.store.load();
7071 //        
7072 //    },
7073 //    
7074 //    onLoad: function () 
7075 //    {   
7076 //        this.fireEvent('load', this);
7077 //    }
7078 //    
7079 //   
7080 });
7081
7082  
7083
7084  /*
7085  * Based on:
7086  * Ext JS Library 1.1.1
7087  * Copyright(c) 2006-2007, Ext JS, LLC.
7088  *
7089  * Originally Released Under LGPL - original licence link has changed is not relivant.
7090  *
7091  * Fork - LGPL
7092  * <script type="text/javascript">
7093  */
7094
7095 // as we use this in bootstrap.
7096 Roo.namespace('Roo.form');
7097  /**
7098  * @class Roo.form.Action
7099  * Internal Class used to handle form actions
7100  * @constructor
7101  * @param {Roo.form.BasicForm} el The form element or its id
7102  * @param {Object} config Configuration options
7103  */
7104
7105  
7106  
7107 // define the action interface
7108 Roo.form.Action = function(form, options){
7109     this.form = form;
7110     this.options = options || {};
7111 };
7112 /**
7113  * Client Validation Failed
7114  * @const 
7115  */
7116 Roo.form.Action.CLIENT_INVALID = 'client';
7117 /**
7118  * Server Validation Failed
7119  * @const 
7120  */
7121 Roo.form.Action.SERVER_INVALID = 'server';
7122  /**
7123  * Connect to Server Failed
7124  * @const 
7125  */
7126 Roo.form.Action.CONNECT_FAILURE = 'connect';
7127 /**
7128  * Reading Data from Server Failed
7129  * @const 
7130  */
7131 Roo.form.Action.LOAD_FAILURE = 'load';
7132
7133 Roo.form.Action.prototype = {
7134     type : 'default',
7135     failureType : undefined,
7136     response : undefined,
7137     result : undefined,
7138
7139     // interface method
7140     run : function(options){
7141
7142     },
7143
7144     // interface method
7145     success : function(response){
7146
7147     },
7148
7149     // interface method
7150     handleResponse : function(response){
7151
7152     },
7153
7154     // default connection failure
7155     failure : function(response){
7156         
7157         this.response = response;
7158         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7159         this.form.afterAction(this, false);
7160     },
7161
7162     processResponse : function(response){
7163         this.response = response;
7164         if(!response.responseText){
7165             return true;
7166         }
7167         this.result = this.handleResponse(response);
7168         return this.result;
7169     },
7170
7171     // utility functions used internally
7172     getUrl : function(appendParams){
7173         var url = this.options.url || this.form.url || this.form.el.dom.action;
7174         if(appendParams){
7175             var p = this.getParams();
7176             if(p){
7177                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7178             }
7179         }
7180         return url;
7181     },
7182
7183     getMethod : function(){
7184         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7185     },
7186
7187     getParams : function(){
7188         var bp = this.form.baseParams;
7189         var p = this.options.params;
7190         if(p){
7191             if(typeof p == "object"){
7192                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7193             }else if(typeof p == 'string' && bp){
7194                 p += '&' + Roo.urlEncode(bp);
7195             }
7196         }else if(bp){
7197             p = Roo.urlEncode(bp);
7198         }
7199         return p;
7200     },
7201
7202     createCallback : function(){
7203         return {
7204             success: this.success,
7205             failure: this.failure,
7206             scope: this,
7207             timeout: (this.form.timeout*1000),
7208             upload: this.form.fileUpload ? this.success : undefined
7209         };
7210     }
7211 };
7212
7213 Roo.form.Action.Submit = function(form, options){
7214     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7215 };
7216
7217 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7218     type : 'submit',
7219
7220     haveProgress : false,
7221     uploadComplete : false,
7222     
7223     // uploadProgress indicator.
7224     uploadProgress : function()
7225     {
7226         if (!this.form.progressUrl) {
7227             return;
7228         }
7229         
7230         if (!this.haveProgress) {
7231             Roo.MessageBox.progress("Uploading", "Uploading");
7232         }
7233         if (this.uploadComplete) {
7234            Roo.MessageBox.hide();
7235            return;
7236         }
7237         
7238         this.haveProgress = true;
7239    
7240         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7241         
7242         var c = new Roo.data.Connection();
7243         c.request({
7244             url : this.form.progressUrl,
7245             params: {
7246                 id : uid
7247             },
7248             method: 'GET',
7249             success : function(req){
7250                //console.log(data);
7251                 var rdata = false;
7252                 var edata;
7253                 try  {
7254                    rdata = Roo.decode(req.responseText)
7255                 } catch (e) {
7256                     Roo.log("Invalid data from server..");
7257                     Roo.log(edata);
7258                     return;
7259                 }
7260                 if (!rdata || !rdata.success) {
7261                     Roo.log(rdata);
7262                     Roo.MessageBox.alert(Roo.encode(rdata));
7263                     return;
7264                 }
7265                 var data = rdata.data;
7266                 
7267                 if (this.uploadComplete) {
7268                    Roo.MessageBox.hide();
7269                    return;
7270                 }
7271                    
7272                 if (data){
7273                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7274                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7275                     );
7276                 }
7277                 this.uploadProgress.defer(2000,this);
7278             },
7279        
7280             failure: function(data) {
7281                 Roo.log('progress url failed ');
7282                 Roo.log(data);
7283             },
7284             scope : this
7285         });
7286            
7287     },
7288     
7289     
7290     run : function()
7291     {
7292         // run get Values on the form, so it syncs any secondary forms.
7293         this.form.getValues();
7294         
7295         var o = this.options;
7296         var method = this.getMethod();
7297         var isPost = method == 'POST';
7298         if(o.clientValidation === false || this.form.isValid()){
7299             
7300             if (this.form.progressUrl) {
7301                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7302                     (new Date() * 1) + '' + Math.random());
7303                     
7304             } 
7305             
7306             
7307             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7308                 form:this.form.el.dom,
7309                 url:this.getUrl(!isPost),
7310                 method: method,
7311                 params:isPost ? this.getParams() : null,
7312                 isUpload: this.form.fileUpload
7313             }));
7314             
7315             this.uploadProgress();
7316
7317         }else if (o.clientValidation !== false){ // client validation failed
7318             this.failureType = Roo.form.Action.CLIENT_INVALID;
7319             this.form.afterAction(this, false);
7320         }
7321     },
7322
7323     success : function(response)
7324     {
7325         this.uploadComplete= true;
7326         if (this.haveProgress) {
7327             Roo.MessageBox.hide();
7328         }
7329         
7330         
7331         var result = this.processResponse(response);
7332         if(result === true || result.success){
7333             this.form.afterAction(this, true);
7334             return;
7335         }
7336         if(result.errors){
7337             this.form.markInvalid(result.errors);
7338             this.failureType = Roo.form.Action.SERVER_INVALID;
7339         }
7340         this.form.afterAction(this, false);
7341     },
7342     failure : function(response)
7343     {
7344         this.uploadComplete= true;
7345         if (this.haveProgress) {
7346             Roo.MessageBox.hide();
7347         }
7348         
7349         this.response = response;
7350         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7351         this.form.afterAction(this, false);
7352     },
7353     
7354     handleResponse : function(response){
7355         if(this.form.errorReader){
7356             var rs = this.form.errorReader.read(response);
7357             var errors = [];
7358             if(rs.records){
7359                 for(var i = 0, len = rs.records.length; i < len; i++) {
7360                     var r = rs.records[i];
7361                     errors[i] = r.data;
7362                 }
7363             }
7364             if(errors.length < 1){
7365                 errors = null;
7366             }
7367             return {
7368                 success : rs.success,
7369                 errors : errors
7370             };
7371         }
7372         var ret = false;
7373         try {
7374             ret = Roo.decode(response.responseText);
7375         } catch (e) {
7376             ret = {
7377                 success: false,
7378                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7379                 errors : []
7380             };
7381         }
7382         return ret;
7383         
7384     }
7385 });
7386
7387
7388 Roo.form.Action.Load = function(form, options){
7389     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7390     this.reader = this.form.reader;
7391 };
7392
7393 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7394     type : 'load',
7395
7396     run : function(){
7397         
7398         Roo.Ajax.request(Roo.apply(
7399                 this.createCallback(), {
7400                     method:this.getMethod(),
7401                     url:this.getUrl(false),
7402                     params:this.getParams()
7403         }));
7404     },
7405
7406     success : function(response){
7407         
7408         var result = this.processResponse(response);
7409         if(result === true || !result.success || !result.data){
7410             this.failureType = Roo.form.Action.LOAD_FAILURE;
7411             this.form.afterAction(this, false);
7412             return;
7413         }
7414         this.form.clearInvalid();
7415         this.form.setValues(result.data);
7416         this.form.afterAction(this, true);
7417     },
7418
7419     handleResponse : function(response){
7420         if(this.form.reader){
7421             var rs = this.form.reader.read(response);
7422             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7423             return {
7424                 success : rs.success,
7425                 data : data
7426             };
7427         }
7428         return Roo.decode(response.responseText);
7429     }
7430 });
7431
7432 Roo.form.Action.ACTION_TYPES = {
7433     'load' : Roo.form.Action.Load,
7434     'submit' : Roo.form.Action.Submit
7435 };/*
7436  * - LGPL
7437  *
7438  * form
7439  *
7440  */
7441
7442 /**
7443  * @class Roo.bootstrap.Form
7444  * @extends Roo.bootstrap.Component
7445  * Bootstrap Form class
7446  * @cfg {String} method  GET | POST (default POST)
7447  * @cfg {String} labelAlign top | left (default top)
7448  * @cfg {String} align left  | right - for navbars
7449  * @cfg {Boolean} loadMask load mask when submit (default true)
7450
7451  *
7452  * @constructor
7453  * Create a new Form
7454  * @param {Object} config The config object
7455  */
7456
7457
7458 Roo.bootstrap.Form = function(config){
7459     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7460     
7461     Roo.bootstrap.Form.popover.apply();
7462     
7463     this.addEvents({
7464         /**
7465          * @event clientvalidation
7466          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7467          * @param {Form} this
7468          * @param {Boolean} valid true if the form has passed client-side validation
7469          */
7470         clientvalidation: true,
7471         /**
7472          * @event beforeaction
7473          * Fires before any action is performed. Return false to cancel the action.
7474          * @param {Form} this
7475          * @param {Action} action The action to be performed
7476          */
7477         beforeaction: true,
7478         /**
7479          * @event actionfailed
7480          * Fires when an action fails.
7481          * @param {Form} this
7482          * @param {Action} action The action that failed
7483          */
7484         actionfailed : true,
7485         /**
7486          * @event actioncomplete
7487          * Fires when an action is completed.
7488          * @param {Form} this
7489          * @param {Action} action The action that completed
7490          */
7491         actioncomplete : true
7492     });
7493
7494 };
7495
7496 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7497
7498      /**
7499      * @cfg {String} method
7500      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7501      */
7502     method : 'POST',
7503     /**
7504      * @cfg {String} url
7505      * The URL to use for form actions if one isn't supplied in the action options.
7506      */
7507     /**
7508      * @cfg {Boolean} fileUpload
7509      * Set to true if this form is a file upload.
7510      */
7511
7512     /**
7513      * @cfg {Object} baseParams
7514      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7515      */
7516
7517     /**
7518      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7519      */
7520     timeout: 30,
7521     /**
7522      * @cfg {Sting} align (left|right) for navbar forms
7523      */
7524     align : 'left',
7525
7526     // private
7527     activeAction : null,
7528
7529     /**
7530      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7531      * element by passing it or its id or mask the form itself by passing in true.
7532      * @type Mixed
7533      */
7534     waitMsgTarget : false,
7535
7536     loadMask : true,
7537     
7538     /**
7539      * @cfg {Boolean} errorMask (true|false) default false
7540      */
7541     errorMask : false,
7542
7543     getAutoCreate : function(){
7544
7545         var cfg = {
7546             tag: 'form',
7547             method : this.method || 'POST',
7548             id : this.id || Roo.id(),
7549             cls : ''
7550         };
7551         if (this.parent().xtype.match(/^Nav/)) {
7552             cfg.cls = 'navbar-form navbar-' + this.align;
7553
7554         }
7555
7556         if (this.labelAlign == 'left' ) {
7557             cfg.cls += ' form-horizontal';
7558         }
7559
7560
7561         return cfg;
7562     },
7563     initEvents : function()
7564     {
7565         this.el.on('submit', this.onSubmit, this);
7566         // this was added as random key presses on the form where triggering form submit.
7567         this.el.on('keypress', function(e) {
7568             if (e.getCharCode() != 13) {
7569                 return true;
7570             }
7571             // we might need to allow it for textareas.. and some other items.
7572             // check e.getTarget().
7573
7574             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7575                 return true;
7576             }
7577
7578             Roo.log("keypress blocked");
7579
7580             e.preventDefault();
7581             return false;
7582         });
7583         
7584     },
7585     // private
7586     onSubmit : function(e){
7587         e.stopEvent();
7588     },
7589
7590      /**
7591      * Returns true if client-side validation on the form is successful.
7592      * @return Boolean
7593      */
7594     isValid : function(){
7595         var items = this.getItems();
7596         var valid = true;
7597         var target = false;
7598         
7599         items.each(function(f){
7600             
7601             if(f.validate()){
7602                 return;
7603             }
7604             valid = false;
7605
7606             if(!target && f.el.isVisible(true)){
7607                 target = f;
7608             }
7609            
7610         });
7611         
7612         if(this.errorMask && !valid){
7613             Roo.bootstrap.Form.popover.mask(this, target);
7614         }
7615         
7616         return valid;
7617     },
7618     
7619     /**
7620      * Returns true if any fields in this form have changed since their original load.
7621      * @return Boolean
7622      */
7623     isDirty : function(){
7624         var dirty = false;
7625         var items = this.getItems();
7626         items.each(function(f){
7627            if(f.isDirty()){
7628                dirty = true;
7629                return false;
7630            }
7631            return true;
7632         });
7633         return dirty;
7634     },
7635      /**
7636      * Performs a predefined action (submit or load) or custom actions you define on this form.
7637      * @param {String} actionName The name of the action type
7638      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7639      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7640      * accept other config options):
7641      * <pre>
7642 Property          Type             Description
7643 ----------------  ---------------  ----------------------------------------------------------------------------------
7644 url               String           The url for the action (defaults to the form's url)
7645 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7646 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7647 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7648                                    validate the form on the client (defaults to false)
7649      * </pre>
7650      * @return {BasicForm} this
7651      */
7652     doAction : function(action, options){
7653         if(typeof action == 'string'){
7654             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7655         }
7656         if(this.fireEvent('beforeaction', this, action) !== false){
7657             this.beforeAction(action);
7658             action.run.defer(100, action);
7659         }
7660         return this;
7661     },
7662
7663     // private
7664     beforeAction : function(action){
7665         var o = action.options;
7666
7667         if(this.loadMask){
7668             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7669         }
7670         // not really supported yet.. ??
7671
7672         //if(this.waitMsgTarget === true){
7673         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7674         //}else if(this.waitMsgTarget){
7675         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7676         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7677         //}else {
7678         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7679        // }
7680
7681     },
7682
7683     // private
7684     afterAction : function(action, success){
7685         this.activeAction = null;
7686         var o = action.options;
7687
7688         //if(this.waitMsgTarget === true){
7689             this.el.unmask();
7690         //}else if(this.waitMsgTarget){
7691         //    this.waitMsgTarget.unmask();
7692         //}else{
7693         //    Roo.MessageBox.updateProgress(1);
7694         //    Roo.MessageBox.hide();
7695        // }
7696         //
7697         if(success){
7698             if(o.reset){
7699                 this.reset();
7700             }
7701             Roo.callback(o.success, o.scope, [this, action]);
7702             this.fireEvent('actioncomplete', this, action);
7703
7704         }else{
7705
7706             // failure condition..
7707             // we have a scenario where updates need confirming.
7708             // eg. if a locking scenario exists..
7709             // we look for { errors : { needs_confirm : true }} in the response.
7710             if (
7711                 (typeof(action.result) != 'undefined')  &&
7712                 (typeof(action.result.errors) != 'undefined')  &&
7713                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7714            ){
7715                 var _t = this;
7716                 Roo.log("not supported yet");
7717                  /*
7718
7719                 Roo.MessageBox.confirm(
7720                     "Change requires confirmation",
7721                     action.result.errorMsg,
7722                     function(r) {
7723                         if (r != 'yes') {
7724                             return;
7725                         }
7726                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7727                     }
7728
7729                 );
7730                 */
7731
7732
7733                 return;
7734             }
7735
7736             Roo.callback(o.failure, o.scope, [this, action]);
7737             // show an error message if no failed handler is set..
7738             if (!this.hasListener('actionfailed')) {
7739                 Roo.log("need to add dialog support");
7740                 /*
7741                 Roo.MessageBox.alert("Error",
7742                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7743                         action.result.errorMsg :
7744                         "Saving Failed, please check your entries or try again"
7745                 );
7746                 */
7747             }
7748
7749             this.fireEvent('actionfailed', this, action);
7750         }
7751
7752     },
7753     /**
7754      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7755      * @param {String} id The value to search for
7756      * @return Field
7757      */
7758     findField : function(id){
7759         var items = this.getItems();
7760         var field = items.get(id);
7761         if(!field){
7762              items.each(function(f){
7763                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7764                     field = f;
7765                     return false;
7766                 }
7767                 return true;
7768             });
7769         }
7770         return field || null;
7771     },
7772      /**
7773      * Mark fields in this form invalid in bulk.
7774      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7775      * @return {BasicForm} this
7776      */
7777     markInvalid : function(errors){
7778         if(errors instanceof Array){
7779             for(var i = 0, len = errors.length; i < len; i++){
7780                 var fieldError = errors[i];
7781                 var f = this.findField(fieldError.id);
7782                 if(f){
7783                     f.markInvalid(fieldError.msg);
7784                 }
7785             }
7786         }else{
7787             var field, id;
7788             for(id in errors){
7789                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7790                     field.markInvalid(errors[id]);
7791                 }
7792             }
7793         }
7794         //Roo.each(this.childForms || [], function (f) {
7795         //    f.markInvalid(errors);
7796         //});
7797
7798         return this;
7799     },
7800
7801     /**
7802      * Set values for fields in this form in bulk.
7803      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7804      * @return {BasicForm} this
7805      */
7806     setValues : function(values){
7807         if(values instanceof Array){ // array of objects
7808             for(var i = 0, len = values.length; i < len; i++){
7809                 var v = values[i];
7810                 var f = this.findField(v.id);
7811                 if(f){
7812                     f.setValue(v.value);
7813                     if(this.trackResetOnLoad){
7814                         f.originalValue = f.getValue();
7815                     }
7816                 }
7817             }
7818         }else{ // object hash
7819             var field, id;
7820             for(id in values){
7821                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7822
7823                     if (field.setFromData &&
7824                         field.valueField &&
7825                         field.displayField &&
7826                         // combos' with local stores can
7827                         // be queried via setValue()
7828                         // to set their value..
7829                         (field.store && !field.store.isLocal)
7830                         ) {
7831                         // it's a combo
7832                         var sd = { };
7833                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7834                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7835                         field.setFromData(sd);
7836
7837                     } else {
7838                         field.setValue(values[id]);
7839                     }
7840
7841
7842                     if(this.trackResetOnLoad){
7843                         field.originalValue = field.getValue();
7844                     }
7845                 }
7846             }
7847         }
7848
7849         //Roo.each(this.childForms || [], function (f) {
7850         //    f.setValues(values);
7851         //});
7852
7853         return this;
7854     },
7855
7856     /**
7857      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7858      * they are returned as an array.
7859      * @param {Boolean} asString
7860      * @return {Object}
7861      */
7862     getValues : function(asString){
7863         //if (this.childForms) {
7864             // copy values from the child forms
7865         //    Roo.each(this.childForms, function (f) {
7866         //        this.setValues(f.getValues());
7867         //    }, this);
7868         //}
7869
7870
7871
7872         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7873         if(asString === true){
7874             return fs;
7875         }
7876         return Roo.urlDecode(fs);
7877     },
7878
7879     /**
7880      * Returns the fields in this form as an object with key/value pairs.
7881      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7882      * @return {Object}
7883      */
7884     getFieldValues : function(with_hidden)
7885     {
7886         var items = this.getItems();
7887         var ret = {};
7888         items.each(function(f){
7889             if (!f.getName()) {
7890                 return;
7891             }
7892             var v = f.getValue();
7893             if (f.inputType =='radio') {
7894                 if (typeof(ret[f.getName()]) == 'undefined') {
7895                     ret[f.getName()] = ''; // empty..
7896                 }
7897
7898                 if (!f.el.dom.checked) {
7899                     return;
7900
7901                 }
7902                 v = f.el.dom.value;
7903
7904             }
7905
7906             // not sure if this supported any more..
7907             if ((typeof(v) == 'object') && f.getRawValue) {
7908                 v = f.getRawValue() ; // dates..
7909             }
7910             // combo boxes where name != hiddenName...
7911             if (f.name !== false && f.name != '' && f.name != f.getName()) {
7912                 ret[f.name] = f.getRawValue();
7913             }
7914             ret[f.getName()] = v;
7915         });
7916
7917         return ret;
7918     },
7919
7920     /**
7921      * Clears all invalid messages in this form.
7922      * @return {BasicForm} this
7923      */
7924     clearInvalid : function(){
7925         var items = this.getItems();
7926
7927         items.each(function(f){
7928            f.clearInvalid();
7929         });
7930
7931
7932
7933         return this;
7934     },
7935
7936     /**
7937      * Resets this form.
7938      * @return {BasicForm} this
7939      */
7940     reset : function(){
7941         var items = this.getItems();
7942         items.each(function(f){
7943             f.reset();
7944         });
7945
7946         Roo.each(this.childForms || [], function (f) {
7947             f.reset();
7948         });
7949
7950
7951         return this;
7952     },
7953     getItems : function()
7954     {
7955         var r=new Roo.util.MixedCollection(false, function(o){
7956             return o.id || (o.id = Roo.id());
7957         });
7958         var iter = function(el) {
7959             if (el.inputEl) {
7960                 r.add(el);
7961             }
7962             if (!el.items) {
7963                 return;
7964             }
7965             Roo.each(el.items,function(e) {
7966                 iter(e);
7967             });
7968
7969
7970         };
7971
7972         iter(this);
7973         return r;
7974
7975
7976
7977
7978     }
7979
7980 });
7981
7982 Roo.apply(Roo.bootstrap.Form, {
7983     
7984     popover : {
7985         
7986         padding : 5,
7987         
7988         isApplied : false,
7989         
7990         isMasked : false,
7991         
7992         form : false,
7993         
7994         target : false,
7995         
7996         toolTip : false,
7997         
7998         intervalID : false,
7999         
8000         maskEl : false,
8001         
8002         apply : function()
8003         {
8004             if(this.isApplied){
8005                 return;
8006             }
8007             
8008             this.maskEl = {
8009                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8010                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8011                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8012                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8013             };
8014             
8015             this.maskEl.top.enableDisplayMode("block");
8016             this.maskEl.left.enableDisplayMode("block");
8017             this.maskEl.bottom.enableDisplayMode("block");
8018             this.maskEl.right.enableDisplayMode("block");
8019             
8020             this.toolTip = new Roo.bootstrap.Tooltip({
8021                 cls : 'roo-form-error-popover',
8022                 alignment : {
8023                     'left' : ['r-l', [-2,0], 'right'],
8024                     'right' : ['l-r', [2,0], 'left'],
8025                     'bottom' : ['tl-bl', [0,2], 'top'],
8026                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8027                 }
8028             });
8029             
8030             this.toolTip.render(Roo.get(document.body));
8031
8032             this.toolTip.el.enableDisplayMode("block");
8033             
8034             Roo.get(document.body).on('click', function(){
8035                 this.unmask();
8036             }, this);
8037             
8038             this.isApplied = true
8039         },
8040         
8041         mask : function(form, target)
8042         {
8043             this.form = form;
8044             
8045             this.target = target;
8046             
8047             if(!this.form.errorMask || !target.el){
8048                 return;
8049             }
8050             
8051             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8052             
8053             var scrolled = scrollable.getScroll();
8054             
8055             var ot = this.target.el.calcOffsetsTo(scrollable);
8056             
8057             scrollTo = ot[1] - 100;
8058             
8059             scrollable.scrollTo('top', scrollTo);
8060             
8061             var box = this.target.el.getBox();
8062
8063             var zIndex = Roo.bootstrap.Modal.zIndex++;
8064
8065             this.maskEl.top.setStyle('position', 'fixed');
8066             this.maskEl.top.setStyle('z-index', zIndex);
8067             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8068             this.maskEl.top.setXY([0, 0]);
8069             this.maskEl.top.show();
8070
8071             this.maskEl.left.setStyle('position', 'fixed');
8072             this.maskEl.left.setStyle('z-index', zIndex);
8073             this.maskEl.left.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8074             this.maskEl.left.setXY([box.right + this.padding, box.y - this.padding]);
8075             this.maskEl.left.show();
8076
8077             this.maskEl.bottom.setStyle('position', 'fixed');
8078             this.maskEl.bottom.setStyle('z-index', zIndex);
8079             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8080             this.maskEl.bottom.setXY([0, box.bottom + this.padding]);
8081             this.maskEl.bottom.show();
8082
8083             this.maskEl.right.setStyle('position', 'fixed');
8084             this.maskEl.right.setStyle('z-index', zIndex);
8085             this.maskEl.right.setSize(box.x - this.padding, box.height + this.padding * 2);
8086             this.maskEl.right.setXY([0, box.y - this.padding]);
8087             this.maskEl.right.show();
8088
8089
8090             this.toolTip.bindEl = this.target.el;
8091
8092             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8093
8094             var tip = this.target.blankText;
8095
8096             if(this.target.getValue() !== '' && this.target.regexText.length){
8097                 tip = this.target.regexText;
8098             }
8099
8100             this.toolTip.show(tip);
8101
8102             this.intervalID = window.setInterval(function() {
8103                 Roo.bootstrap.Form.popover.unmask();
8104             }, 10000);
8105
8106             window.onwheel = function(){ return false;};
8107             
8108             (function(){ this.isMasked = true; }).defer(500, this);
8109                 
8110             
8111             
8112         },
8113         
8114         unmask : function()
8115         {
8116             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8117                 return;
8118             }
8119             
8120             this.maskEl.top.setStyle('position', 'absolute');
8121             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8122             this.maskEl.top.hide();
8123
8124             this.maskEl.left.setStyle('position', 'absolute');
8125             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8126             this.maskEl.left.hide();
8127
8128             this.maskEl.bottom.setStyle('position', 'absolute');
8129             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8130             this.maskEl.bottom.hide();
8131
8132             this.maskEl.right.setStyle('position', 'absolute');
8133             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8134             this.maskEl.right.hide();
8135             
8136             this.toolTip.hide();
8137             
8138             this.toolTip.el.hide();
8139             
8140             window.onwheel = function(){ return true;};
8141             
8142             if(this.intervalID){
8143                 window.clearInterval(this.intervalID);
8144                 this.intervalID = false;
8145             }
8146             
8147             this.isMasked = false;
8148             
8149         }
8150         
8151     }
8152     
8153 });
8154
8155 /*
8156  * Based on:
8157  * Ext JS Library 1.1.1
8158  * Copyright(c) 2006-2007, Ext JS, LLC.
8159  *
8160  * Originally Released Under LGPL - original licence link has changed is not relivant.
8161  *
8162  * Fork - LGPL
8163  * <script type="text/javascript">
8164  */
8165 /**
8166  * @class Roo.form.VTypes
8167  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8168  * @singleton
8169  */
8170 Roo.form.VTypes = function(){
8171     // closure these in so they are only created once.
8172     var alpha = /^[a-zA-Z_]+$/;
8173     var alphanum = /^[a-zA-Z0-9_]+$/;
8174     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8175     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8176
8177     // All these messages and functions are configurable
8178     return {
8179         /**
8180          * The function used to validate email addresses
8181          * @param {String} value The email address
8182          */
8183         'email' : function(v){
8184             return email.test(v);
8185         },
8186         /**
8187          * The error text to display when the email validation function returns false
8188          * @type String
8189          */
8190         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8191         /**
8192          * The keystroke filter mask to be applied on email input
8193          * @type RegExp
8194          */
8195         'emailMask' : /[a-z0-9_\.\-@]/i,
8196
8197         /**
8198          * The function used to validate URLs
8199          * @param {String} value The URL
8200          */
8201         'url' : function(v){
8202             return url.test(v);
8203         },
8204         /**
8205          * The error text to display when the url validation function returns false
8206          * @type String
8207          */
8208         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8209         
8210         /**
8211          * The function used to validate alpha values
8212          * @param {String} value The value
8213          */
8214         'alpha' : function(v){
8215             return alpha.test(v);
8216         },
8217         /**
8218          * The error text to display when the alpha validation function returns false
8219          * @type String
8220          */
8221         'alphaText' : 'This field should only contain letters and _',
8222         /**
8223          * The keystroke filter mask to be applied on alpha input
8224          * @type RegExp
8225          */
8226         'alphaMask' : /[a-z_]/i,
8227
8228         /**
8229          * The function used to validate alphanumeric values
8230          * @param {String} value The value
8231          */
8232         'alphanum' : function(v){
8233             return alphanum.test(v);
8234         },
8235         /**
8236          * The error text to display when the alphanumeric validation function returns false
8237          * @type String
8238          */
8239         'alphanumText' : 'This field should only contain letters, numbers and _',
8240         /**
8241          * The keystroke filter mask to be applied on alphanumeric input
8242          * @type RegExp
8243          */
8244         'alphanumMask' : /[a-z0-9_]/i
8245     };
8246 }();/*
8247  * - LGPL
8248  *
8249  * Input
8250  * 
8251  */
8252
8253 /**
8254  * @class Roo.bootstrap.Input
8255  * @extends Roo.bootstrap.Component
8256  * Bootstrap Input class
8257  * @cfg {Boolean} disabled is it disabled
8258  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8259  * @cfg {String} name name of the input
8260  * @cfg {string} fieldLabel - the label associated
8261  * @cfg {string} placeholder - placeholder to put in text.
8262  * @cfg {string}  before - input group add on before
8263  * @cfg {string} after - input group add on after
8264  * @cfg {string} size - (lg|sm) or leave empty..
8265  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8266  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8267  * @cfg {Number} md colspan out of 12 for computer-sized screens
8268  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8269  * @cfg {string} value default value of the input
8270  * @cfg {Number} labelWidth set the width of label 
8271  * @cfg {Number} labellg set the width of label (1-12)
8272  * @cfg {Number} labelmd set the width of label (1-12)
8273  * @cfg {Number} labelsm set the width of label (1-12)
8274  * @cfg {Number} labelxs set the width of label (1-12)
8275  * @cfg {String} labelAlign (top|left)
8276  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8277  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8278  * @cfg {String} indicatorpos (left|right) default left
8279
8280  * @cfg {String} align (left|center|right) Default left
8281  * @cfg {Boolean} forceFeedback (true|false) Default false
8282  * 
8283  * 
8284  * 
8285  * 
8286  * @constructor
8287  * Create a new Input
8288  * @param {Object} config The config object
8289  */
8290
8291 Roo.bootstrap.Input = function(config){
8292     
8293     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8294     
8295     this.addEvents({
8296         /**
8297          * @event focus
8298          * Fires when this field receives input focus.
8299          * @param {Roo.form.Field} this
8300          */
8301         focus : true,
8302         /**
8303          * @event blur
8304          * Fires when this field loses input focus.
8305          * @param {Roo.form.Field} this
8306          */
8307         blur : true,
8308         /**
8309          * @event specialkey
8310          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8311          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8312          * @param {Roo.form.Field} this
8313          * @param {Roo.EventObject} e The event object
8314          */
8315         specialkey : true,
8316         /**
8317          * @event change
8318          * Fires just before the field blurs if the field value has changed.
8319          * @param {Roo.form.Field} this
8320          * @param {Mixed} newValue The new value
8321          * @param {Mixed} oldValue The original value
8322          */
8323         change : true,
8324         /**
8325          * @event invalid
8326          * Fires after the field has been marked as invalid.
8327          * @param {Roo.form.Field} this
8328          * @param {String} msg The validation message
8329          */
8330         invalid : true,
8331         /**
8332          * @event valid
8333          * Fires after the field has been validated with no errors.
8334          * @param {Roo.form.Field} this
8335          */
8336         valid : true,
8337          /**
8338          * @event keyup
8339          * Fires after the key up
8340          * @param {Roo.form.Field} this
8341          * @param {Roo.EventObject}  e The event Object
8342          */
8343         keyup : true
8344     });
8345 };
8346
8347 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8348      /**
8349      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8350       automatic validation (defaults to "keyup").
8351      */
8352     validationEvent : "keyup",
8353      /**
8354      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8355      */
8356     validateOnBlur : true,
8357     /**
8358      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8359      */
8360     validationDelay : 250,
8361      /**
8362      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8363      */
8364     focusClass : "x-form-focus",  // not needed???
8365     
8366        
8367     /**
8368      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8369      */
8370     invalidClass : "has-warning",
8371     
8372     /**
8373      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8374      */
8375     validClass : "has-success",
8376     
8377     /**
8378      * @cfg {Boolean} hasFeedback (true|false) default true
8379      */
8380     hasFeedback : true,
8381     
8382     /**
8383      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8384      */
8385     invalidFeedbackClass : "glyphicon-warning-sign",
8386     
8387     /**
8388      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8389      */
8390     validFeedbackClass : "glyphicon-ok",
8391     
8392     /**
8393      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8394      */
8395     selectOnFocus : false,
8396     
8397      /**
8398      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8399      */
8400     maskRe : null,
8401        /**
8402      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8403      */
8404     vtype : null,
8405     
8406       /**
8407      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8408      */
8409     disableKeyFilter : false,
8410     
8411        /**
8412      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8413      */
8414     disabled : false,
8415      /**
8416      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8417      */
8418     allowBlank : true,
8419     /**
8420      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8421      */
8422     blankText : "Please complete this mandatory field",
8423     
8424      /**
8425      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8426      */
8427     minLength : 0,
8428     /**
8429      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8430      */
8431     maxLength : Number.MAX_VALUE,
8432     /**
8433      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8434      */
8435     minLengthText : "The minimum length for this field is {0}",
8436     /**
8437      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8438      */
8439     maxLengthText : "The maximum length for this field is {0}",
8440   
8441     
8442     /**
8443      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8444      * If available, this function will be called only after the basic validators all return true, and will be passed the
8445      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8446      */
8447     validator : null,
8448     /**
8449      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8450      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8451      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8452      */
8453     regex : null,
8454     /**
8455      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8456      */
8457     regexText : "",
8458     
8459     autocomplete: false,
8460     
8461     
8462     fieldLabel : '',
8463     inputType : 'text',
8464     
8465     name : false,
8466     placeholder: false,
8467     before : false,
8468     after : false,
8469     size : false,
8470     hasFocus : false,
8471     preventMark: false,
8472     isFormField : true,
8473     value : '',
8474     labelWidth : 2,
8475     labelAlign : false,
8476     readOnly : false,
8477     align : false,
8478     formatedValue : false,
8479     forceFeedback : false,
8480     
8481     indicatorpos : 'left',
8482     
8483     labellg : 0,
8484     labelmd : 0,
8485     labelsm : 0,
8486     labelxs : 0,
8487     
8488     parentLabelAlign : function()
8489     {
8490         var parent = this;
8491         while (parent.parent()) {
8492             parent = parent.parent();
8493             if (typeof(parent.labelAlign) !='undefined') {
8494                 return parent.labelAlign;
8495             }
8496         }
8497         return 'left';
8498         
8499     },
8500     
8501     getAutoCreate : function()
8502     {
8503         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8504         
8505         var id = Roo.id();
8506         
8507         var cfg = {};
8508         
8509         if(this.inputType != 'hidden'){
8510             cfg.cls = 'form-group' //input-group
8511         }
8512         
8513         var input =  {
8514             tag: 'input',
8515             id : id,
8516             type : this.inputType,
8517             value : this.value,
8518             cls : 'form-control',
8519             placeholder : this.placeholder || '',
8520             autocomplete : this.autocomplete || 'new-password'
8521         };
8522         
8523         if(this.align){
8524             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8525         }
8526         
8527         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8528             input.maxLength = this.maxLength;
8529         }
8530         
8531         if (this.disabled) {
8532             input.disabled=true;
8533         }
8534         
8535         if (this.readOnly) {
8536             input.readonly=true;
8537         }
8538         
8539         if (this.name) {
8540             input.name = this.name;
8541         }
8542         
8543         if (this.size) {
8544             input.cls += ' input-' + this.size;
8545         }
8546         
8547         var settings=this;
8548         ['xs','sm','md','lg'].map(function(size){
8549             if (settings[size]) {
8550                 cfg.cls += ' col-' + size + '-' + settings[size];
8551             }
8552         });
8553         
8554         var inputblock = input;
8555         
8556         var feedback = {
8557             tag: 'span',
8558             cls: 'glyphicon form-control-feedback'
8559         };
8560             
8561         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8562             
8563             inputblock = {
8564                 cls : 'has-feedback',
8565                 cn :  [
8566                     input,
8567                     feedback
8568                 ] 
8569             };  
8570         }
8571         
8572         if (this.before || this.after) {
8573             
8574             inputblock = {
8575                 cls : 'input-group',
8576                 cn :  [] 
8577             };
8578             
8579             if (this.before && typeof(this.before) == 'string') {
8580                 
8581                 inputblock.cn.push({
8582                     tag :'span',
8583                     cls : 'roo-input-before input-group-addon',
8584                     html : this.before
8585                 });
8586             }
8587             if (this.before && typeof(this.before) == 'object') {
8588                 this.before = Roo.factory(this.before);
8589                 
8590                 inputblock.cn.push({
8591                     tag :'span',
8592                     cls : 'roo-input-before input-group-' +
8593                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8594                 });
8595             }
8596             
8597             inputblock.cn.push(input);
8598             
8599             if (this.after && typeof(this.after) == 'string') {
8600                 inputblock.cn.push({
8601                     tag :'span',
8602                     cls : 'roo-input-after input-group-addon',
8603                     html : this.after
8604                 });
8605             }
8606             if (this.after && typeof(this.after) == 'object') {
8607                 this.after = Roo.factory(this.after);
8608                 
8609                 inputblock.cn.push({
8610                     tag :'span',
8611                     cls : 'roo-input-after input-group-' +
8612                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8613                 });
8614             }
8615             
8616             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8617                 inputblock.cls += ' has-feedback';
8618                 inputblock.cn.push(feedback);
8619             }
8620         };
8621         
8622         if (align ==='left' && this.fieldLabel.length) {
8623             
8624             cfg.cls += ' roo-form-group-label-left';
8625             
8626             cfg.cn = [
8627                 {
8628                     tag : 'i',
8629                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8630                     tooltip : 'This field is required'
8631                 },
8632                 {
8633                     tag: 'label',
8634                     'for' :  id,
8635                     cls : 'control-label',
8636                     html : this.fieldLabel
8637
8638                 },
8639                 {
8640                     cls : "", 
8641                     cn: [
8642                         inputblock
8643                     ]
8644                 }
8645             ];
8646             
8647             var labelCfg = cfg.cn[1];
8648             var contentCfg = cfg.cn[2];
8649             
8650             if(this.indicatorpos == 'right'){
8651                 cfg.cn = [
8652                     {
8653                         tag: 'label',
8654                         'for' :  id,
8655                         cls : 'control-label',
8656                         html : this.fieldLabel
8657
8658                     },
8659                     {
8660                         tag : 'i',
8661                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8662                         tooltip : 'This field is required'
8663                     },
8664                     {
8665                         cls : "",
8666                         cn: [
8667                             inputblock
8668                         ]
8669                     }
8670
8671                 ];
8672                 
8673                 labelCfg = cfg.cn[0];
8674                 contentCfg = cfg.cn[2];
8675             
8676             }
8677             
8678             if(this.labelWidth > 12){
8679                 labelCfg.style = "width: " + this.labelWidth + 'px';
8680             }
8681             
8682             if(this.labelWidth < 13 && this.labelmd == 0){
8683                 this.labelmd = this.labelWidth;
8684             }
8685             
8686             if(this.labellg > 0){
8687                 labelCfg.cls += ' col-lg-' + this.labellg;
8688                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8689             }
8690             
8691             if(this.labelmd > 0){
8692                 labelCfg.cls += ' col-md-' + this.labelmd;
8693                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8694             }
8695             
8696             if(this.labelsm > 0){
8697                 labelCfg.cls += ' col-sm-' + this.labelsm;
8698                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8699             }
8700             
8701             if(this.labelxs > 0){
8702                 labelCfg.cls += ' col-xs-' + this.labelxs;
8703                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8704             }
8705             
8706             
8707         } else if ( this.fieldLabel.length) {
8708                 
8709             cfg.cn = [
8710                 {
8711                     tag : 'i',
8712                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8713                     tooltip : 'This field is required'
8714                 },
8715                 {
8716                     tag: 'label',
8717                    //cls : 'input-group-addon',
8718                     html : this.fieldLabel
8719
8720                 },
8721
8722                inputblock
8723
8724            ];
8725            
8726            if(this.indicatorpos == 'right'){
8727                 
8728                 cfg.cn = [
8729                     {
8730                         tag: 'label',
8731                        //cls : 'input-group-addon',
8732                         html : this.fieldLabel
8733
8734                     },
8735                     {
8736                         tag : 'i',
8737                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8738                         tooltip : 'This field is required'
8739                     },
8740
8741                    inputblock
8742
8743                ];
8744
8745             }
8746
8747         } else {
8748             
8749             cfg.cn = [
8750
8751                     inputblock
8752
8753             ];
8754                 
8755                 
8756         };
8757         
8758         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8759            cfg.cls += ' navbar-form';
8760         }
8761         
8762         if (this.parentType === 'NavGroup') {
8763            cfg.cls += ' navbar-form';
8764            cfg.tag = 'li';
8765         }
8766         
8767         return cfg;
8768         
8769     },
8770     /**
8771      * return the real input element.
8772      */
8773     inputEl: function ()
8774     {
8775         return this.el.select('input.form-control',true).first();
8776     },
8777     
8778     tooltipEl : function()
8779     {
8780         return this.inputEl();
8781     },
8782     
8783     indicatorEl : function()
8784     {
8785         var indicator = this.el.select('i.roo-required-indicator',true).first();
8786         
8787         if(!indicator){
8788             return false;
8789         }
8790         
8791         return indicator;
8792         
8793     },
8794     
8795     setDisabled : function(v)
8796     {
8797         var i  = this.inputEl().dom;
8798         if (!v) {
8799             i.removeAttribute('disabled');
8800             return;
8801             
8802         }
8803         i.setAttribute('disabled','true');
8804     },
8805     initEvents : function()
8806     {
8807           
8808         this.inputEl().on("keydown" , this.fireKey,  this);
8809         this.inputEl().on("focus", this.onFocus,  this);
8810         this.inputEl().on("blur", this.onBlur,  this);
8811         
8812         this.inputEl().relayEvent('keyup', this);
8813         
8814         this.indicator = this.indicatorEl();
8815         
8816         if(this.indicator){
8817             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8818             this.indicator.hide();
8819         }
8820  
8821         // reference to original value for reset
8822         this.originalValue = this.getValue();
8823         //Roo.form.TextField.superclass.initEvents.call(this);
8824         if(this.validationEvent == 'keyup'){
8825             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8826             this.inputEl().on('keyup', this.filterValidation, this);
8827         }
8828         else if(this.validationEvent !== false){
8829             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8830         }
8831         
8832         if(this.selectOnFocus){
8833             this.on("focus", this.preFocus, this);
8834             
8835         }
8836         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8837             this.inputEl().on("keypress", this.filterKeys, this);
8838         } else {
8839             this.inputEl().relayEvent('keypress', this);
8840         }
8841        /* if(this.grow){
8842             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8843             this.el.on("click", this.autoSize,  this);
8844         }
8845         */
8846         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8847             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8848         }
8849         
8850         if (typeof(this.before) == 'object') {
8851             this.before.render(this.el.select('.roo-input-before',true).first());
8852         }
8853         if (typeof(this.after) == 'object') {
8854             this.after.render(this.el.select('.roo-input-after',true).first());
8855         }
8856         
8857         
8858     },
8859     filterValidation : function(e){
8860         if(!e.isNavKeyPress()){
8861             this.validationTask.delay(this.validationDelay);
8862         }
8863     },
8864      /**
8865      * Validates the field value
8866      * @return {Boolean} True if the value is valid, else false
8867      */
8868     validate : function(){
8869         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8870         if(this.disabled || this.validateValue(this.getRawValue())){
8871             this.markValid();
8872             return true;
8873         }
8874         
8875         this.markInvalid();
8876         return false;
8877     },
8878     
8879     
8880     /**
8881      * Validates a value according to the field's validation rules and marks the field as invalid
8882      * if the validation fails
8883      * @param {Mixed} value The value to validate
8884      * @return {Boolean} True if the value is valid, else false
8885      */
8886     validateValue : function(value){
8887         if(value.length < 1)  { // if it's blank
8888             if(this.allowBlank){
8889                 return true;
8890             }
8891             return false;
8892         }
8893         
8894         if(value.length < this.minLength){
8895             return false;
8896         }
8897         if(value.length > this.maxLength){
8898             return false;
8899         }
8900         if(this.vtype){
8901             var vt = Roo.form.VTypes;
8902             if(!vt[this.vtype](value, this)){
8903                 return false;
8904             }
8905         }
8906         if(typeof this.validator == "function"){
8907             var msg = this.validator(value);
8908             if(msg !== true){
8909                 return false;
8910             }
8911         }
8912         
8913         if(this.regex && !this.regex.test(value)){
8914             return false;
8915         }
8916         
8917         return true;
8918     },
8919
8920     
8921     
8922      // private
8923     fireKey : function(e){
8924         //Roo.log('field ' + e.getKey());
8925         if(e.isNavKeyPress()){
8926             this.fireEvent("specialkey", this, e);
8927         }
8928     },
8929     focus : function (selectText){
8930         if(this.rendered){
8931             this.inputEl().focus();
8932             if(selectText === true){
8933                 this.inputEl().dom.select();
8934             }
8935         }
8936         return this;
8937     } ,
8938     
8939     onFocus : function(){
8940         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8941            // this.el.addClass(this.focusClass);
8942         }
8943         if(!this.hasFocus){
8944             this.hasFocus = true;
8945             this.startValue = this.getValue();
8946             this.fireEvent("focus", this);
8947         }
8948     },
8949     
8950     beforeBlur : Roo.emptyFn,
8951
8952     
8953     // private
8954     onBlur : function(){
8955         this.beforeBlur();
8956         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8957             //this.el.removeClass(this.focusClass);
8958         }
8959         this.hasFocus = false;
8960         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8961             this.validate();
8962         }
8963         var v = this.getValue();
8964         if(String(v) !== String(this.startValue)){
8965             this.fireEvent('change', this, v, this.startValue);
8966         }
8967         this.fireEvent("blur", this);
8968     },
8969     
8970     /**
8971      * Resets the current field value to the originally loaded value and clears any validation messages
8972      */
8973     reset : function(){
8974         this.setValue(this.originalValue);
8975         this.validate();
8976     },
8977      /**
8978      * Returns the name of the field
8979      * @return {Mixed} name The name field
8980      */
8981     getName: function(){
8982         return this.name;
8983     },
8984      /**
8985      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8986      * @return {Mixed} value The field value
8987      */
8988     getValue : function(){
8989         
8990         var v = this.inputEl().getValue();
8991         
8992         return v;
8993     },
8994     /**
8995      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8996      * @return {Mixed} value The field value
8997      */
8998     getRawValue : function(){
8999         var v = this.inputEl().getValue();
9000         
9001         return v;
9002     },
9003     
9004     /**
9005      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9006      * @param {Mixed} value The value to set
9007      */
9008     setRawValue : function(v){
9009         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9010     },
9011     
9012     selectText : function(start, end){
9013         var v = this.getRawValue();
9014         if(v.length > 0){
9015             start = start === undefined ? 0 : start;
9016             end = end === undefined ? v.length : end;
9017             var d = this.inputEl().dom;
9018             if(d.setSelectionRange){
9019                 d.setSelectionRange(start, end);
9020             }else if(d.createTextRange){
9021                 var range = d.createTextRange();
9022                 range.moveStart("character", start);
9023                 range.moveEnd("character", v.length-end);
9024                 range.select();
9025             }
9026         }
9027     },
9028     
9029     /**
9030      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9031      * @param {Mixed} value The value to set
9032      */
9033     setValue : function(v){
9034         this.value = v;
9035         if(this.rendered){
9036             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9037             this.validate();
9038         }
9039     },
9040     
9041     /*
9042     processValue : function(value){
9043         if(this.stripCharsRe){
9044             var newValue = value.replace(this.stripCharsRe, '');
9045             if(newValue !== value){
9046                 this.setRawValue(newValue);
9047                 return newValue;
9048             }
9049         }
9050         return value;
9051     },
9052   */
9053     preFocus : function(){
9054         
9055         if(this.selectOnFocus){
9056             this.inputEl().dom.select();
9057         }
9058     },
9059     filterKeys : function(e){
9060         var k = e.getKey();
9061         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9062             return;
9063         }
9064         var c = e.getCharCode(), cc = String.fromCharCode(c);
9065         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9066             return;
9067         }
9068         if(!this.maskRe.test(cc)){
9069             e.stopEvent();
9070         }
9071     },
9072      /**
9073      * Clear any invalid styles/messages for this field
9074      */
9075     clearInvalid : function(){
9076         
9077         if(!this.el || this.preventMark){ // not rendered
9078             return;
9079         }
9080         
9081         if(this.indicator){
9082             this.indicator.hide();
9083         }
9084         
9085         this.el.removeClass(this.invalidClass);
9086         
9087         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9088             
9089             var feedback = this.el.select('.form-control-feedback', true).first();
9090             
9091             if(feedback){
9092                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9093             }
9094             
9095         }
9096         
9097         this.fireEvent('valid', this);
9098     },
9099     
9100      /**
9101      * Mark this field as valid
9102      */
9103     markValid : function()
9104     {
9105         if(!this.el  || this.preventMark){ // not rendered...
9106             return;
9107         }
9108         
9109         this.el.removeClass([this.invalidClass, this.validClass]);
9110         
9111         var feedback = this.el.select('.form-control-feedback', true).first();
9112             
9113         if(feedback){
9114             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9115         }
9116
9117         if(this.disabled){
9118             return;
9119         }
9120         
9121         if(this.allowBlank && !this.getRawValue().length){
9122             return;
9123         }
9124         
9125         if(this.indicator){
9126             this.indicator.hide();
9127         }
9128         
9129         this.el.addClass(this.validClass);
9130         
9131         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9132             
9133             var feedback = this.el.select('.form-control-feedback', true).first();
9134             
9135             if(feedback){
9136                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9137                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9138             }
9139             
9140         }
9141         
9142         this.fireEvent('valid', this);
9143     },
9144     
9145      /**
9146      * Mark this field as invalid
9147      * @param {String} msg The validation message
9148      */
9149     markInvalid : function(msg)
9150     {
9151         if(!this.el  || this.preventMark){ // not rendered
9152             return;
9153         }
9154         
9155         this.el.removeClass([this.invalidClass, this.validClass]);
9156         
9157         var feedback = this.el.select('.form-control-feedback', true).first();
9158             
9159         if(feedback){
9160             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9161         }
9162
9163         if(this.disabled){
9164             return;
9165         }
9166         
9167         if(this.allowBlank && !this.getRawValue().length){
9168             return;
9169         }
9170         
9171         if(this.indicator){
9172             this.indicator.show();
9173         }
9174         
9175         this.el.addClass(this.invalidClass);
9176         
9177         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9178             
9179             var feedback = this.el.select('.form-control-feedback', true).first();
9180             
9181             if(feedback){
9182                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9183                 
9184                 if(this.getValue().length || this.forceFeedback){
9185                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9186                 }
9187                 
9188             }
9189             
9190         }
9191         
9192         this.fireEvent('invalid', this, msg);
9193     },
9194     // private
9195     SafariOnKeyDown : function(event)
9196     {
9197         // this is a workaround for a password hang bug on chrome/ webkit.
9198         if (this.inputEl().dom.type != 'password') {
9199             return;
9200         }
9201         
9202         var isSelectAll = false;
9203         
9204         if(this.inputEl().dom.selectionEnd > 0){
9205             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9206         }
9207         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9208             event.preventDefault();
9209             this.setValue('');
9210             return;
9211         }
9212         
9213         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9214             
9215             event.preventDefault();
9216             // this is very hacky as keydown always get's upper case.
9217             //
9218             var cc = String.fromCharCode(event.getCharCode());
9219             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9220             
9221         }
9222     },
9223     adjustWidth : function(tag, w){
9224         tag = tag.toLowerCase();
9225         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9226             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9227                 if(tag == 'input'){
9228                     return w + 2;
9229                 }
9230                 if(tag == 'textarea'){
9231                     return w-2;
9232                 }
9233             }else if(Roo.isOpera){
9234                 if(tag == 'input'){
9235                     return w + 2;
9236                 }
9237                 if(tag == 'textarea'){
9238                     return w-2;
9239                 }
9240             }
9241         }
9242         return w;
9243     }
9244     
9245 });
9246
9247  
9248 /*
9249  * - LGPL
9250  *
9251  * Input
9252  * 
9253  */
9254
9255 /**
9256  * @class Roo.bootstrap.TextArea
9257  * @extends Roo.bootstrap.Input
9258  * Bootstrap TextArea class
9259  * @cfg {Number} cols Specifies the visible width of a text area
9260  * @cfg {Number} rows Specifies the visible number of lines in a text area
9261  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9262  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9263  * @cfg {string} html text
9264  * 
9265  * @constructor
9266  * Create a new TextArea
9267  * @param {Object} config The config object
9268  */
9269
9270 Roo.bootstrap.TextArea = function(config){
9271     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9272    
9273 };
9274
9275 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9276      
9277     cols : false,
9278     rows : 5,
9279     readOnly : false,
9280     warp : 'soft',
9281     resize : false,
9282     value: false,
9283     html: false,
9284     
9285     getAutoCreate : function(){
9286         
9287         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9288         
9289         var id = Roo.id();
9290         
9291         var cfg = {};
9292         
9293         var input =  {
9294             tag: 'textarea',
9295             id : id,
9296             warp : this.warp,
9297             rows : this.rows,
9298             value : this.value || '',
9299             html: this.html || '',
9300             cls : 'form-control',
9301             placeholder : this.placeholder || '' 
9302             
9303         };
9304         
9305         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9306             input.maxLength = this.maxLength;
9307         }
9308         
9309         if(this.resize){
9310             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9311         }
9312         
9313         if(this.cols){
9314             input.cols = this.cols;
9315         }
9316         
9317         if (this.readOnly) {
9318             input.readonly = true;
9319         }
9320         
9321         if (this.name) {
9322             input.name = this.name;
9323         }
9324         
9325         if (this.size) {
9326             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9327         }
9328         
9329         var settings=this;
9330         ['xs','sm','md','lg'].map(function(size){
9331             if (settings[size]) {
9332                 cfg.cls += ' col-' + size + '-' + settings[size];
9333             }
9334         });
9335         
9336         var inputblock = input;
9337         
9338         if(this.hasFeedback && !this.allowBlank){
9339             
9340             var feedback = {
9341                 tag: 'span',
9342                 cls: 'glyphicon form-control-feedback'
9343             };
9344
9345             inputblock = {
9346                 cls : 'has-feedback',
9347                 cn :  [
9348                     input,
9349                     feedback
9350                 ] 
9351             };  
9352         }
9353         
9354         
9355         if (this.before || this.after) {
9356             
9357             inputblock = {
9358                 cls : 'input-group',
9359                 cn :  [] 
9360             };
9361             if (this.before) {
9362                 inputblock.cn.push({
9363                     tag :'span',
9364                     cls : 'input-group-addon',
9365                     html : this.before
9366                 });
9367             }
9368             
9369             inputblock.cn.push(input);
9370             
9371             if(this.hasFeedback && !this.allowBlank){
9372                 inputblock.cls += ' has-feedback';
9373                 inputblock.cn.push(feedback);
9374             }
9375             
9376             if (this.after) {
9377                 inputblock.cn.push({
9378                     tag :'span',
9379                     cls : 'input-group-addon',
9380                     html : this.after
9381                 });
9382             }
9383             
9384         }
9385         
9386         if (align ==='left' && this.fieldLabel.length) {
9387             cfg.cn = [
9388                 {
9389                     tag: 'label',
9390                     'for' :  id,
9391                     cls : 'control-label',
9392                     html : this.fieldLabel
9393                 },
9394                 {
9395                     cls : "",
9396                     cn: [
9397                         inputblock
9398                     ]
9399                 }
9400
9401             ];
9402             
9403             if(this.labelWidth > 12){
9404                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9405             }
9406
9407             if(this.labelWidth < 13 && this.labelmd == 0){
9408                 this.labelmd = this.labelWidth;
9409             }
9410
9411             if(this.labellg > 0){
9412                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9413                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9414             }
9415
9416             if(this.labelmd > 0){
9417                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9418                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9419             }
9420
9421             if(this.labelsm > 0){
9422                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9423                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9424             }
9425
9426             if(this.labelxs > 0){
9427                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9428                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9429             }
9430             
9431         } else if ( this.fieldLabel.length) {
9432             cfg.cn = [
9433
9434                {
9435                    tag: 'label',
9436                    //cls : 'input-group-addon',
9437                    html : this.fieldLabel
9438
9439                },
9440
9441                inputblock
9442
9443            ];
9444
9445         } else {
9446
9447             cfg.cn = [
9448
9449                 inputblock
9450
9451             ];
9452                 
9453         }
9454         
9455         if (this.disabled) {
9456             input.disabled=true;
9457         }
9458         
9459         return cfg;
9460         
9461     },
9462     /**
9463      * return the real textarea element.
9464      */
9465     inputEl: function ()
9466     {
9467         return this.el.select('textarea.form-control',true).first();
9468     },
9469     
9470     /**
9471      * Clear any invalid styles/messages for this field
9472      */
9473     clearInvalid : function()
9474     {
9475         
9476         if(!this.el || this.preventMark){ // not rendered
9477             return;
9478         }
9479         
9480         var label = this.el.select('label', true).first();
9481         var icon = this.el.select('i.fa-star', true).first();
9482         
9483         if(label && icon){
9484             icon.remove();
9485         }
9486         
9487         this.el.removeClass(this.invalidClass);
9488         
9489         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9490             
9491             var feedback = this.el.select('.form-control-feedback', true).first();
9492             
9493             if(feedback){
9494                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9495             }
9496             
9497         }
9498         
9499         this.fireEvent('valid', this);
9500     },
9501     
9502      /**
9503      * Mark this field as valid
9504      */
9505     markValid : function()
9506     {
9507         if(!this.el  || this.preventMark){ // not rendered
9508             return;
9509         }
9510         
9511         this.el.removeClass([this.invalidClass, this.validClass]);
9512         
9513         var feedback = this.el.select('.form-control-feedback', true).first();
9514             
9515         if(feedback){
9516             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9517         }
9518
9519         if(this.disabled || this.allowBlank){
9520             return;
9521         }
9522         
9523         var label = this.el.select('label', true).first();
9524         var icon = this.el.select('i.fa-star', true).first();
9525         
9526         if(label && icon){
9527             icon.remove();
9528         }
9529         
9530         this.el.addClass(this.validClass);
9531         
9532         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9533             
9534             var feedback = this.el.select('.form-control-feedback', true).first();
9535             
9536             if(feedback){
9537                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9538                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9539             }
9540             
9541         }
9542         
9543         this.fireEvent('valid', this);
9544     },
9545     
9546      /**
9547      * Mark this field as invalid
9548      * @param {String} msg The validation message
9549      */
9550     markInvalid : function(msg)
9551     {
9552         if(!this.el  || this.preventMark){ // not rendered
9553             return;
9554         }
9555         
9556         this.el.removeClass([this.invalidClass, this.validClass]);
9557         
9558         var feedback = this.el.select('.form-control-feedback', true).first();
9559             
9560         if(feedback){
9561             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9562         }
9563
9564         if(this.disabled || this.allowBlank){
9565             return;
9566         }
9567         
9568         var label = this.el.select('label', true).first();
9569         var icon = this.el.select('i.fa-star', true).first();
9570         
9571         if(!this.getValue().length && label && !icon){
9572             this.el.createChild({
9573                 tag : 'i',
9574                 cls : 'text-danger fa fa-lg fa-star',
9575                 tooltip : 'This field is required',
9576                 style : 'margin-right:5px;'
9577             }, label, true);
9578         }
9579
9580         this.el.addClass(this.invalidClass);
9581         
9582         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9583             
9584             var feedback = this.el.select('.form-control-feedback', true).first();
9585             
9586             if(feedback){
9587                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9588                 
9589                 if(this.getValue().length || this.forceFeedback){
9590                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9591                 }
9592                 
9593             }
9594             
9595         }
9596         
9597         this.fireEvent('invalid', this, msg);
9598     }
9599 });
9600
9601  
9602 /*
9603  * - LGPL
9604  *
9605  * trigger field - base class for combo..
9606  * 
9607  */
9608  
9609 /**
9610  * @class Roo.bootstrap.TriggerField
9611  * @extends Roo.bootstrap.Input
9612  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9613  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9614  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9615  * for which you can provide a custom implementation.  For example:
9616  * <pre><code>
9617 var trigger = new Roo.bootstrap.TriggerField();
9618 trigger.onTriggerClick = myTriggerFn;
9619 trigger.applyTo('my-field');
9620 </code></pre>
9621  *
9622  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9623  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9624  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9625  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9626  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9627
9628  * @constructor
9629  * Create a new TriggerField.
9630  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9631  * to the base TextField)
9632  */
9633 Roo.bootstrap.TriggerField = function(config){
9634     this.mimicing = false;
9635     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9636 };
9637
9638 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9639     /**
9640      * @cfg {String} triggerClass A CSS class to apply to the trigger
9641      */
9642      /**
9643      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9644      */
9645     hideTrigger:false,
9646
9647     /**
9648      * @cfg {Boolean} removable (true|false) special filter default false
9649      */
9650     removable : false,
9651     
9652     /** @cfg {Boolean} grow @hide */
9653     /** @cfg {Number} growMin @hide */
9654     /** @cfg {Number} growMax @hide */
9655
9656     /**
9657      * @hide 
9658      * @method
9659      */
9660     autoSize: Roo.emptyFn,
9661     // private
9662     monitorTab : true,
9663     // private
9664     deferHeight : true,
9665
9666     
9667     actionMode : 'wrap',
9668     
9669     caret : false,
9670     
9671     
9672     getAutoCreate : function(){
9673        
9674         var align = this.labelAlign || this.parentLabelAlign();
9675         
9676         var id = Roo.id();
9677         
9678         var cfg = {
9679             cls: 'form-group' //input-group
9680         };
9681         
9682         
9683         var input =  {
9684             tag: 'input',
9685             id : id,
9686             type : this.inputType,
9687             cls : 'form-control',
9688             autocomplete: 'new-password',
9689             placeholder : this.placeholder || '' 
9690             
9691         };
9692         if (this.name) {
9693             input.name = this.name;
9694         }
9695         if (this.size) {
9696             input.cls += ' input-' + this.size;
9697         }
9698         
9699         if (this.disabled) {
9700             input.disabled=true;
9701         }
9702         
9703         var inputblock = input;
9704         
9705         if(this.hasFeedback && !this.allowBlank){
9706             
9707             var feedback = {
9708                 tag: 'span',
9709                 cls: 'glyphicon form-control-feedback'
9710             };
9711             
9712             if(this.removable && !this.editable && !this.tickable){
9713                 inputblock = {
9714                     cls : 'has-feedback',
9715                     cn :  [
9716                         inputblock,
9717                         {
9718                             tag: 'button',
9719                             html : 'x',
9720                             cls : 'roo-combo-removable-btn close'
9721                         },
9722                         feedback
9723                     ] 
9724                 };
9725             } else {
9726                 inputblock = {
9727                     cls : 'has-feedback',
9728                     cn :  [
9729                         inputblock,
9730                         feedback
9731                     ] 
9732                 };
9733             }
9734
9735         } else {
9736             if(this.removable && !this.editable && !this.tickable){
9737                 inputblock = {
9738                     cls : 'roo-removable',
9739                     cn :  [
9740                         inputblock,
9741                         {
9742                             tag: 'button',
9743                             html : 'x',
9744                             cls : 'roo-combo-removable-btn close'
9745                         }
9746                     ] 
9747                 };
9748             }
9749         }
9750         
9751         if (this.before || this.after) {
9752             
9753             inputblock = {
9754                 cls : 'input-group',
9755                 cn :  [] 
9756             };
9757             if (this.before) {
9758                 inputblock.cn.push({
9759                     tag :'span',
9760                     cls : 'input-group-addon',
9761                     html : this.before
9762                 });
9763             }
9764             
9765             inputblock.cn.push(input);
9766             
9767             if(this.hasFeedback && !this.allowBlank){
9768                 inputblock.cls += ' has-feedback';
9769                 inputblock.cn.push(feedback);
9770             }
9771             
9772             if (this.after) {
9773                 inputblock.cn.push({
9774                     tag :'span',
9775                     cls : 'input-group-addon',
9776                     html : this.after
9777                 });
9778             }
9779             
9780         };
9781         
9782         var box = {
9783             tag: 'div',
9784             cn: [
9785                 {
9786                     tag: 'input',
9787                     type : 'hidden',
9788                     cls: 'form-hidden-field'
9789                 },
9790                 inputblock
9791             ]
9792             
9793         };
9794         
9795         if(this.multiple){
9796             box = {
9797                 tag: 'div',
9798                 cn: [
9799                     {
9800                         tag: 'input',
9801                         type : 'hidden',
9802                         cls: 'form-hidden-field'
9803                     },
9804                     {
9805                         tag: 'ul',
9806                         cls: 'roo-select2-choices',
9807                         cn:[
9808                             {
9809                                 tag: 'li',
9810                                 cls: 'roo-select2-search-field',
9811                                 cn: [
9812
9813                                     inputblock
9814                                 ]
9815                             }
9816                         ]
9817                     }
9818                 ]
9819             }
9820         };
9821         
9822         var combobox = {
9823             cls: 'roo-select2-container input-group',
9824             cn: [
9825                 box
9826 //                {
9827 //                    tag: 'ul',
9828 //                    cls: 'typeahead typeahead-long dropdown-menu',
9829 //                    style: 'display:none'
9830 //                }
9831             ]
9832         };
9833         
9834         if(!this.multiple && this.showToggleBtn){
9835             
9836             var caret = {
9837                         tag: 'span',
9838                         cls: 'caret'
9839              };
9840             if (this.caret != false) {
9841                 caret = {
9842                      tag: 'i',
9843                      cls: 'fa fa-' + this.caret
9844                 };
9845                 
9846             }
9847             
9848             combobox.cn.push({
9849                 tag :'span',
9850                 cls : 'input-group-addon btn dropdown-toggle',
9851                 cn : [
9852                     caret,
9853                     {
9854                         tag: 'span',
9855                         cls: 'combobox-clear',
9856                         cn  : [
9857                             {
9858                                 tag : 'i',
9859                                 cls: 'icon-remove'
9860                             }
9861                         ]
9862                     }
9863                 ]
9864
9865             })
9866         }
9867         
9868         if(this.multiple){
9869             combobox.cls += ' roo-select2-container-multi';
9870         }
9871         
9872         if (align ==='left' && this.fieldLabel.length) {
9873             
9874             cfg.cls += ' roo-form-group-label-left';
9875
9876             cfg.cn = [
9877                 {
9878                     tag : 'i',
9879                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9880                     tooltip : 'This field is required'
9881                 },
9882                 {
9883                     tag: 'label',
9884                     'for' :  id,
9885                     cls : 'control-label',
9886                     html : this.fieldLabel
9887
9888                 },
9889                 {
9890                     cls : "", 
9891                     cn: [
9892                         combobox
9893                     ]
9894                 }
9895
9896             ];
9897             
9898             var labelCfg = cfg.cn[1];
9899             var contentCfg = cfg.cn[2];
9900             
9901             if(this.indicatorpos == 'right'){
9902                 cfg.cn = [
9903                     {
9904                         tag: 'label',
9905                         'for' :  id,
9906                         cls : 'control-label',
9907                         cn : [
9908                             {
9909                                 tag : 'span',
9910                                 html : this.fieldLabel
9911                             },
9912                             {
9913                                 tag : 'i',
9914                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9915                                 tooltip : 'This field is required'
9916                             }
9917                         ]
9918                     },
9919                     {
9920                         cls : "", 
9921                         cn: [
9922                             combobox
9923                         ]
9924                     }
9925
9926                 ];
9927                 
9928                 labelCfg = cfg.cn[0];
9929                 contentCfg = cfg.cn[1];
9930             }
9931             
9932             if(this.labelWidth > 12){
9933                 labelCfg.style = "width: " + this.labelWidth + 'px';
9934             }
9935             
9936             if(this.labelWidth < 13 && this.labelmd == 0){
9937                 this.labelmd = this.labelWidth;
9938             }
9939             
9940             if(this.labellg > 0){
9941                 labelCfg.cls += ' col-lg-' + this.labellg;
9942                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9943             }
9944             
9945             if(this.labelmd > 0){
9946                 labelCfg.cls += ' col-md-' + this.labelmd;
9947                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9948             }
9949             
9950             if(this.labelsm > 0){
9951                 labelCfg.cls += ' col-sm-' + this.labelsm;
9952                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9953             }
9954             
9955             if(this.labelxs > 0){
9956                 labelCfg.cls += ' col-xs-' + this.labelxs;
9957                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9958             }
9959             
9960         } else if ( this.fieldLabel.length) {
9961 //                Roo.log(" label");
9962             cfg.cn = [
9963                 {
9964                    tag : 'i',
9965                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9966                    tooltip : 'This field is required'
9967                },
9968                {
9969                    tag: 'label',
9970                    //cls : 'input-group-addon',
9971                    html : this.fieldLabel
9972
9973                },
9974
9975                combobox
9976
9977             ];
9978             
9979             if(this.indicatorpos == 'right'){
9980                 
9981                 cfg.cn = [
9982                     {
9983                        tag: 'label',
9984                        cn : [
9985                            {
9986                                tag : 'span',
9987                                html : this.fieldLabel
9988                            },
9989                            {
9990                               tag : 'i',
9991                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9992                               tooltip : 'This field is required'
9993                            }
9994                        ]
9995
9996                     },
9997                     combobox
9998
9999                 ];
10000
10001             }
10002
10003         } else {
10004             
10005 //                Roo.log(" no label && no align");
10006                 cfg = combobox
10007                      
10008                 
10009         }
10010         
10011         var settings=this;
10012         ['xs','sm','md','lg'].map(function(size){
10013             if (settings[size]) {
10014                 cfg.cls += ' col-' + size + '-' + settings[size];
10015             }
10016         });
10017         
10018         return cfg;
10019         
10020     },
10021     
10022     
10023     
10024     // private
10025     onResize : function(w, h){
10026 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10027 //        if(typeof w == 'number'){
10028 //            var x = w - this.trigger.getWidth();
10029 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10030 //            this.trigger.setStyle('left', x+'px');
10031 //        }
10032     },
10033
10034     // private
10035     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10036
10037     // private
10038     getResizeEl : function(){
10039         return this.inputEl();
10040     },
10041
10042     // private
10043     getPositionEl : function(){
10044         return this.inputEl();
10045     },
10046
10047     // private
10048     alignErrorIcon : function(){
10049         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10050     },
10051
10052     // private
10053     initEvents : function(){
10054         
10055         this.createList();
10056         
10057         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10058         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10059         if(!this.multiple && this.showToggleBtn){
10060             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10061             if(this.hideTrigger){
10062                 this.trigger.setDisplayed(false);
10063             }
10064             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10065         }
10066         
10067         if(this.multiple){
10068             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10069         }
10070         
10071         if(this.removable && !this.editable && !this.tickable){
10072             var close = this.closeTriggerEl();
10073             
10074             if(close){
10075                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10076                 close.on('click', this.removeBtnClick, this, close);
10077             }
10078         }
10079         
10080         //this.trigger.addClassOnOver('x-form-trigger-over');
10081         //this.trigger.addClassOnClick('x-form-trigger-click');
10082         
10083         //if(!this.width){
10084         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10085         //}
10086     },
10087     
10088     closeTriggerEl : function()
10089     {
10090         var close = this.el.select('.roo-combo-removable-btn', true).first();
10091         return close ? close : false;
10092     },
10093     
10094     removeBtnClick : function(e, h, el)
10095     {
10096         e.preventDefault();
10097         
10098         if(this.fireEvent("remove", this) !== false){
10099             this.reset();
10100             this.fireEvent("afterremove", this)
10101         }
10102     },
10103     
10104     createList : function()
10105     {
10106         this.list = Roo.get(document.body).createChild({
10107             tag: 'ul',
10108             cls: 'typeahead typeahead-long dropdown-menu',
10109             style: 'display:none'
10110         });
10111         
10112         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10113         
10114     },
10115
10116     // private
10117     initTrigger : function(){
10118        
10119     },
10120
10121     // private
10122     onDestroy : function(){
10123         if(this.trigger){
10124             this.trigger.removeAllListeners();
10125           //  this.trigger.remove();
10126         }
10127         //if(this.wrap){
10128         //    this.wrap.remove();
10129         //}
10130         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10131     },
10132
10133     // private
10134     onFocus : function(){
10135         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10136         /*
10137         if(!this.mimicing){
10138             this.wrap.addClass('x-trigger-wrap-focus');
10139             this.mimicing = true;
10140             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10141             if(this.monitorTab){
10142                 this.el.on("keydown", this.checkTab, this);
10143             }
10144         }
10145         */
10146     },
10147
10148     // private
10149     checkTab : function(e){
10150         if(e.getKey() == e.TAB){
10151             this.triggerBlur();
10152         }
10153     },
10154
10155     // private
10156     onBlur : function(){
10157         // do nothing
10158     },
10159
10160     // private
10161     mimicBlur : function(e, t){
10162         /*
10163         if(!this.wrap.contains(t) && this.validateBlur()){
10164             this.triggerBlur();
10165         }
10166         */
10167     },
10168
10169     // private
10170     triggerBlur : function(){
10171         this.mimicing = false;
10172         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10173         if(this.monitorTab){
10174             this.el.un("keydown", this.checkTab, this);
10175         }
10176         //this.wrap.removeClass('x-trigger-wrap-focus');
10177         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10178     },
10179
10180     // private
10181     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10182     validateBlur : function(e, t){
10183         return true;
10184     },
10185
10186     // private
10187     onDisable : function(){
10188         this.inputEl().dom.disabled = true;
10189         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10190         //if(this.wrap){
10191         //    this.wrap.addClass('x-item-disabled');
10192         //}
10193     },
10194
10195     // private
10196     onEnable : function(){
10197         this.inputEl().dom.disabled = false;
10198         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10199         //if(this.wrap){
10200         //    this.el.removeClass('x-item-disabled');
10201         //}
10202     },
10203
10204     // private
10205     onShow : function(){
10206         var ae = this.getActionEl();
10207         
10208         if(ae){
10209             ae.dom.style.display = '';
10210             ae.dom.style.visibility = 'visible';
10211         }
10212     },
10213
10214     // private
10215     
10216     onHide : function(){
10217         var ae = this.getActionEl();
10218         ae.dom.style.display = 'none';
10219     },
10220
10221     /**
10222      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10223      * by an implementing function.
10224      * @method
10225      * @param {EventObject} e
10226      */
10227     onTriggerClick : Roo.emptyFn
10228 });
10229  /*
10230  * Based on:
10231  * Ext JS Library 1.1.1
10232  * Copyright(c) 2006-2007, Ext JS, LLC.
10233  *
10234  * Originally Released Under LGPL - original licence link has changed is not relivant.
10235  *
10236  * Fork - LGPL
10237  * <script type="text/javascript">
10238  */
10239
10240
10241 /**
10242  * @class Roo.data.SortTypes
10243  * @singleton
10244  * Defines the default sorting (casting?) comparison functions used when sorting data.
10245  */
10246 Roo.data.SortTypes = {
10247     /**
10248      * Default sort that does nothing
10249      * @param {Mixed} s The value being converted
10250      * @return {Mixed} The comparison value
10251      */
10252     none : function(s){
10253         return s;
10254     },
10255     
10256     /**
10257      * The regular expression used to strip tags
10258      * @type {RegExp}
10259      * @property
10260      */
10261     stripTagsRE : /<\/?[^>]+>/gi,
10262     
10263     /**
10264      * Strips all HTML tags to sort on text only
10265      * @param {Mixed} s The value being converted
10266      * @return {String} The comparison value
10267      */
10268     asText : function(s){
10269         return String(s).replace(this.stripTagsRE, "");
10270     },
10271     
10272     /**
10273      * Strips all HTML tags to sort on text only - Case insensitive
10274      * @param {Mixed} s The value being converted
10275      * @return {String} The comparison value
10276      */
10277     asUCText : function(s){
10278         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10279     },
10280     
10281     /**
10282      * Case insensitive string
10283      * @param {Mixed} s The value being converted
10284      * @return {String} The comparison value
10285      */
10286     asUCString : function(s) {
10287         return String(s).toUpperCase();
10288     },
10289     
10290     /**
10291      * Date sorting
10292      * @param {Mixed} s The value being converted
10293      * @return {Number} The comparison value
10294      */
10295     asDate : function(s) {
10296         if(!s){
10297             return 0;
10298         }
10299         if(s instanceof Date){
10300             return s.getTime();
10301         }
10302         return Date.parse(String(s));
10303     },
10304     
10305     /**
10306      * Float sorting
10307      * @param {Mixed} s The value being converted
10308      * @return {Float} The comparison value
10309      */
10310     asFloat : function(s) {
10311         var val = parseFloat(String(s).replace(/,/g, ""));
10312         if(isNaN(val)) {
10313             val = 0;
10314         }
10315         return val;
10316     },
10317     
10318     /**
10319      * Integer sorting
10320      * @param {Mixed} s The value being converted
10321      * @return {Number} The comparison value
10322      */
10323     asInt : function(s) {
10324         var val = parseInt(String(s).replace(/,/g, ""));
10325         if(isNaN(val)) {
10326             val = 0;
10327         }
10328         return val;
10329     }
10330 };/*
10331  * Based on:
10332  * Ext JS Library 1.1.1
10333  * Copyright(c) 2006-2007, Ext JS, LLC.
10334  *
10335  * Originally Released Under LGPL - original licence link has changed is not relivant.
10336  *
10337  * Fork - LGPL
10338  * <script type="text/javascript">
10339  */
10340
10341 /**
10342 * @class Roo.data.Record
10343  * Instances of this class encapsulate both record <em>definition</em> information, and record
10344  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10345  * to access Records cached in an {@link Roo.data.Store} object.<br>
10346  * <p>
10347  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10348  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10349  * objects.<br>
10350  * <p>
10351  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10352  * @constructor
10353  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10354  * {@link #create}. The parameters are the same.
10355  * @param {Array} data An associative Array of data values keyed by the field name.
10356  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10357  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10358  * not specified an integer id is generated.
10359  */
10360 Roo.data.Record = function(data, id){
10361     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10362     this.data = data;
10363 };
10364
10365 /**
10366  * Generate a constructor for a specific record layout.
10367  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10368  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10369  * Each field definition object may contain the following properties: <ul>
10370  * <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,
10371  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10372  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10373  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10374  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10375  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10376  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10377  * this may be omitted.</p></li>
10378  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10379  * <ul><li>auto (Default, implies no conversion)</li>
10380  * <li>string</li>
10381  * <li>int</li>
10382  * <li>float</li>
10383  * <li>boolean</li>
10384  * <li>date</li></ul></p></li>
10385  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10386  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10387  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10388  * by the Reader into an object that will be stored in the Record. It is passed the
10389  * following parameters:<ul>
10390  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10391  * </ul></p></li>
10392  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10393  * </ul>
10394  * <br>usage:<br><pre><code>
10395 var TopicRecord = Roo.data.Record.create(
10396     {name: 'title', mapping: 'topic_title'},
10397     {name: 'author', mapping: 'username'},
10398     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10399     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10400     {name: 'lastPoster', mapping: 'user2'},
10401     {name: 'excerpt', mapping: 'post_text'}
10402 );
10403
10404 var myNewRecord = new TopicRecord({
10405     title: 'Do my job please',
10406     author: 'noobie',
10407     totalPosts: 1,
10408     lastPost: new Date(),
10409     lastPoster: 'Animal',
10410     excerpt: 'No way dude!'
10411 });
10412 myStore.add(myNewRecord);
10413 </code></pre>
10414  * @method create
10415  * @static
10416  */
10417 Roo.data.Record.create = function(o){
10418     var f = function(){
10419         f.superclass.constructor.apply(this, arguments);
10420     };
10421     Roo.extend(f, Roo.data.Record);
10422     var p = f.prototype;
10423     p.fields = new Roo.util.MixedCollection(false, function(field){
10424         return field.name;
10425     });
10426     for(var i = 0, len = o.length; i < len; i++){
10427         p.fields.add(new Roo.data.Field(o[i]));
10428     }
10429     f.getField = function(name){
10430         return p.fields.get(name);  
10431     };
10432     return f;
10433 };
10434
10435 Roo.data.Record.AUTO_ID = 1000;
10436 Roo.data.Record.EDIT = 'edit';
10437 Roo.data.Record.REJECT = 'reject';
10438 Roo.data.Record.COMMIT = 'commit';
10439
10440 Roo.data.Record.prototype = {
10441     /**
10442      * Readonly flag - true if this record has been modified.
10443      * @type Boolean
10444      */
10445     dirty : false,
10446     editing : false,
10447     error: null,
10448     modified: null,
10449
10450     // private
10451     join : function(store){
10452         this.store = store;
10453     },
10454
10455     /**
10456      * Set the named field to the specified value.
10457      * @param {String} name The name of the field to set.
10458      * @param {Object} value The value to set the field to.
10459      */
10460     set : function(name, value){
10461         if(this.data[name] == value){
10462             return;
10463         }
10464         this.dirty = true;
10465         if(!this.modified){
10466             this.modified = {};
10467         }
10468         if(typeof this.modified[name] == 'undefined'){
10469             this.modified[name] = this.data[name];
10470         }
10471         this.data[name] = value;
10472         if(!this.editing && this.store){
10473             this.store.afterEdit(this);
10474         }       
10475     },
10476
10477     /**
10478      * Get the value of the named field.
10479      * @param {String} name The name of the field to get the value of.
10480      * @return {Object} The value of the field.
10481      */
10482     get : function(name){
10483         return this.data[name]; 
10484     },
10485
10486     // private
10487     beginEdit : function(){
10488         this.editing = true;
10489         this.modified = {}; 
10490     },
10491
10492     // private
10493     cancelEdit : function(){
10494         this.editing = false;
10495         delete this.modified;
10496     },
10497
10498     // private
10499     endEdit : function(){
10500         this.editing = false;
10501         if(this.dirty && this.store){
10502             this.store.afterEdit(this);
10503         }
10504     },
10505
10506     /**
10507      * Usually called by the {@link Roo.data.Store} which owns the Record.
10508      * Rejects all changes made to the Record since either creation, or the last commit operation.
10509      * Modified fields are reverted to their original values.
10510      * <p>
10511      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10512      * of reject operations.
10513      */
10514     reject : function(){
10515         var m = this.modified;
10516         for(var n in m){
10517             if(typeof m[n] != "function"){
10518                 this.data[n] = m[n];
10519             }
10520         }
10521         this.dirty = false;
10522         delete this.modified;
10523         this.editing = false;
10524         if(this.store){
10525             this.store.afterReject(this);
10526         }
10527     },
10528
10529     /**
10530      * Usually called by the {@link Roo.data.Store} which owns the Record.
10531      * Commits all changes made to the Record since either creation, or the last commit operation.
10532      * <p>
10533      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10534      * of commit operations.
10535      */
10536     commit : function(){
10537         this.dirty = false;
10538         delete this.modified;
10539         this.editing = false;
10540         if(this.store){
10541             this.store.afterCommit(this);
10542         }
10543     },
10544
10545     // private
10546     hasError : function(){
10547         return this.error != null;
10548     },
10549
10550     // private
10551     clearError : function(){
10552         this.error = null;
10553     },
10554
10555     /**
10556      * Creates a copy of this record.
10557      * @param {String} id (optional) A new record id if you don't want to use this record's id
10558      * @return {Record}
10559      */
10560     copy : function(newId) {
10561         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10562     }
10563 };/*
10564  * Based on:
10565  * Ext JS Library 1.1.1
10566  * Copyright(c) 2006-2007, Ext JS, LLC.
10567  *
10568  * Originally Released Under LGPL - original licence link has changed is not relivant.
10569  *
10570  * Fork - LGPL
10571  * <script type="text/javascript">
10572  */
10573
10574
10575
10576 /**
10577  * @class Roo.data.Store
10578  * @extends Roo.util.Observable
10579  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10580  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10581  * <p>
10582  * 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
10583  * has no knowledge of the format of the data returned by the Proxy.<br>
10584  * <p>
10585  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10586  * instances from the data object. These records are cached and made available through accessor functions.
10587  * @constructor
10588  * Creates a new Store.
10589  * @param {Object} config A config object containing the objects needed for the Store to access data,
10590  * and read the data into Records.
10591  */
10592 Roo.data.Store = function(config){
10593     this.data = new Roo.util.MixedCollection(false);
10594     this.data.getKey = function(o){
10595         return o.id;
10596     };
10597     this.baseParams = {};
10598     // private
10599     this.paramNames = {
10600         "start" : "start",
10601         "limit" : "limit",
10602         "sort" : "sort",
10603         "dir" : "dir",
10604         "multisort" : "_multisort"
10605     };
10606
10607     if(config && config.data){
10608         this.inlineData = config.data;
10609         delete config.data;
10610     }
10611
10612     Roo.apply(this, config);
10613     
10614     if(this.reader){ // reader passed
10615         this.reader = Roo.factory(this.reader, Roo.data);
10616         this.reader.xmodule = this.xmodule || false;
10617         if(!this.recordType){
10618             this.recordType = this.reader.recordType;
10619         }
10620         if(this.reader.onMetaChange){
10621             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10622         }
10623     }
10624
10625     if(this.recordType){
10626         this.fields = this.recordType.prototype.fields;
10627     }
10628     this.modified = [];
10629
10630     this.addEvents({
10631         /**
10632          * @event datachanged
10633          * Fires when the data cache has changed, and a widget which is using this Store
10634          * as a Record cache should refresh its view.
10635          * @param {Store} this
10636          */
10637         datachanged : true,
10638         /**
10639          * @event metachange
10640          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10641          * @param {Store} this
10642          * @param {Object} meta The JSON metadata
10643          */
10644         metachange : true,
10645         /**
10646          * @event add
10647          * Fires when Records have been added to the Store
10648          * @param {Store} this
10649          * @param {Roo.data.Record[]} records The array of Records added
10650          * @param {Number} index The index at which the record(s) were added
10651          */
10652         add : true,
10653         /**
10654          * @event remove
10655          * Fires when a Record has been removed from the Store
10656          * @param {Store} this
10657          * @param {Roo.data.Record} record The Record that was removed
10658          * @param {Number} index The index at which the record was removed
10659          */
10660         remove : true,
10661         /**
10662          * @event update
10663          * Fires when a Record has been updated
10664          * @param {Store} this
10665          * @param {Roo.data.Record} record The Record that was updated
10666          * @param {String} operation The update operation being performed.  Value may be one of:
10667          * <pre><code>
10668  Roo.data.Record.EDIT
10669  Roo.data.Record.REJECT
10670  Roo.data.Record.COMMIT
10671          * </code></pre>
10672          */
10673         update : true,
10674         /**
10675          * @event clear
10676          * Fires when the data cache has been cleared.
10677          * @param {Store} this
10678          */
10679         clear : true,
10680         /**
10681          * @event beforeload
10682          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10683          * the load action will be canceled.
10684          * @param {Store} this
10685          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10686          */
10687         beforeload : true,
10688         /**
10689          * @event beforeloadadd
10690          * Fires after a new set of Records has been loaded.
10691          * @param {Store} this
10692          * @param {Roo.data.Record[]} records The Records that were loaded
10693          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10694          */
10695         beforeloadadd : true,
10696         /**
10697          * @event load
10698          * Fires after a new set of Records has been loaded, before they are added to the store.
10699          * @param {Store} this
10700          * @param {Roo.data.Record[]} records The Records that were loaded
10701          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10702          * @params {Object} return from reader
10703          */
10704         load : true,
10705         /**
10706          * @event loadexception
10707          * Fires if an exception occurs in the Proxy during loading.
10708          * Called with the signature of the Proxy's "loadexception" event.
10709          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10710          * 
10711          * @param {Proxy} 
10712          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10713          * @param {Object} load options 
10714          * @param {Object} jsonData from your request (normally this contains the Exception)
10715          */
10716         loadexception : true
10717     });
10718     
10719     if(this.proxy){
10720         this.proxy = Roo.factory(this.proxy, Roo.data);
10721         this.proxy.xmodule = this.xmodule || false;
10722         this.relayEvents(this.proxy,  ["loadexception"]);
10723     }
10724     this.sortToggle = {};
10725     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10726
10727     Roo.data.Store.superclass.constructor.call(this);
10728
10729     if(this.inlineData){
10730         this.loadData(this.inlineData);
10731         delete this.inlineData;
10732     }
10733 };
10734
10735 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10736      /**
10737     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10738     * without a remote query - used by combo/forms at present.
10739     */
10740     
10741     /**
10742     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10743     */
10744     /**
10745     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10746     */
10747     /**
10748     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10749     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10750     */
10751     /**
10752     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10753     * on any HTTP request
10754     */
10755     /**
10756     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10757     */
10758     /**
10759     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10760     */
10761     multiSort: false,
10762     /**
10763     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10764     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10765     */
10766     remoteSort : false,
10767
10768     /**
10769     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10770      * loaded or when a record is removed. (defaults to false).
10771     */
10772     pruneModifiedRecords : false,
10773
10774     // private
10775     lastOptions : null,
10776
10777     /**
10778      * Add Records to the Store and fires the add event.
10779      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10780      */
10781     add : function(records){
10782         records = [].concat(records);
10783         for(var i = 0, len = records.length; i < len; i++){
10784             records[i].join(this);
10785         }
10786         var index = this.data.length;
10787         this.data.addAll(records);
10788         this.fireEvent("add", this, records, index);
10789     },
10790
10791     /**
10792      * Remove a Record from the Store and fires the remove event.
10793      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10794      */
10795     remove : function(record){
10796         var index = this.data.indexOf(record);
10797         this.data.removeAt(index);
10798         if(this.pruneModifiedRecords){
10799             this.modified.remove(record);
10800         }
10801         this.fireEvent("remove", this, record, index);
10802     },
10803
10804     /**
10805      * Remove all Records from the Store and fires the clear event.
10806      */
10807     removeAll : function(){
10808         this.data.clear();
10809         if(this.pruneModifiedRecords){
10810             this.modified = [];
10811         }
10812         this.fireEvent("clear", this);
10813     },
10814
10815     /**
10816      * Inserts Records to the Store at the given index and fires the add event.
10817      * @param {Number} index The start index at which to insert the passed Records.
10818      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10819      */
10820     insert : function(index, records){
10821         records = [].concat(records);
10822         for(var i = 0, len = records.length; i < len; i++){
10823             this.data.insert(index, records[i]);
10824             records[i].join(this);
10825         }
10826         this.fireEvent("add", this, records, index);
10827     },
10828
10829     /**
10830      * Get the index within the cache of the passed Record.
10831      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10832      * @return {Number} The index of the passed Record. Returns -1 if not found.
10833      */
10834     indexOf : function(record){
10835         return this.data.indexOf(record);
10836     },
10837
10838     /**
10839      * Get the index within the cache of the Record with the passed id.
10840      * @param {String} id The id of the Record to find.
10841      * @return {Number} The index of the Record. Returns -1 if not found.
10842      */
10843     indexOfId : function(id){
10844         return this.data.indexOfKey(id);
10845     },
10846
10847     /**
10848      * Get the Record with the specified id.
10849      * @param {String} id The id of the Record to find.
10850      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10851      */
10852     getById : function(id){
10853         return this.data.key(id);
10854     },
10855
10856     /**
10857      * Get the Record at the specified index.
10858      * @param {Number} index The index of the Record to find.
10859      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10860      */
10861     getAt : function(index){
10862         return this.data.itemAt(index);
10863     },
10864
10865     /**
10866      * Returns a range of Records between specified indices.
10867      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10868      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10869      * @return {Roo.data.Record[]} An array of Records
10870      */
10871     getRange : function(start, end){
10872         return this.data.getRange(start, end);
10873     },
10874
10875     // private
10876     storeOptions : function(o){
10877         o = Roo.apply({}, o);
10878         delete o.callback;
10879         delete o.scope;
10880         this.lastOptions = o;
10881     },
10882
10883     /**
10884      * Loads the Record cache from the configured Proxy using the configured Reader.
10885      * <p>
10886      * If using remote paging, then the first load call must specify the <em>start</em>
10887      * and <em>limit</em> properties in the options.params property to establish the initial
10888      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10889      * <p>
10890      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10891      * and this call will return before the new data has been loaded. Perform any post-processing
10892      * in a callback function, or in a "load" event handler.</strong>
10893      * <p>
10894      * @param {Object} options An object containing properties which control loading options:<ul>
10895      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10896      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10897      * passed the following arguments:<ul>
10898      * <li>r : Roo.data.Record[]</li>
10899      * <li>options: Options object from the load call</li>
10900      * <li>success: Boolean success indicator</li></ul></li>
10901      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10902      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10903      * </ul>
10904      */
10905     load : function(options){
10906         options = options || {};
10907         if(this.fireEvent("beforeload", this, options) !== false){
10908             this.storeOptions(options);
10909             var p = Roo.apply(options.params || {}, this.baseParams);
10910             // if meta was not loaded from remote source.. try requesting it.
10911             if (!this.reader.metaFromRemote) {
10912                 p._requestMeta = 1;
10913             }
10914             if(this.sortInfo && this.remoteSort){
10915                 var pn = this.paramNames;
10916                 p[pn["sort"]] = this.sortInfo.field;
10917                 p[pn["dir"]] = this.sortInfo.direction;
10918             }
10919             if (this.multiSort) {
10920                 var pn = this.paramNames;
10921                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10922             }
10923             
10924             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10925         }
10926     },
10927
10928     /**
10929      * Reloads the Record cache from the configured Proxy using the configured Reader and
10930      * the options from the last load operation performed.
10931      * @param {Object} options (optional) An object containing properties which may override the options
10932      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10933      * the most recently used options are reused).
10934      */
10935     reload : function(options){
10936         this.load(Roo.applyIf(options||{}, this.lastOptions));
10937     },
10938
10939     // private
10940     // Called as a callback by the Reader during a load operation.
10941     loadRecords : function(o, options, success){
10942         if(!o || success === false){
10943             if(success !== false){
10944                 this.fireEvent("load", this, [], options, o);
10945             }
10946             if(options.callback){
10947                 options.callback.call(options.scope || this, [], options, false);
10948             }
10949             return;
10950         }
10951         // if data returned failure - throw an exception.
10952         if (o.success === false) {
10953             // show a message if no listener is registered.
10954             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10955                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10956             }
10957             // loadmask wil be hooked into this..
10958             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10959             return;
10960         }
10961         var r = o.records, t = o.totalRecords || r.length;
10962         
10963         this.fireEvent("beforeloadadd", this, r, options, o);
10964         
10965         if(!options || options.add !== true){
10966             if(this.pruneModifiedRecords){
10967                 this.modified = [];
10968             }
10969             for(var i = 0, len = r.length; i < len; i++){
10970                 r[i].join(this);
10971             }
10972             if(this.snapshot){
10973                 this.data = this.snapshot;
10974                 delete this.snapshot;
10975             }
10976             this.data.clear();
10977             this.data.addAll(r);
10978             this.totalLength = t;
10979             this.applySort();
10980             this.fireEvent("datachanged", this);
10981         }else{
10982             this.totalLength = Math.max(t, this.data.length+r.length);
10983             this.add(r);
10984         }
10985         this.fireEvent("load", this, r, options, o);
10986         if(options.callback){
10987             options.callback.call(options.scope || this, r, options, true);
10988         }
10989     },
10990
10991
10992     /**
10993      * Loads data from a passed data block. A Reader which understands the format of the data
10994      * must have been configured in the constructor.
10995      * @param {Object} data The data block from which to read the Records.  The format of the data expected
10996      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10997      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10998      */
10999     loadData : function(o, append){
11000         var r = this.reader.readRecords(o);
11001         this.loadRecords(r, {add: append}, true);
11002     },
11003
11004     /**
11005      * Gets the number of cached records.
11006      * <p>
11007      * <em>If using paging, this may not be the total size of the dataset. If the data object
11008      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11009      * the data set size</em>
11010      */
11011     getCount : function(){
11012         return this.data.length || 0;
11013     },
11014
11015     /**
11016      * Gets the total number of records in the dataset as returned by the server.
11017      * <p>
11018      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11019      * the dataset size</em>
11020      */
11021     getTotalCount : function(){
11022         return this.totalLength || 0;
11023     },
11024
11025     /**
11026      * Returns the sort state of the Store as an object with two properties:
11027      * <pre><code>
11028  field {String} The name of the field by which the Records are sorted
11029  direction {String} The sort order, "ASC" or "DESC"
11030      * </code></pre>
11031      */
11032     getSortState : function(){
11033         return this.sortInfo;
11034     },
11035
11036     // private
11037     applySort : function(){
11038         if(this.sortInfo && !this.remoteSort){
11039             var s = this.sortInfo, f = s.field;
11040             var st = this.fields.get(f).sortType;
11041             var fn = function(r1, r2){
11042                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11043                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11044             };
11045             this.data.sort(s.direction, fn);
11046             if(this.snapshot && this.snapshot != this.data){
11047                 this.snapshot.sort(s.direction, fn);
11048             }
11049         }
11050     },
11051
11052     /**
11053      * Sets the default sort column and order to be used by the next load operation.
11054      * @param {String} fieldName The name of the field to sort by.
11055      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11056      */
11057     setDefaultSort : function(field, dir){
11058         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11059     },
11060
11061     /**
11062      * Sort the Records.
11063      * If remote sorting is used, the sort is performed on the server, and the cache is
11064      * reloaded. If local sorting is used, the cache is sorted internally.
11065      * @param {String} fieldName The name of the field to sort by.
11066      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11067      */
11068     sort : function(fieldName, dir){
11069         var f = this.fields.get(fieldName);
11070         if(!dir){
11071             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11072             
11073             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11074                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11075             }else{
11076                 dir = f.sortDir;
11077             }
11078         }
11079         this.sortToggle[f.name] = dir;
11080         this.sortInfo = {field: f.name, direction: dir};
11081         if(!this.remoteSort){
11082             this.applySort();
11083             this.fireEvent("datachanged", this);
11084         }else{
11085             this.load(this.lastOptions);
11086         }
11087     },
11088
11089     /**
11090      * Calls the specified function for each of the Records in the cache.
11091      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11092      * Returning <em>false</em> aborts and exits the iteration.
11093      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11094      */
11095     each : function(fn, scope){
11096         this.data.each(fn, scope);
11097     },
11098
11099     /**
11100      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11101      * (e.g., during paging).
11102      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11103      */
11104     getModifiedRecords : function(){
11105         return this.modified;
11106     },
11107
11108     // private
11109     createFilterFn : function(property, value, anyMatch){
11110         if(!value.exec){ // not a regex
11111             value = String(value);
11112             if(value.length == 0){
11113                 return false;
11114             }
11115             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11116         }
11117         return function(r){
11118             return value.test(r.data[property]);
11119         };
11120     },
11121
11122     /**
11123      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11124      * @param {String} property A field on your records
11125      * @param {Number} start The record index to start at (defaults to 0)
11126      * @param {Number} end The last record index to include (defaults to length - 1)
11127      * @return {Number} The sum
11128      */
11129     sum : function(property, start, end){
11130         var rs = this.data.items, v = 0;
11131         start = start || 0;
11132         end = (end || end === 0) ? end : rs.length-1;
11133
11134         for(var i = start; i <= end; i++){
11135             v += (rs[i].data[property] || 0);
11136         }
11137         return v;
11138     },
11139
11140     /**
11141      * Filter the records by a specified property.
11142      * @param {String} field A field on your records
11143      * @param {String/RegExp} value Either a string that the field
11144      * should start with or a RegExp to test against the field
11145      * @param {Boolean} anyMatch True to match any part not just the beginning
11146      */
11147     filter : function(property, value, anyMatch){
11148         var fn = this.createFilterFn(property, value, anyMatch);
11149         return fn ? this.filterBy(fn) : this.clearFilter();
11150     },
11151
11152     /**
11153      * Filter by a function. The specified function will be called with each
11154      * record in this data source. If the function returns true the record is included,
11155      * otherwise it is filtered.
11156      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11157      * @param {Object} scope (optional) The scope of the function (defaults to this)
11158      */
11159     filterBy : function(fn, scope){
11160         this.snapshot = this.snapshot || this.data;
11161         this.data = this.queryBy(fn, scope||this);
11162         this.fireEvent("datachanged", this);
11163     },
11164
11165     /**
11166      * Query the records by a specified property.
11167      * @param {String} field A field on your records
11168      * @param {String/RegExp} value Either a string that the field
11169      * should start with or a RegExp to test against the field
11170      * @param {Boolean} anyMatch True to match any part not just the beginning
11171      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11172      */
11173     query : function(property, value, anyMatch){
11174         var fn = this.createFilterFn(property, value, anyMatch);
11175         return fn ? this.queryBy(fn) : this.data.clone();
11176     },
11177
11178     /**
11179      * Query by a function. The specified function will be called with each
11180      * record in this data source. If the function returns true the record is included
11181      * in the results.
11182      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11183      * @param {Object} scope (optional) The scope of the function (defaults to this)
11184       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11185      **/
11186     queryBy : function(fn, scope){
11187         var data = this.snapshot || this.data;
11188         return data.filterBy(fn, scope||this);
11189     },
11190
11191     /**
11192      * Collects unique values for a particular dataIndex from this store.
11193      * @param {String} dataIndex The property to collect
11194      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11195      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11196      * @return {Array} An array of the unique values
11197      **/
11198     collect : function(dataIndex, allowNull, bypassFilter){
11199         var d = (bypassFilter === true && this.snapshot) ?
11200                 this.snapshot.items : this.data.items;
11201         var v, sv, r = [], l = {};
11202         for(var i = 0, len = d.length; i < len; i++){
11203             v = d[i].data[dataIndex];
11204             sv = String(v);
11205             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11206                 l[sv] = true;
11207                 r[r.length] = v;
11208             }
11209         }
11210         return r;
11211     },
11212
11213     /**
11214      * Revert to a view of the Record cache with no filtering applied.
11215      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11216      */
11217     clearFilter : function(suppressEvent){
11218         if(this.snapshot && this.snapshot != this.data){
11219             this.data = this.snapshot;
11220             delete this.snapshot;
11221             if(suppressEvent !== true){
11222                 this.fireEvent("datachanged", this);
11223             }
11224         }
11225     },
11226
11227     // private
11228     afterEdit : function(record){
11229         if(this.modified.indexOf(record) == -1){
11230             this.modified.push(record);
11231         }
11232         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11233     },
11234     
11235     // private
11236     afterReject : function(record){
11237         this.modified.remove(record);
11238         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11239     },
11240
11241     // private
11242     afterCommit : function(record){
11243         this.modified.remove(record);
11244         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11245     },
11246
11247     /**
11248      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11249      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11250      */
11251     commitChanges : function(){
11252         var m = this.modified.slice(0);
11253         this.modified = [];
11254         for(var i = 0, len = m.length; i < len; i++){
11255             m[i].commit();
11256         }
11257     },
11258
11259     /**
11260      * Cancel outstanding changes on all changed records.
11261      */
11262     rejectChanges : function(){
11263         var m = this.modified.slice(0);
11264         this.modified = [];
11265         for(var i = 0, len = m.length; i < len; i++){
11266             m[i].reject();
11267         }
11268     },
11269
11270     onMetaChange : function(meta, rtype, o){
11271         this.recordType = rtype;
11272         this.fields = rtype.prototype.fields;
11273         delete this.snapshot;
11274         this.sortInfo = meta.sortInfo || this.sortInfo;
11275         this.modified = [];
11276         this.fireEvent('metachange', this, this.reader.meta);
11277     },
11278     
11279     moveIndex : function(data, type)
11280     {
11281         var index = this.indexOf(data);
11282         
11283         var newIndex = index + type;
11284         
11285         this.remove(data);
11286         
11287         this.insert(newIndex, data);
11288         
11289     }
11290 });/*
11291  * Based on:
11292  * Ext JS Library 1.1.1
11293  * Copyright(c) 2006-2007, Ext JS, LLC.
11294  *
11295  * Originally Released Under LGPL - original licence link has changed is not relivant.
11296  *
11297  * Fork - LGPL
11298  * <script type="text/javascript">
11299  */
11300
11301 /**
11302  * @class Roo.data.SimpleStore
11303  * @extends Roo.data.Store
11304  * Small helper class to make creating Stores from Array data easier.
11305  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11306  * @cfg {Array} fields An array of field definition objects, or field name strings.
11307  * @cfg {Array} data The multi-dimensional array of data
11308  * @constructor
11309  * @param {Object} config
11310  */
11311 Roo.data.SimpleStore = function(config){
11312     Roo.data.SimpleStore.superclass.constructor.call(this, {
11313         isLocal : true,
11314         reader: new Roo.data.ArrayReader({
11315                 id: config.id
11316             },
11317             Roo.data.Record.create(config.fields)
11318         ),
11319         proxy : new Roo.data.MemoryProxy(config.data)
11320     });
11321     this.load();
11322 };
11323 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11324  * Based on:
11325  * Ext JS Library 1.1.1
11326  * Copyright(c) 2006-2007, Ext JS, LLC.
11327  *
11328  * Originally Released Under LGPL - original licence link has changed is not relivant.
11329  *
11330  * Fork - LGPL
11331  * <script type="text/javascript">
11332  */
11333
11334 /**
11335 /**
11336  * @extends Roo.data.Store
11337  * @class Roo.data.JsonStore
11338  * Small helper class to make creating Stores for JSON data easier. <br/>
11339 <pre><code>
11340 var store = new Roo.data.JsonStore({
11341     url: 'get-images.php',
11342     root: 'images',
11343     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11344 });
11345 </code></pre>
11346  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11347  * JsonReader and HttpProxy (unless inline data is provided).</b>
11348  * @cfg {Array} fields An array of field definition objects, or field name strings.
11349  * @constructor
11350  * @param {Object} config
11351  */
11352 Roo.data.JsonStore = function(c){
11353     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11354         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11355         reader: new Roo.data.JsonReader(c, c.fields)
11356     }));
11357 };
11358 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11359  * Based on:
11360  * Ext JS Library 1.1.1
11361  * Copyright(c) 2006-2007, Ext JS, LLC.
11362  *
11363  * Originally Released Under LGPL - original licence link has changed is not relivant.
11364  *
11365  * Fork - LGPL
11366  * <script type="text/javascript">
11367  */
11368
11369  
11370 Roo.data.Field = function(config){
11371     if(typeof config == "string"){
11372         config = {name: config};
11373     }
11374     Roo.apply(this, config);
11375     
11376     if(!this.type){
11377         this.type = "auto";
11378     }
11379     
11380     var st = Roo.data.SortTypes;
11381     // named sortTypes are supported, here we look them up
11382     if(typeof this.sortType == "string"){
11383         this.sortType = st[this.sortType];
11384     }
11385     
11386     // set default sortType for strings and dates
11387     if(!this.sortType){
11388         switch(this.type){
11389             case "string":
11390                 this.sortType = st.asUCString;
11391                 break;
11392             case "date":
11393                 this.sortType = st.asDate;
11394                 break;
11395             default:
11396                 this.sortType = st.none;
11397         }
11398     }
11399
11400     // define once
11401     var stripRe = /[\$,%]/g;
11402
11403     // prebuilt conversion function for this field, instead of
11404     // switching every time we're reading a value
11405     if(!this.convert){
11406         var cv, dateFormat = this.dateFormat;
11407         switch(this.type){
11408             case "":
11409             case "auto":
11410             case undefined:
11411                 cv = function(v){ return v; };
11412                 break;
11413             case "string":
11414                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11415                 break;
11416             case "int":
11417                 cv = function(v){
11418                     return v !== undefined && v !== null && v !== '' ?
11419                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11420                     };
11421                 break;
11422             case "float":
11423                 cv = function(v){
11424                     return v !== undefined && v !== null && v !== '' ?
11425                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11426                     };
11427                 break;
11428             case "bool":
11429             case "boolean":
11430                 cv = function(v){ return v === true || v === "true" || v == 1; };
11431                 break;
11432             case "date":
11433                 cv = function(v){
11434                     if(!v){
11435                         return '';
11436                     }
11437                     if(v instanceof Date){
11438                         return v;
11439                     }
11440                     if(dateFormat){
11441                         if(dateFormat == "timestamp"){
11442                             return new Date(v*1000);
11443                         }
11444                         return Date.parseDate(v, dateFormat);
11445                     }
11446                     var parsed = Date.parse(v);
11447                     return parsed ? new Date(parsed) : null;
11448                 };
11449              break;
11450             
11451         }
11452         this.convert = cv;
11453     }
11454 };
11455
11456 Roo.data.Field.prototype = {
11457     dateFormat: null,
11458     defaultValue: "",
11459     mapping: null,
11460     sortType : null,
11461     sortDir : "ASC"
11462 };/*
11463  * Based on:
11464  * Ext JS Library 1.1.1
11465  * Copyright(c) 2006-2007, Ext JS, LLC.
11466  *
11467  * Originally Released Under LGPL - original licence link has changed is not relivant.
11468  *
11469  * Fork - LGPL
11470  * <script type="text/javascript">
11471  */
11472  
11473 // Base class for reading structured data from a data source.  This class is intended to be
11474 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11475
11476 /**
11477  * @class Roo.data.DataReader
11478  * Base class for reading structured data from a data source.  This class is intended to be
11479  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11480  */
11481
11482 Roo.data.DataReader = function(meta, recordType){
11483     
11484     this.meta = meta;
11485     
11486     this.recordType = recordType instanceof Array ? 
11487         Roo.data.Record.create(recordType) : recordType;
11488 };
11489
11490 Roo.data.DataReader.prototype = {
11491      /**
11492      * Create an empty record
11493      * @param {Object} data (optional) - overlay some values
11494      * @return {Roo.data.Record} record created.
11495      */
11496     newRow :  function(d) {
11497         var da =  {};
11498         this.recordType.prototype.fields.each(function(c) {
11499             switch( c.type) {
11500                 case 'int' : da[c.name] = 0; break;
11501                 case 'date' : da[c.name] = new Date(); break;
11502                 case 'float' : da[c.name] = 0.0; break;
11503                 case 'boolean' : da[c.name] = false; break;
11504                 default : da[c.name] = ""; break;
11505             }
11506             
11507         });
11508         return new this.recordType(Roo.apply(da, d));
11509     }
11510     
11511 };/*
11512  * Based on:
11513  * Ext JS Library 1.1.1
11514  * Copyright(c) 2006-2007, Ext JS, LLC.
11515  *
11516  * Originally Released Under LGPL - original licence link has changed is not relivant.
11517  *
11518  * Fork - LGPL
11519  * <script type="text/javascript">
11520  */
11521
11522 /**
11523  * @class Roo.data.DataProxy
11524  * @extends Roo.data.Observable
11525  * This class is an abstract base class for implementations which provide retrieval of
11526  * unformatted data objects.<br>
11527  * <p>
11528  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11529  * (of the appropriate type which knows how to parse the data object) to provide a block of
11530  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11531  * <p>
11532  * Custom implementations must implement the load method as described in
11533  * {@link Roo.data.HttpProxy#load}.
11534  */
11535 Roo.data.DataProxy = function(){
11536     this.addEvents({
11537         /**
11538          * @event beforeload
11539          * Fires before a network request is made to retrieve a data object.
11540          * @param {Object} This DataProxy object.
11541          * @param {Object} params The params parameter to the load function.
11542          */
11543         beforeload : true,
11544         /**
11545          * @event load
11546          * Fires before the load method's callback is called.
11547          * @param {Object} This DataProxy object.
11548          * @param {Object} o The data object.
11549          * @param {Object} arg The callback argument object passed to the load function.
11550          */
11551         load : true,
11552         /**
11553          * @event loadexception
11554          * Fires if an Exception occurs during data retrieval.
11555          * @param {Object} This DataProxy object.
11556          * @param {Object} o The data object.
11557          * @param {Object} arg The callback argument object passed to the load function.
11558          * @param {Object} e The Exception.
11559          */
11560         loadexception : true
11561     });
11562     Roo.data.DataProxy.superclass.constructor.call(this);
11563 };
11564
11565 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11566
11567     /**
11568      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11569      */
11570 /*
11571  * Based on:
11572  * Ext JS Library 1.1.1
11573  * Copyright(c) 2006-2007, Ext JS, LLC.
11574  *
11575  * Originally Released Under LGPL - original licence link has changed is not relivant.
11576  *
11577  * Fork - LGPL
11578  * <script type="text/javascript">
11579  */
11580 /**
11581  * @class Roo.data.MemoryProxy
11582  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11583  * to the Reader when its load method is called.
11584  * @constructor
11585  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11586  */
11587 Roo.data.MemoryProxy = function(data){
11588     if (data.data) {
11589         data = data.data;
11590     }
11591     Roo.data.MemoryProxy.superclass.constructor.call(this);
11592     this.data = data;
11593 };
11594
11595 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11596     
11597     /**
11598      * Load data from the requested source (in this case an in-memory
11599      * data object passed to the constructor), read the data object into
11600      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11601      * process that block using the passed callback.
11602      * @param {Object} params This parameter is not used by the MemoryProxy class.
11603      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11604      * object into a block of Roo.data.Records.
11605      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11606      * The function must be passed <ul>
11607      * <li>The Record block object</li>
11608      * <li>The "arg" argument from the load function</li>
11609      * <li>A boolean success indicator</li>
11610      * </ul>
11611      * @param {Object} scope The scope in which to call the callback
11612      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11613      */
11614     load : function(params, reader, callback, scope, arg){
11615         params = params || {};
11616         var result;
11617         try {
11618             result = reader.readRecords(this.data);
11619         }catch(e){
11620             this.fireEvent("loadexception", this, arg, null, e);
11621             callback.call(scope, null, arg, false);
11622             return;
11623         }
11624         callback.call(scope, result, arg, true);
11625     },
11626     
11627     // private
11628     update : function(params, records){
11629         
11630     }
11631 });/*
11632  * Based on:
11633  * Ext JS Library 1.1.1
11634  * Copyright(c) 2006-2007, Ext JS, LLC.
11635  *
11636  * Originally Released Under LGPL - original licence link has changed is not relivant.
11637  *
11638  * Fork - LGPL
11639  * <script type="text/javascript">
11640  */
11641 /**
11642  * @class Roo.data.HttpProxy
11643  * @extends Roo.data.DataProxy
11644  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11645  * configured to reference a certain URL.<br><br>
11646  * <p>
11647  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11648  * from which the running page was served.<br><br>
11649  * <p>
11650  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11651  * <p>
11652  * Be aware that to enable the browser to parse an XML document, the server must set
11653  * the Content-Type header in the HTTP response to "text/xml".
11654  * @constructor
11655  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11656  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11657  * will be used to make the request.
11658  */
11659 Roo.data.HttpProxy = function(conn){
11660     Roo.data.HttpProxy.superclass.constructor.call(this);
11661     // is conn a conn config or a real conn?
11662     this.conn = conn;
11663     this.useAjax = !conn || !conn.events;
11664   
11665 };
11666
11667 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11668     // thse are take from connection...
11669     
11670     /**
11671      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11672      */
11673     /**
11674      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11675      * extra parameters to each request made by this object. (defaults to undefined)
11676      */
11677     /**
11678      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11679      *  to each request made by this object. (defaults to undefined)
11680      */
11681     /**
11682      * @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)
11683      */
11684     /**
11685      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11686      */
11687      /**
11688      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11689      * @type Boolean
11690      */
11691   
11692
11693     /**
11694      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11695      * @type Boolean
11696      */
11697     /**
11698      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11699      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11700      * a finer-grained basis than the DataProxy events.
11701      */
11702     getConnection : function(){
11703         return this.useAjax ? Roo.Ajax : this.conn;
11704     },
11705
11706     /**
11707      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11708      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11709      * process that block using the passed callback.
11710      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11711      * for the request to the remote server.
11712      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11713      * object into a block of Roo.data.Records.
11714      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11715      * The function must be passed <ul>
11716      * <li>The Record block object</li>
11717      * <li>The "arg" argument from the load function</li>
11718      * <li>A boolean success indicator</li>
11719      * </ul>
11720      * @param {Object} scope The scope in which to call the callback
11721      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11722      */
11723     load : function(params, reader, callback, scope, arg){
11724         if(this.fireEvent("beforeload", this, params) !== false){
11725             var  o = {
11726                 params : params || {},
11727                 request: {
11728                     callback : callback,
11729                     scope : scope,
11730                     arg : arg
11731                 },
11732                 reader: reader,
11733                 callback : this.loadResponse,
11734                 scope: this
11735             };
11736             if(this.useAjax){
11737                 Roo.applyIf(o, this.conn);
11738                 if(this.activeRequest){
11739                     Roo.Ajax.abort(this.activeRequest);
11740                 }
11741                 this.activeRequest = Roo.Ajax.request(o);
11742             }else{
11743                 this.conn.request(o);
11744             }
11745         }else{
11746             callback.call(scope||this, null, arg, false);
11747         }
11748     },
11749
11750     // private
11751     loadResponse : function(o, success, response){
11752         delete this.activeRequest;
11753         if(!success){
11754             this.fireEvent("loadexception", this, o, response);
11755             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11756             return;
11757         }
11758         var result;
11759         try {
11760             result = o.reader.read(response);
11761         }catch(e){
11762             this.fireEvent("loadexception", this, o, response, e);
11763             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11764             return;
11765         }
11766         
11767         this.fireEvent("load", this, o, o.request.arg);
11768         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11769     },
11770
11771     // private
11772     update : function(dataSet){
11773
11774     },
11775
11776     // private
11777     updateResponse : function(dataSet){
11778
11779     }
11780 });/*
11781  * Based on:
11782  * Ext JS Library 1.1.1
11783  * Copyright(c) 2006-2007, Ext JS, LLC.
11784  *
11785  * Originally Released Under LGPL - original licence link has changed is not relivant.
11786  *
11787  * Fork - LGPL
11788  * <script type="text/javascript">
11789  */
11790
11791 /**
11792  * @class Roo.data.ScriptTagProxy
11793  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11794  * other than the originating domain of the running page.<br><br>
11795  * <p>
11796  * <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
11797  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11798  * <p>
11799  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11800  * source code that is used as the source inside a &lt;script> tag.<br><br>
11801  * <p>
11802  * In order for the browser to process the returned data, the server must wrap the data object
11803  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11804  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11805  * depending on whether the callback name was passed:
11806  * <p>
11807  * <pre><code>
11808 boolean scriptTag = false;
11809 String cb = request.getParameter("callback");
11810 if (cb != null) {
11811     scriptTag = true;
11812     response.setContentType("text/javascript");
11813 } else {
11814     response.setContentType("application/x-json");
11815 }
11816 Writer out = response.getWriter();
11817 if (scriptTag) {
11818     out.write(cb + "(");
11819 }
11820 out.print(dataBlock.toJsonString());
11821 if (scriptTag) {
11822     out.write(");");
11823 }
11824 </pre></code>
11825  *
11826  * @constructor
11827  * @param {Object} config A configuration object.
11828  */
11829 Roo.data.ScriptTagProxy = function(config){
11830     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11831     Roo.apply(this, config);
11832     this.head = document.getElementsByTagName("head")[0];
11833 };
11834
11835 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11836
11837 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11838     /**
11839      * @cfg {String} url The URL from which to request the data object.
11840      */
11841     /**
11842      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11843      */
11844     timeout : 30000,
11845     /**
11846      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11847      * the server the name of the callback function set up by the load call to process the returned data object.
11848      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11849      * javascript output which calls this named function passing the data object as its only parameter.
11850      */
11851     callbackParam : "callback",
11852     /**
11853      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11854      * name to the request.
11855      */
11856     nocache : true,
11857
11858     /**
11859      * Load data from the configured URL, read the data object into
11860      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11861      * process that block using the passed callback.
11862      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11863      * for the request to the remote server.
11864      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11865      * object into a block of Roo.data.Records.
11866      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11867      * The function must be passed <ul>
11868      * <li>The Record block object</li>
11869      * <li>The "arg" argument from the load function</li>
11870      * <li>A boolean success indicator</li>
11871      * </ul>
11872      * @param {Object} scope The scope in which to call the callback
11873      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11874      */
11875     load : function(params, reader, callback, scope, arg){
11876         if(this.fireEvent("beforeload", this, params) !== false){
11877
11878             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11879
11880             var url = this.url;
11881             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11882             if(this.nocache){
11883                 url += "&_dc=" + (new Date().getTime());
11884             }
11885             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11886             var trans = {
11887                 id : transId,
11888                 cb : "stcCallback"+transId,
11889                 scriptId : "stcScript"+transId,
11890                 params : params,
11891                 arg : arg,
11892                 url : url,
11893                 callback : callback,
11894                 scope : scope,
11895                 reader : reader
11896             };
11897             var conn = this;
11898
11899             window[trans.cb] = function(o){
11900                 conn.handleResponse(o, trans);
11901             };
11902
11903             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11904
11905             if(this.autoAbort !== false){
11906                 this.abort();
11907             }
11908
11909             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11910
11911             var script = document.createElement("script");
11912             script.setAttribute("src", url);
11913             script.setAttribute("type", "text/javascript");
11914             script.setAttribute("id", trans.scriptId);
11915             this.head.appendChild(script);
11916
11917             this.trans = trans;
11918         }else{
11919             callback.call(scope||this, null, arg, false);
11920         }
11921     },
11922
11923     // private
11924     isLoading : function(){
11925         return this.trans ? true : false;
11926     },
11927
11928     /**
11929      * Abort the current server request.
11930      */
11931     abort : function(){
11932         if(this.isLoading()){
11933             this.destroyTrans(this.trans);
11934         }
11935     },
11936
11937     // private
11938     destroyTrans : function(trans, isLoaded){
11939         this.head.removeChild(document.getElementById(trans.scriptId));
11940         clearTimeout(trans.timeoutId);
11941         if(isLoaded){
11942             window[trans.cb] = undefined;
11943             try{
11944                 delete window[trans.cb];
11945             }catch(e){}
11946         }else{
11947             // if hasn't been loaded, wait for load to remove it to prevent script error
11948             window[trans.cb] = function(){
11949                 window[trans.cb] = undefined;
11950                 try{
11951                     delete window[trans.cb];
11952                 }catch(e){}
11953             };
11954         }
11955     },
11956
11957     // private
11958     handleResponse : function(o, trans){
11959         this.trans = false;
11960         this.destroyTrans(trans, true);
11961         var result;
11962         try {
11963             result = trans.reader.readRecords(o);
11964         }catch(e){
11965             this.fireEvent("loadexception", this, o, trans.arg, e);
11966             trans.callback.call(trans.scope||window, null, trans.arg, false);
11967             return;
11968         }
11969         this.fireEvent("load", this, o, trans.arg);
11970         trans.callback.call(trans.scope||window, result, trans.arg, true);
11971     },
11972
11973     // private
11974     handleFailure : function(trans){
11975         this.trans = false;
11976         this.destroyTrans(trans, false);
11977         this.fireEvent("loadexception", this, null, trans.arg);
11978         trans.callback.call(trans.scope||window, null, trans.arg, false);
11979     }
11980 });/*
11981  * Based on:
11982  * Ext JS Library 1.1.1
11983  * Copyright(c) 2006-2007, Ext JS, LLC.
11984  *
11985  * Originally Released Under LGPL - original licence link has changed is not relivant.
11986  *
11987  * Fork - LGPL
11988  * <script type="text/javascript">
11989  */
11990
11991 /**
11992  * @class Roo.data.JsonReader
11993  * @extends Roo.data.DataReader
11994  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11995  * based on mappings in a provided Roo.data.Record constructor.
11996  * 
11997  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11998  * in the reply previously. 
11999  * 
12000  * <p>
12001  * Example code:
12002  * <pre><code>
12003 var RecordDef = Roo.data.Record.create([
12004     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12005     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12006 ]);
12007 var myReader = new Roo.data.JsonReader({
12008     totalProperty: "results",    // The property which contains the total dataset size (optional)
12009     root: "rows",                // The property which contains an Array of row objects
12010     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12011 }, RecordDef);
12012 </code></pre>
12013  * <p>
12014  * This would consume a JSON file like this:
12015  * <pre><code>
12016 { 'results': 2, 'rows': [
12017     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12018     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12019 }
12020 </code></pre>
12021  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12022  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12023  * paged from the remote server.
12024  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12025  * @cfg {String} root name of the property which contains the Array of row objects.
12026  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12027  * @cfg {Array} fields Array of field definition objects
12028  * @constructor
12029  * Create a new JsonReader
12030  * @param {Object} meta Metadata configuration options
12031  * @param {Object} recordType Either an Array of field definition objects,
12032  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12033  */
12034 Roo.data.JsonReader = function(meta, recordType){
12035     
12036     meta = meta || {};
12037     // set some defaults:
12038     Roo.applyIf(meta, {
12039         totalProperty: 'total',
12040         successProperty : 'success',
12041         root : 'data',
12042         id : 'id'
12043     });
12044     
12045     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12046 };
12047 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12048     
12049     /**
12050      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12051      * Used by Store query builder to append _requestMeta to params.
12052      * 
12053      */
12054     metaFromRemote : false,
12055     /**
12056      * This method is only used by a DataProxy which has retrieved data from a remote server.
12057      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12058      * @return {Object} data A data block which is used by an Roo.data.Store object as
12059      * a cache of Roo.data.Records.
12060      */
12061     read : function(response){
12062         var json = response.responseText;
12063        
12064         var o = /* eval:var:o */ eval("("+json+")");
12065         if(!o) {
12066             throw {message: "JsonReader.read: Json object not found"};
12067         }
12068         
12069         if(o.metaData){
12070             
12071             delete this.ef;
12072             this.metaFromRemote = true;
12073             this.meta = o.metaData;
12074             this.recordType = Roo.data.Record.create(o.metaData.fields);
12075             this.onMetaChange(this.meta, this.recordType, o);
12076         }
12077         return this.readRecords(o);
12078     },
12079
12080     // private function a store will implement
12081     onMetaChange : function(meta, recordType, o){
12082
12083     },
12084
12085     /**
12086          * @ignore
12087          */
12088     simpleAccess: function(obj, subsc) {
12089         return obj[subsc];
12090     },
12091
12092         /**
12093          * @ignore
12094          */
12095     getJsonAccessor: function(){
12096         var re = /[\[\.]/;
12097         return function(expr) {
12098             try {
12099                 return(re.test(expr))
12100                     ? new Function("obj", "return obj." + expr)
12101                     : function(obj){
12102                         return obj[expr];
12103                     };
12104             } catch(e){}
12105             return Roo.emptyFn;
12106         };
12107     }(),
12108
12109     /**
12110      * Create a data block containing Roo.data.Records from an XML document.
12111      * @param {Object} o An object which contains an Array of row objects in the property specified
12112      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12113      * which contains the total size of the dataset.
12114      * @return {Object} data A data block which is used by an Roo.data.Store object as
12115      * a cache of Roo.data.Records.
12116      */
12117     readRecords : function(o){
12118         /**
12119          * After any data loads, the raw JSON data is available for further custom processing.
12120          * @type Object
12121          */
12122         this.o = o;
12123         var s = this.meta, Record = this.recordType,
12124             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12125
12126 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12127         if (!this.ef) {
12128             if(s.totalProperty) {
12129                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12130                 }
12131                 if(s.successProperty) {
12132                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12133                 }
12134                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12135                 if (s.id) {
12136                         var g = this.getJsonAccessor(s.id);
12137                         this.getId = function(rec) {
12138                                 var r = g(rec);  
12139                                 return (r === undefined || r === "") ? null : r;
12140                         };
12141                 } else {
12142                         this.getId = function(){return null;};
12143                 }
12144             this.ef = [];
12145             for(var jj = 0; jj < fl; jj++){
12146                 f = fi[jj];
12147                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12148                 this.ef[jj] = this.getJsonAccessor(map);
12149             }
12150         }
12151
12152         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12153         if(s.totalProperty){
12154             var vt = parseInt(this.getTotal(o), 10);
12155             if(!isNaN(vt)){
12156                 totalRecords = vt;
12157             }
12158         }
12159         if(s.successProperty){
12160             var vs = this.getSuccess(o);
12161             if(vs === false || vs === 'false'){
12162                 success = false;
12163             }
12164         }
12165         var records = [];
12166         for(var i = 0; i < c; i++){
12167                 var n = root[i];
12168             var values = {};
12169             var id = this.getId(n);
12170             for(var j = 0; j < fl; j++){
12171                 f = fi[j];
12172             var v = this.ef[j](n);
12173             if (!f.convert) {
12174                 Roo.log('missing convert for ' + f.name);
12175                 Roo.log(f);
12176                 continue;
12177             }
12178             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12179             }
12180             var record = new Record(values, id);
12181             record.json = n;
12182             records[i] = record;
12183         }
12184         return {
12185             raw : o,
12186             success : success,
12187             records : records,
12188             totalRecords : totalRecords
12189         };
12190     }
12191 });/*
12192  * Based on:
12193  * Ext JS Library 1.1.1
12194  * Copyright(c) 2006-2007, Ext JS, LLC.
12195  *
12196  * Originally Released Under LGPL - original licence link has changed is not relivant.
12197  *
12198  * Fork - LGPL
12199  * <script type="text/javascript">
12200  */
12201
12202 /**
12203  * @class Roo.data.ArrayReader
12204  * @extends Roo.data.DataReader
12205  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12206  * Each element of that Array represents a row of data fields. The
12207  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12208  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12209  * <p>
12210  * Example code:.
12211  * <pre><code>
12212 var RecordDef = Roo.data.Record.create([
12213     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12214     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12215 ]);
12216 var myReader = new Roo.data.ArrayReader({
12217     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12218 }, RecordDef);
12219 </code></pre>
12220  * <p>
12221  * This would consume an Array like this:
12222  * <pre><code>
12223 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12224   </code></pre>
12225  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12226  * @constructor
12227  * Create a new JsonReader
12228  * @param {Object} meta Metadata configuration options.
12229  * @param {Object} recordType Either an Array of field definition objects
12230  * as specified to {@link Roo.data.Record#create},
12231  * or an {@link Roo.data.Record} object
12232  * created using {@link Roo.data.Record#create}.
12233  */
12234 Roo.data.ArrayReader = function(meta, recordType){
12235     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12236 };
12237
12238 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12239     /**
12240      * Create a data block containing Roo.data.Records from an XML document.
12241      * @param {Object} o An Array of row objects which represents the dataset.
12242      * @return {Object} data A data block which is used by an Roo.data.Store object as
12243      * a cache of Roo.data.Records.
12244      */
12245     readRecords : function(o){
12246         var sid = this.meta ? this.meta.id : null;
12247         var recordType = this.recordType, fields = recordType.prototype.fields;
12248         var records = [];
12249         var root = o;
12250             for(var i = 0; i < root.length; i++){
12251                     var n = root[i];
12252                 var values = {};
12253                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12254                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12255                 var f = fields.items[j];
12256                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12257                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12258                 v = f.convert(v);
12259                 values[f.name] = v;
12260             }
12261                 var record = new recordType(values, id);
12262                 record.json = n;
12263                 records[records.length] = record;
12264             }
12265             return {
12266                 records : records,
12267                 totalRecords : records.length
12268             };
12269     }
12270 });/*
12271  * - LGPL
12272  * * 
12273  */
12274
12275 /**
12276  * @class Roo.bootstrap.ComboBox
12277  * @extends Roo.bootstrap.TriggerField
12278  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12279  * @cfg {Boolean} append (true|false) default false
12280  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12281  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12282  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12283  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12284  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12285  * @cfg {Boolean} animate default true
12286  * @cfg {Boolean} emptyResultText only for touch device
12287  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12288  * @constructor
12289  * Create a new ComboBox.
12290  * @param {Object} config Configuration options
12291  */
12292 Roo.bootstrap.ComboBox = function(config){
12293     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12294     this.addEvents({
12295         /**
12296          * @event expand
12297          * Fires when the dropdown list is expanded
12298              * @param {Roo.bootstrap.ComboBox} combo This combo box
12299              */
12300         'expand' : true,
12301         /**
12302          * @event collapse
12303          * Fires when the dropdown list is collapsed
12304              * @param {Roo.bootstrap.ComboBox} combo This combo box
12305              */
12306         'collapse' : true,
12307         /**
12308          * @event beforeselect
12309          * Fires before a list item is selected. Return false to cancel the selection.
12310              * @param {Roo.bootstrap.ComboBox} combo This combo box
12311              * @param {Roo.data.Record} record The data record returned from the underlying store
12312              * @param {Number} index The index of the selected item in the dropdown list
12313              */
12314         'beforeselect' : true,
12315         /**
12316          * @event select
12317          * Fires when a list item is selected
12318              * @param {Roo.bootstrap.ComboBox} combo This combo box
12319              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12320              * @param {Number} index The index of the selected item in the dropdown list
12321              */
12322         'select' : true,
12323         /**
12324          * @event beforequery
12325          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12326          * The event object passed has these properties:
12327              * @param {Roo.bootstrap.ComboBox} combo This combo box
12328              * @param {String} query The query
12329              * @param {Boolean} forceAll true to force "all" query
12330              * @param {Boolean} cancel true to cancel the query
12331              * @param {Object} e The query event object
12332              */
12333         'beforequery': true,
12334          /**
12335          * @event add
12336          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12337              * @param {Roo.bootstrap.ComboBox} combo This combo box
12338              */
12339         'add' : true,
12340         /**
12341          * @event edit
12342          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12343              * @param {Roo.bootstrap.ComboBox} combo This combo box
12344              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12345              */
12346         'edit' : true,
12347         /**
12348          * @event remove
12349          * Fires when the remove value from the combobox array
12350              * @param {Roo.bootstrap.ComboBox} combo This combo box
12351              */
12352         'remove' : true,
12353         /**
12354          * @event afterremove
12355          * Fires when the remove value from the combobox array
12356              * @param {Roo.bootstrap.ComboBox} combo This combo box
12357              */
12358         'afterremove' : true,
12359         /**
12360          * @event specialfilter
12361          * Fires when specialfilter
12362             * @param {Roo.bootstrap.ComboBox} combo This combo box
12363             */
12364         'specialfilter' : true,
12365         /**
12366          * @event tick
12367          * Fires when tick the element
12368             * @param {Roo.bootstrap.ComboBox} combo This combo box
12369             */
12370         'tick' : true,
12371         /**
12372          * @event touchviewdisplay
12373          * Fires when touch view require special display (default is using displayField)
12374             * @param {Roo.bootstrap.ComboBox} combo This combo box
12375             * @param {Object} cfg set html .
12376             */
12377         'touchviewdisplay' : true
12378         
12379     });
12380     
12381     this.item = [];
12382     this.tickItems = [];
12383     
12384     this.selectedIndex = -1;
12385     if(this.mode == 'local'){
12386         if(config.queryDelay === undefined){
12387             this.queryDelay = 10;
12388         }
12389         if(config.minChars === undefined){
12390             this.minChars = 0;
12391         }
12392     }
12393 };
12394
12395 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12396      
12397     /**
12398      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12399      * rendering into an Roo.Editor, defaults to false)
12400      */
12401     /**
12402      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12403      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12404      */
12405     /**
12406      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12407      */
12408     /**
12409      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12410      * the dropdown list (defaults to undefined, with no header element)
12411      */
12412
12413      /**
12414      * @cfg {String/Roo.Template} tpl The template to use to render the output
12415      */
12416      
12417      /**
12418      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12419      */
12420     listWidth: undefined,
12421     /**
12422      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12423      * mode = 'remote' or 'text' if mode = 'local')
12424      */
12425     displayField: undefined,
12426     
12427     /**
12428      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12429      * mode = 'remote' or 'value' if mode = 'local'). 
12430      * Note: use of a valueField requires the user make a selection
12431      * in order for a value to be mapped.
12432      */
12433     valueField: undefined,
12434     /**
12435      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12436      */
12437     modalTitle : '',
12438     
12439     /**
12440      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12441      * field's data value (defaults to the underlying DOM element's name)
12442      */
12443     hiddenName: undefined,
12444     /**
12445      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12446      */
12447     listClass: '',
12448     /**
12449      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12450      */
12451     selectedClass: 'active',
12452     
12453     /**
12454      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12455      */
12456     shadow:'sides',
12457     /**
12458      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12459      * anchor positions (defaults to 'tl-bl')
12460      */
12461     listAlign: 'tl-bl?',
12462     /**
12463      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12464      */
12465     maxHeight: 300,
12466     /**
12467      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12468      * query specified by the allQuery config option (defaults to 'query')
12469      */
12470     triggerAction: 'query',
12471     /**
12472      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12473      * (defaults to 4, does not apply if editable = false)
12474      */
12475     minChars : 4,
12476     /**
12477      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12478      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12479      */
12480     typeAhead: false,
12481     /**
12482      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12483      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12484      */
12485     queryDelay: 500,
12486     /**
12487      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12488      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12489      */
12490     pageSize: 0,
12491     /**
12492      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12493      * when editable = true (defaults to false)
12494      */
12495     selectOnFocus:false,
12496     /**
12497      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12498      */
12499     queryParam: 'query',
12500     /**
12501      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12502      * when mode = 'remote' (defaults to 'Loading...')
12503      */
12504     loadingText: 'Loading...',
12505     /**
12506      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12507      */
12508     resizable: false,
12509     /**
12510      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12511      */
12512     handleHeight : 8,
12513     /**
12514      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12515      * traditional select (defaults to true)
12516      */
12517     editable: true,
12518     /**
12519      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12520      */
12521     allQuery: '',
12522     /**
12523      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12524      */
12525     mode: 'remote',
12526     /**
12527      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12528      * listWidth has a higher value)
12529      */
12530     minListWidth : 70,
12531     /**
12532      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12533      * allow the user to set arbitrary text into the field (defaults to false)
12534      */
12535     forceSelection:false,
12536     /**
12537      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12538      * if typeAhead = true (defaults to 250)
12539      */
12540     typeAheadDelay : 250,
12541     /**
12542      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12543      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12544      */
12545     valueNotFoundText : undefined,
12546     /**
12547      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12548      */
12549     blockFocus : false,
12550     
12551     /**
12552      * @cfg {Boolean} disableClear Disable showing of clear button.
12553      */
12554     disableClear : false,
12555     /**
12556      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12557      */
12558     alwaysQuery : false,
12559     
12560     /**
12561      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12562      */
12563     multiple : false,
12564     
12565     /**
12566      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12567      */
12568     invalidClass : "has-warning",
12569     
12570     /**
12571      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12572      */
12573     validClass : "has-success",
12574     
12575     /**
12576      * @cfg {Boolean} specialFilter (true|false) special filter default false
12577      */
12578     specialFilter : false,
12579     
12580     /**
12581      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12582      */
12583     mobileTouchView : true,
12584     
12585     /**
12586      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12587      */
12588     useNativeIOS : false,
12589     
12590     ios_options : false,
12591     
12592     //private
12593     addicon : false,
12594     editicon: false,
12595     
12596     page: 0,
12597     hasQuery: false,
12598     append: false,
12599     loadNext: false,
12600     autoFocus : true,
12601     tickable : false,
12602     btnPosition : 'right',
12603     triggerList : true,
12604     showToggleBtn : true,
12605     animate : true,
12606     emptyResultText: 'Empty',
12607     triggerText : 'Select',
12608     
12609     // element that contains real text value.. (when hidden is used..)
12610     
12611     getAutoCreate : function()
12612     {   
12613         var cfg = false;
12614         //render
12615         /*
12616          * Render classic select for iso
12617          */
12618         
12619         if(Roo.isIOS && this.useNativeIOS){
12620             cfg = this.getAutoCreateNativeIOS();
12621             return cfg;
12622         }
12623         
12624         /*
12625          * Touch Devices
12626          */
12627         
12628         if(Roo.isTouch && this.mobileTouchView){
12629             cfg = this.getAutoCreateTouchView();
12630             return cfg;;
12631         }
12632         
12633         /*
12634          *  Normal ComboBox
12635          */
12636         if(!this.tickable){
12637             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12638             if(this.name == 'info_year_invest_id_display_name'){
12639                 Roo.log('cfg.................................................');
12640                 Roo.log(cfg);
12641             }
12642             return cfg;
12643         }
12644         
12645         /*
12646          *  ComboBox with tickable selections
12647          */
12648              
12649         var align = this.labelAlign || this.parentLabelAlign();
12650         
12651         cfg = {
12652             cls : 'form-group roo-combobox-tickable' //input-group
12653         };
12654         
12655         var btn_text_select = '';
12656         var btn_text_done = '';
12657         var btn_text_cancel = '';
12658         
12659         if (this.btn_text_show) {
12660             btn_text_select = 'Select';
12661             btn_text_done = 'Done';
12662             btn_text_cancel = 'Cancel'; 
12663         }
12664         
12665         var buttons = {
12666             tag : 'div',
12667             cls : 'tickable-buttons',
12668             cn : [
12669                 {
12670                     tag : 'button',
12671                     type : 'button',
12672                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12673                     //html : this.triggerText
12674                     html: btn_text_select
12675                 },
12676                 {
12677                     tag : 'button',
12678                     type : 'button',
12679                     name : 'ok',
12680                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12681                     //html : 'Done'
12682                     html: btn_text_done
12683                 },
12684                 {
12685                     tag : 'button',
12686                     type : 'button',
12687                     name : 'cancel',
12688                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12689                     //html : 'Cancel'
12690                     html: btn_text_cancel
12691                 }
12692             ]
12693         };
12694         
12695         if(this.editable){
12696             buttons.cn.unshift({
12697                 tag: 'input',
12698                 cls: 'roo-select2-search-field-input'
12699             });
12700         }
12701         
12702         var _this = this;
12703         
12704         Roo.each(buttons.cn, function(c){
12705             if (_this.size) {
12706                 c.cls += ' btn-' + _this.size;
12707             }
12708
12709             if (_this.disabled) {
12710                 c.disabled = true;
12711             }
12712         });
12713         
12714         var box = {
12715             tag: 'div',
12716             cn: [
12717                 {
12718                     tag: 'input',
12719                     type : 'hidden',
12720                     cls: 'form-hidden-field'
12721                 },
12722                 {
12723                     tag: 'ul',
12724                     cls: 'roo-select2-choices',
12725                     cn:[
12726                         {
12727                             tag: 'li',
12728                             cls: 'roo-select2-search-field',
12729                             cn: [
12730                                 buttons
12731                             ]
12732                         }
12733                     ]
12734                 }
12735             ]
12736         };
12737         
12738         var combobox = {
12739             cls: 'roo-select2-container input-group roo-select2-container-multi',
12740             cn: [
12741                 box
12742 //                {
12743 //                    tag: 'ul',
12744 //                    cls: 'typeahead typeahead-long dropdown-menu',
12745 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12746 //                }
12747             ]
12748         };
12749         
12750         if(this.hasFeedback && !this.allowBlank){
12751             
12752             var feedback = {
12753                 tag: 'span',
12754                 cls: 'glyphicon form-control-feedback'
12755             };
12756
12757             combobox.cn.push(feedback);
12758         }
12759         
12760         
12761         if (align ==='left' && this.fieldLabel.length) {
12762             
12763             cfg.cls += ' roo-form-group-label-left';
12764             
12765             cfg.cn = [
12766                 {
12767                     tag : 'i',
12768                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12769                     tooltip : 'This field is required'
12770                 },
12771                 {
12772                     tag: 'label',
12773                     'for' :  id,
12774                     cls : 'control-label',
12775                     html : this.fieldLabel
12776
12777                 },
12778                 {
12779                     cls : "", 
12780                     cn: [
12781                         combobox
12782                     ]
12783                 }
12784
12785             ];
12786             
12787             var labelCfg = cfg.cn[1];
12788             var contentCfg = cfg.cn[2];
12789             
12790
12791             if(this.indicatorpos == 'right'){
12792                 
12793                 cfg.cn = [
12794                     {
12795                         tag: 'label',
12796                         'for' :  id,
12797                         cls : 'control-label',
12798                         cn : [
12799                             {
12800                                 tag : 'span',
12801                                 html : this.fieldLabel
12802                             },
12803                             {
12804                                 tag : 'i',
12805                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12806                                 tooltip : 'This field is required'
12807                             }
12808                         ]
12809                     },
12810                     {
12811                         cls : "",
12812                         cn: [
12813                             combobox
12814                         ]
12815                     }
12816
12817                 ];
12818                 
12819                 
12820                 
12821                 labelCfg = cfg.cn[0];
12822                 contentCfg = cfg.cn[1];
12823             
12824             }
12825             
12826             if(this.labelWidth > 12){
12827                 labelCfg.style = "width: " + this.labelWidth + 'px';
12828             }
12829             
12830             if(this.labelWidth < 13 && this.labelmd == 0){
12831                 this.labelmd = this.labelWidth;
12832             }
12833             
12834             if(this.labellg > 0){
12835                 labelCfg.cls += ' col-lg-' + this.labellg;
12836                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12837             }
12838             
12839             if(this.labelmd > 0){
12840                 labelCfg.cls += ' col-md-' + this.labelmd;
12841                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12842             }
12843             
12844             if(this.labelsm > 0){
12845                 labelCfg.cls += ' col-sm-' + this.labelsm;
12846                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12847             }
12848             
12849             if(this.labelxs > 0){
12850                 labelCfg.cls += ' col-xs-' + this.labelxs;
12851                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12852             }
12853                 
12854                 
12855         } else if ( this.fieldLabel.length) {
12856 //                Roo.log(" label");
12857                  cfg.cn = [
12858                     {
12859                         tag : 'i',
12860                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12861                         tooltip : 'This field is required'
12862                     },
12863                     {
12864                         tag: 'label',
12865                         //cls : 'input-group-addon',
12866                         html : this.fieldLabel
12867                         
12868                     },
12869                     
12870                     combobox
12871                     
12872                 ];
12873                 
12874                 if(this.indicatorpos == 'right'){
12875                     
12876                     cfg.cn = [
12877                         {
12878                             tag: 'label',
12879                             //cls : 'input-group-addon',
12880                             cn : [
12881                                 {
12882                                     tag : 'span',
12883                                     html : this.fieldLabel
12884                                 },
12885                                 {
12886                                     tag : 'i',
12887                                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12888                                     tooltip : 'This field is required'
12889                                 }
12890                             ]
12891                         },
12892                         
12893                         
12894                         
12895                         combobox
12896
12897                     ];
12898                 
12899                 }
12900
12901         } else {
12902             
12903 //                Roo.log(" no label && no align");
12904                 cfg = combobox
12905                      
12906                 
12907         }
12908          
12909         var settings=this;
12910         ['xs','sm','md','lg'].map(function(size){
12911             if (settings[size]) {
12912                 cfg.cls += ' col-' + size + '-' + settings[size];
12913             }
12914         });
12915         
12916         return cfg;
12917         
12918     },
12919     
12920     _initEventsCalled : false,
12921     
12922     // private
12923     initEvents: function()
12924     {   
12925         if (this._initEventsCalled) { // as we call render... prevent looping...
12926             return;
12927         }
12928         this._initEventsCalled = true;
12929         
12930         if (!this.store) {
12931             throw "can not find store for combo";
12932         }
12933         
12934         this.store = Roo.factory(this.store, Roo.data);
12935         
12936         // if we are building from html. then this element is so complex, that we can not really
12937         // use the rendered HTML.
12938         // so we have to trash and replace the previous code.
12939         if (Roo.XComponent.build_from_html) {
12940             
12941             // remove this element....
12942             var e = this.el.dom, k=0;
12943             while (e ) { e = e.previousSibling;  ++k;}
12944
12945             this.el.remove();
12946             
12947             this.el=false;
12948             this.rendered = false;
12949             
12950             this.render(this.parent().getChildContainer(true), k);
12951             
12952             
12953             
12954         }
12955         
12956         if(Roo.isIOS && this.useNativeIOS){
12957             this.initIOSView();
12958             return;
12959         }
12960         
12961         /*
12962          * Touch Devices
12963          */
12964         
12965         if(Roo.isTouch && this.mobileTouchView){
12966             this.initTouchView();
12967             return;
12968         }
12969         
12970         if(this.tickable){
12971             this.initTickableEvents();
12972             return;
12973         }
12974         
12975         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12976         
12977         if(this.hiddenName){
12978             
12979             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12980             
12981             this.hiddenField.dom.value =
12982                 this.hiddenValue !== undefined ? this.hiddenValue :
12983                 this.value !== undefined ? this.value : '';
12984
12985             // prevent input submission
12986             this.el.dom.removeAttribute('name');
12987             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12988              
12989              
12990         }
12991         //if(Roo.isGecko){
12992         //    this.el.dom.setAttribute('autocomplete', 'off');
12993         //}
12994         
12995         var cls = 'x-combo-list';
12996         
12997         //this.list = new Roo.Layer({
12998         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12999         //});
13000         
13001         var _this = this;
13002         
13003         (function(){
13004             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13005             _this.list.setWidth(lw);
13006         }).defer(100);
13007         
13008         this.list.on('mouseover', this.onViewOver, this);
13009         this.list.on('mousemove', this.onViewMove, this);
13010         
13011         this.list.on('scroll', this.onViewScroll, this);
13012         
13013         /*
13014         this.list.swallowEvent('mousewheel');
13015         this.assetHeight = 0;
13016
13017         if(this.title){
13018             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13019             this.assetHeight += this.header.getHeight();
13020         }
13021
13022         this.innerList = this.list.createChild({cls:cls+'-inner'});
13023         this.innerList.on('mouseover', this.onViewOver, this);
13024         this.innerList.on('mousemove', this.onViewMove, this);
13025         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13026         
13027         if(this.allowBlank && !this.pageSize && !this.disableClear){
13028             this.footer = this.list.createChild({cls:cls+'-ft'});
13029             this.pageTb = new Roo.Toolbar(this.footer);
13030            
13031         }
13032         if(this.pageSize){
13033             this.footer = this.list.createChild({cls:cls+'-ft'});
13034             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13035                     {pageSize: this.pageSize});
13036             
13037         }
13038         
13039         if (this.pageTb && this.allowBlank && !this.disableClear) {
13040             var _this = this;
13041             this.pageTb.add(new Roo.Toolbar.Fill(), {
13042                 cls: 'x-btn-icon x-btn-clear',
13043                 text: '&#160;',
13044                 handler: function()
13045                 {
13046                     _this.collapse();
13047                     _this.clearValue();
13048                     _this.onSelect(false, -1);
13049                 }
13050             });
13051         }
13052         if (this.footer) {
13053             this.assetHeight += this.footer.getHeight();
13054         }
13055         */
13056             
13057         if(!this.tpl){
13058             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13059         }
13060
13061         this.view = new Roo.View(this.list, this.tpl, {
13062             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13063         });
13064         //this.view.wrapEl.setDisplayed(false);
13065         this.view.on('click', this.onViewClick, this);
13066         
13067         
13068         
13069         this.store.on('beforeload', this.onBeforeLoad, this);
13070         this.store.on('load', this.onLoad, this);
13071         this.store.on('loadexception', this.onLoadException, this);
13072         /*
13073         if(this.resizable){
13074             this.resizer = new Roo.Resizable(this.list,  {
13075                pinned:true, handles:'se'
13076             });
13077             this.resizer.on('resize', function(r, w, h){
13078                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13079                 this.listWidth = w;
13080                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13081                 this.restrictHeight();
13082             }, this);
13083             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13084         }
13085         */
13086         if(!this.editable){
13087             this.editable = true;
13088             this.setEditable(false);
13089         }
13090         
13091         /*
13092         
13093         if (typeof(this.events.add.listeners) != 'undefined') {
13094             
13095             this.addicon = this.wrap.createChild(
13096                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13097        
13098             this.addicon.on('click', function(e) {
13099                 this.fireEvent('add', this);
13100             }, this);
13101         }
13102         if (typeof(this.events.edit.listeners) != 'undefined') {
13103             
13104             this.editicon = this.wrap.createChild(
13105                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13106             if (this.addicon) {
13107                 this.editicon.setStyle('margin-left', '40px');
13108             }
13109             this.editicon.on('click', function(e) {
13110                 
13111                 // we fire even  if inothing is selected..
13112                 this.fireEvent('edit', this, this.lastData );
13113                 
13114             }, this);
13115         }
13116         */
13117         
13118         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13119             "up" : function(e){
13120                 this.inKeyMode = true;
13121                 this.selectPrev();
13122             },
13123
13124             "down" : function(e){
13125                 if(!this.isExpanded()){
13126                     this.onTriggerClick();
13127                 }else{
13128                     this.inKeyMode = true;
13129                     this.selectNext();
13130                 }
13131             },
13132
13133             "enter" : function(e){
13134 //                this.onViewClick();
13135                 //return true;
13136                 this.collapse();
13137                 
13138                 if(this.fireEvent("specialkey", this, e)){
13139                     this.onViewClick(false);
13140                 }
13141                 
13142                 return true;
13143             },
13144
13145             "esc" : function(e){
13146                 this.collapse();
13147             },
13148
13149             "tab" : function(e){
13150                 this.collapse();
13151                 
13152                 if(this.fireEvent("specialkey", this, e)){
13153                     this.onViewClick(false);
13154                 }
13155                 
13156                 return true;
13157             },
13158
13159             scope : this,
13160
13161             doRelay : function(foo, bar, hname){
13162                 if(hname == 'down' || this.scope.isExpanded()){
13163                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13164                 }
13165                 return true;
13166             },
13167
13168             forceKeyDown: true
13169         });
13170         
13171         
13172         this.queryDelay = Math.max(this.queryDelay || 10,
13173                 this.mode == 'local' ? 10 : 250);
13174         
13175         
13176         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13177         
13178         if(this.typeAhead){
13179             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13180         }
13181         if(this.editable !== false){
13182             this.inputEl().on("keyup", this.onKeyUp, this);
13183         }
13184         if(this.forceSelection){
13185             this.inputEl().on('blur', this.doForce, this);
13186         }
13187         
13188         if(this.multiple){
13189             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13190             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13191         }
13192     },
13193     
13194     initTickableEvents: function()
13195     {   
13196         this.createList();
13197         
13198         if(this.hiddenName){
13199             
13200             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13201             
13202             this.hiddenField.dom.value =
13203                 this.hiddenValue !== undefined ? this.hiddenValue :
13204                 this.value !== undefined ? this.value : '';
13205
13206             // prevent input submission
13207             this.el.dom.removeAttribute('name');
13208             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13209              
13210              
13211         }
13212         
13213 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13214         
13215         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13216         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13217         if(this.triggerList){
13218             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13219         }
13220          
13221         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13222         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13223         
13224         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13225         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13226         
13227         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13228         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13229         
13230         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13231         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13232         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13233         
13234         this.okBtn.hide();
13235         this.cancelBtn.hide();
13236         
13237         var _this = this;
13238         
13239         (function(){
13240             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13241             _this.list.setWidth(lw);
13242         }).defer(100);
13243         
13244         this.list.on('mouseover', this.onViewOver, this);
13245         this.list.on('mousemove', this.onViewMove, this);
13246         
13247         this.list.on('scroll', this.onViewScroll, this);
13248         
13249         if(!this.tpl){
13250             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></div></li>';
13251         }
13252
13253         this.view = new Roo.View(this.list, this.tpl, {
13254             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13255         });
13256         
13257         //this.view.wrapEl.setDisplayed(false);
13258         this.view.on('click', this.onViewClick, this);
13259         
13260         
13261         
13262         this.store.on('beforeload', this.onBeforeLoad, this);
13263         this.store.on('load', this.onLoad, this);
13264         this.store.on('loadexception', this.onLoadException, this);
13265         
13266         if(this.editable){
13267             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13268                 "up" : function(e){
13269                     this.inKeyMode = true;
13270                     this.selectPrev();
13271                 },
13272
13273                 "down" : function(e){
13274                     this.inKeyMode = true;
13275                     this.selectNext();
13276                 },
13277
13278                 "enter" : function(e){
13279                     if(this.fireEvent("specialkey", this, e)){
13280                         this.onViewClick(false);
13281                     }
13282                     
13283                     return true;
13284                 },
13285
13286                 "esc" : function(e){
13287                     this.onTickableFooterButtonClick(e, false, false);
13288                 },
13289
13290                 "tab" : function(e){
13291                     this.fireEvent("specialkey", this, e);
13292                     
13293                     this.onTickableFooterButtonClick(e, false, false);
13294                     
13295                     return true;
13296                 },
13297
13298                 scope : this,
13299
13300                 doRelay : function(e, fn, key){
13301                     if(this.scope.isExpanded()){
13302                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13303                     }
13304                     return true;
13305                 },
13306
13307                 forceKeyDown: true
13308             });
13309         }
13310         
13311         this.queryDelay = Math.max(this.queryDelay || 10,
13312                 this.mode == 'local' ? 10 : 250);
13313         
13314         
13315         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13316         
13317         if(this.typeAhead){
13318             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13319         }
13320         
13321         if(this.editable !== false){
13322             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13323         }
13324         
13325         this.indicator = this.indicatorEl();
13326         
13327         if(this.indicator){
13328             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13329             this.indicator.hide();
13330         }
13331         
13332     },
13333
13334     onDestroy : function(){
13335         if(this.view){
13336             this.view.setStore(null);
13337             this.view.el.removeAllListeners();
13338             this.view.el.remove();
13339             this.view.purgeListeners();
13340         }
13341         if(this.list){
13342             this.list.dom.innerHTML  = '';
13343         }
13344         
13345         if(this.store){
13346             this.store.un('beforeload', this.onBeforeLoad, this);
13347             this.store.un('load', this.onLoad, this);
13348             this.store.un('loadexception', this.onLoadException, this);
13349         }
13350         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13351     },
13352
13353     // private
13354     fireKey : function(e){
13355         if(e.isNavKeyPress() && !this.list.isVisible()){
13356             this.fireEvent("specialkey", this, e);
13357         }
13358     },
13359
13360     // private
13361     onResize: function(w, h){
13362 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13363 //        
13364 //        if(typeof w != 'number'){
13365 //            // we do not handle it!?!?
13366 //            return;
13367 //        }
13368 //        var tw = this.trigger.getWidth();
13369 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13370 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13371 //        var x = w - tw;
13372 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13373 //            
13374 //        //this.trigger.setStyle('left', x+'px');
13375 //        
13376 //        if(this.list && this.listWidth === undefined){
13377 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13378 //            this.list.setWidth(lw);
13379 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13380 //        }
13381         
13382     
13383         
13384     },
13385
13386     /**
13387      * Allow or prevent the user from directly editing the field text.  If false is passed,
13388      * the user will only be able to select from the items defined in the dropdown list.  This method
13389      * is the runtime equivalent of setting the 'editable' config option at config time.
13390      * @param {Boolean} value True to allow the user to directly edit the field text
13391      */
13392     setEditable : function(value){
13393         if(value == this.editable){
13394             return;
13395         }
13396         this.editable = value;
13397         if(!value){
13398             this.inputEl().dom.setAttribute('readOnly', true);
13399             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13400             this.inputEl().addClass('x-combo-noedit');
13401         }else{
13402             this.inputEl().dom.setAttribute('readOnly', false);
13403             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13404             this.inputEl().removeClass('x-combo-noedit');
13405         }
13406     },
13407
13408     // private
13409     
13410     onBeforeLoad : function(combo,opts){
13411         if(!this.hasFocus){
13412             return;
13413         }
13414          if (!opts.add) {
13415             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13416          }
13417         this.restrictHeight();
13418         this.selectedIndex = -1;
13419     },
13420
13421     // private
13422     onLoad : function(){
13423         
13424         this.hasQuery = false;
13425         
13426         if(!this.hasFocus){
13427             return;
13428         }
13429         
13430         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13431             this.loading.hide();
13432         }
13433              
13434         if(this.store.getCount() > 0){
13435             this.expand();
13436             this.restrictHeight();
13437             if(this.lastQuery == this.allQuery){
13438                 if(this.editable && !this.tickable){
13439                     this.inputEl().dom.select();
13440                 }
13441                 
13442                 if(
13443                     !this.selectByValue(this.value, true) &&
13444                     this.autoFocus && 
13445                     (
13446                         !this.store.lastOptions ||
13447                         typeof(this.store.lastOptions.add) == 'undefined' || 
13448                         this.store.lastOptions.add != true
13449                     )
13450                 ){
13451                     this.select(0, true);
13452                 }
13453             }else{
13454                 if(this.autoFocus){
13455                     this.selectNext();
13456                 }
13457                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13458                     this.taTask.delay(this.typeAheadDelay);
13459                 }
13460             }
13461         }else{
13462             this.onEmptyResults();
13463         }
13464         
13465         //this.el.focus();
13466     },
13467     // private
13468     onLoadException : function()
13469     {
13470         this.hasQuery = false;
13471         
13472         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13473             this.loading.hide();
13474         }
13475         
13476         if(this.tickable && this.editable){
13477             return;
13478         }
13479         
13480         this.collapse();
13481         // only causes errors at present
13482         //Roo.log(this.store.reader.jsonData);
13483         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13484             // fixme
13485             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13486         //}
13487         
13488         
13489     },
13490     // private
13491     onTypeAhead : function(){
13492         if(this.store.getCount() > 0){
13493             var r = this.store.getAt(0);
13494             var newValue = r.data[this.displayField];
13495             var len = newValue.length;
13496             var selStart = this.getRawValue().length;
13497             
13498             if(selStart != len){
13499                 this.setRawValue(newValue);
13500                 this.selectText(selStart, newValue.length);
13501             }
13502         }
13503     },
13504
13505     // private
13506     onSelect : function(record, index){
13507         
13508         if(this.fireEvent('beforeselect', this, record, index) !== false){
13509         
13510             this.setFromData(index > -1 ? record.data : false);
13511             
13512             this.collapse();
13513             this.fireEvent('select', this, record, index);
13514         }
13515     },
13516
13517     /**
13518      * Returns the currently selected field value or empty string if no value is set.
13519      * @return {String} value The selected value
13520      */
13521     getValue : function()
13522     {
13523         if(Roo.isIOS && this.useNativeIOS){
13524             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13525         }
13526         
13527         if(this.multiple){
13528             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13529         }
13530         
13531         if(this.valueField){
13532             return typeof this.value != 'undefined' ? this.value : '';
13533         }else{
13534             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13535         }
13536     },
13537     
13538     getRawValue : function()
13539     {
13540         if(Roo.isIOS && this.useNativeIOS){
13541             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13542         }
13543         
13544         var v = this.inputEl().getValue();
13545         
13546         return v;
13547     },
13548
13549     /**
13550      * Clears any text/value currently set in the field
13551      */
13552     clearValue : function(){
13553         
13554         if(this.hiddenField){
13555             this.hiddenField.dom.value = '';
13556         }
13557         this.value = '';
13558         this.setRawValue('');
13559         this.lastSelectionText = '';
13560         this.lastData = false;
13561         
13562         var close = this.closeTriggerEl();
13563         
13564         if(close){
13565             close.hide();
13566         }
13567         
13568         this.validate();
13569         
13570     },
13571
13572     /**
13573      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13574      * will be displayed in the field.  If the value does not match the data value of an existing item,
13575      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13576      * Otherwise the field will be blank (although the value will still be set).
13577      * @param {String} value The value to match
13578      */
13579     setValue : function(v)
13580     {
13581         if(Roo.isIOS && this.useNativeIOS){
13582             this.setIOSValue(v);
13583             return;
13584         }
13585         
13586         if(this.multiple){
13587             this.syncValue();
13588             return;
13589         }
13590         
13591         var text = v;
13592         if(this.valueField){
13593             var r = this.findRecord(this.valueField, v);
13594             if(r){
13595                 text = r.data[this.displayField];
13596             }else if(this.valueNotFoundText !== undefined){
13597                 text = this.valueNotFoundText;
13598             }
13599         }
13600         this.lastSelectionText = text;
13601         if(this.hiddenField){
13602             this.hiddenField.dom.value = v;
13603         }
13604         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13605         this.value = v;
13606         
13607         var close = this.closeTriggerEl();
13608         
13609         if(close){
13610             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13611         }
13612         
13613         this.validate();
13614     },
13615     /**
13616      * @property {Object} the last set data for the element
13617      */
13618     
13619     lastData : false,
13620     /**
13621      * Sets the value of the field based on a object which is related to the record format for the store.
13622      * @param {Object} value the value to set as. or false on reset?
13623      */
13624     setFromData : function(o){
13625         
13626         if(this.multiple){
13627             this.addItem(o);
13628             return;
13629         }
13630             
13631         var dv = ''; // display value
13632         var vv = ''; // value value..
13633         this.lastData = o;
13634         if (this.displayField) {
13635             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13636         } else {
13637             // this is an error condition!!!
13638             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13639         }
13640         
13641         if(this.valueField){
13642             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13643         }
13644         
13645         var close = this.closeTriggerEl();
13646         
13647         if(close){
13648             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13649         }
13650         
13651         if(this.hiddenField){
13652             this.hiddenField.dom.value = vv;
13653             
13654             this.lastSelectionText = dv;
13655             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13656             this.value = vv;
13657             return;
13658         }
13659         // no hidden field.. - we store the value in 'value', but still display
13660         // display field!!!!
13661         this.lastSelectionText = dv;
13662         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13663         this.value = vv;
13664         
13665         
13666         
13667     },
13668     // private
13669     reset : function(){
13670         // overridden so that last data is reset..
13671         
13672         if(this.multiple){
13673             this.clearItem();
13674             return;
13675         }
13676         
13677         this.setValue(this.originalValue);
13678         //this.clearInvalid();
13679         this.lastData = false;
13680         if (this.view) {
13681             this.view.clearSelections();
13682         }
13683         
13684         this.validate();
13685     },
13686     // private
13687     findRecord : function(prop, value){
13688         var record;
13689         if(this.store.getCount() > 0){
13690             this.store.each(function(r){
13691                 if(r.data[prop] == value){
13692                     record = r;
13693                     return false;
13694                 }
13695                 return true;
13696             });
13697         }
13698         return record;
13699     },
13700     
13701     getName: function()
13702     {
13703         // returns hidden if it's set..
13704         if (!this.rendered) {return ''};
13705         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13706         
13707     },
13708     // private
13709     onViewMove : function(e, t){
13710         this.inKeyMode = false;
13711     },
13712
13713     // private
13714     onViewOver : function(e, t){
13715         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13716             return;
13717         }
13718         var item = this.view.findItemFromChild(t);
13719         
13720         if(item){
13721             var index = this.view.indexOf(item);
13722             this.select(index, false);
13723         }
13724     },
13725
13726     // private
13727     onViewClick : function(view, doFocus, el, e)
13728     {
13729         var index = this.view.getSelectedIndexes()[0];
13730         
13731         var r = this.store.getAt(index);
13732         
13733         if(this.tickable){
13734             
13735             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13736                 return;
13737             }
13738             
13739             var rm = false;
13740             var _this = this;
13741             
13742             Roo.each(this.tickItems, function(v,k){
13743                 
13744                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13745                     Roo.log(v);
13746                     _this.tickItems.splice(k, 1);
13747                     
13748                     if(typeof(e) == 'undefined' && view == false){
13749                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13750                     }
13751                     
13752                     rm = true;
13753                     return;
13754                 }
13755             });
13756             
13757             if(rm){
13758                 return;
13759             }
13760             
13761             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13762                 this.tickItems.push(r.data);
13763             }
13764             
13765             if(typeof(e) == 'undefined' && view == false){
13766                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13767             }
13768                     
13769             return;
13770         }
13771         
13772         if(r){
13773             this.onSelect(r, index);
13774         }
13775         if(doFocus !== false && !this.blockFocus){
13776             this.inputEl().focus();
13777         }
13778     },
13779
13780     // private
13781     restrictHeight : function(){
13782         //this.innerList.dom.style.height = '';
13783         //var inner = this.innerList.dom;
13784         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13785         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13786         //this.list.beginUpdate();
13787         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13788         this.list.alignTo(this.inputEl(), this.listAlign);
13789         this.list.alignTo(this.inputEl(), this.listAlign);
13790         //this.list.endUpdate();
13791     },
13792
13793     // private
13794     onEmptyResults : function(){
13795         
13796         if(this.tickable && this.editable){
13797             this.restrictHeight();
13798             return;
13799         }
13800         
13801         this.collapse();
13802     },
13803
13804     /**
13805      * Returns true if the dropdown list is expanded, else false.
13806      */
13807     isExpanded : function(){
13808         return this.list.isVisible();
13809     },
13810
13811     /**
13812      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13813      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13814      * @param {String} value The data value of the item to select
13815      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13816      * selected item if it is not currently in view (defaults to true)
13817      * @return {Boolean} True if the value matched an item in the list, else false
13818      */
13819     selectByValue : function(v, scrollIntoView){
13820         if(v !== undefined && v !== null){
13821             var r = this.findRecord(this.valueField || this.displayField, v);
13822             if(r){
13823                 this.select(this.store.indexOf(r), scrollIntoView);
13824                 return true;
13825             }
13826         }
13827         return false;
13828     },
13829
13830     /**
13831      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13832      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13833      * @param {Number} index The zero-based index of the list item to select
13834      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13835      * selected item if it is not currently in view (defaults to true)
13836      */
13837     select : function(index, scrollIntoView){
13838         this.selectedIndex = index;
13839         this.view.select(index);
13840         if(scrollIntoView !== false){
13841             var el = this.view.getNode(index);
13842             /*
13843              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13844              */
13845             if(el){
13846                 this.list.scrollChildIntoView(el, false);
13847             }
13848         }
13849     },
13850
13851     // private
13852     selectNext : function(){
13853         var ct = this.store.getCount();
13854         if(ct > 0){
13855             if(this.selectedIndex == -1){
13856                 this.select(0);
13857             }else if(this.selectedIndex < ct-1){
13858                 this.select(this.selectedIndex+1);
13859             }
13860         }
13861     },
13862
13863     // private
13864     selectPrev : function(){
13865         var ct = this.store.getCount();
13866         if(ct > 0){
13867             if(this.selectedIndex == -1){
13868                 this.select(0);
13869             }else if(this.selectedIndex != 0){
13870                 this.select(this.selectedIndex-1);
13871             }
13872         }
13873     },
13874
13875     // private
13876     onKeyUp : function(e){
13877         if(this.editable !== false && !e.isSpecialKey()){
13878             this.lastKey = e.getKey();
13879             this.dqTask.delay(this.queryDelay);
13880         }
13881     },
13882
13883     // private
13884     validateBlur : function(){
13885         return !this.list || !this.list.isVisible();   
13886     },
13887
13888     // private
13889     initQuery : function(){
13890         
13891         var v = this.getRawValue();
13892         
13893         if(this.tickable && this.editable){
13894             v = this.tickableInputEl().getValue();
13895         }
13896         
13897         this.doQuery(v);
13898     },
13899
13900     // private
13901     doForce : function(){
13902         if(this.inputEl().dom.value.length > 0){
13903             this.inputEl().dom.value =
13904                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13905              
13906         }
13907     },
13908
13909     /**
13910      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
13911      * query allowing the query action to be canceled if needed.
13912      * @param {String} query The SQL query to execute
13913      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13914      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
13915      * saved in the current store (defaults to false)
13916      */
13917     doQuery : function(q, forceAll){
13918         
13919         if(q === undefined || q === null){
13920             q = '';
13921         }
13922         var qe = {
13923             query: q,
13924             forceAll: forceAll,
13925             combo: this,
13926             cancel:false
13927         };
13928         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13929             return false;
13930         }
13931         q = qe.query;
13932         
13933         forceAll = qe.forceAll;
13934         if(forceAll === true || (q.length >= this.minChars)){
13935             
13936             this.hasQuery = true;
13937             
13938             if(this.lastQuery != q || this.alwaysQuery){
13939                 this.lastQuery = q;
13940                 if(this.mode == 'local'){
13941                     this.selectedIndex = -1;
13942                     if(forceAll){
13943                         this.store.clearFilter();
13944                     }else{
13945                         
13946                         if(this.specialFilter){
13947                             this.fireEvent('specialfilter', this);
13948                             this.onLoad();
13949                             return;
13950                         }
13951                         
13952                         this.store.filter(this.displayField, q);
13953                     }
13954                     
13955                     this.store.fireEvent("datachanged", this.store);
13956                     
13957                     this.onLoad();
13958                     
13959                     
13960                 }else{
13961                     
13962                     this.store.baseParams[this.queryParam] = q;
13963                     
13964                     var options = {params : this.getParams(q)};
13965                     
13966                     if(this.loadNext){
13967                         options.add = true;
13968                         options.params.start = this.page * this.pageSize;
13969                     }
13970                     
13971                     this.store.load(options);
13972                     
13973                     /*
13974                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
13975                      *  we should expand the list on onLoad
13976                      *  so command out it
13977                      */
13978 //                    this.expand();
13979                 }
13980             }else{
13981                 this.selectedIndex = -1;
13982                 this.onLoad();   
13983             }
13984         }
13985         
13986         this.loadNext = false;
13987     },
13988     
13989     // private
13990     getParams : function(q){
13991         var p = {};
13992         //p[this.queryParam] = q;
13993         
13994         if(this.pageSize){
13995             p.start = 0;
13996             p.limit = this.pageSize;
13997         }
13998         return p;
13999     },
14000
14001     /**
14002      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14003      */
14004     collapse : function(){
14005         if(!this.isExpanded()){
14006             return;
14007         }
14008         
14009         this.list.hide();
14010         
14011         this.hasFocus = false;
14012         
14013         if(this.tickable){
14014             this.okBtn.hide();
14015             this.cancelBtn.hide();
14016             this.trigger.show();
14017             
14018             if(this.editable){
14019                 this.tickableInputEl().dom.value = '';
14020                 this.tickableInputEl().blur();
14021             }
14022             
14023         }
14024         
14025         Roo.get(document).un('mousedown', this.collapseIf, this);
14026         Roo.get(document).un('mousewheel', this.collapseIf, this);
14027         if (!this.editable) {
14028             Roo.get(document).un('keydown', this.listKeyPress, this);
14029         }
14030         this.fireEvent('collapse', this);
14031         
14032         this.validate();
14033     },
14034
14035     // private
14036     collapseIf : function(e){
14037         var in_combo  = e.within(this.el);
14038         var in_list =  e.within(this.list);
14039         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14040         
14041         if (in_combo || in_list || is_list) {
14042             //e.stopPropagation();
14043             return;
14044         }
14045         
14046         if(this.tickable){
14047             this.onTickableFooterButtonClick(e, false, false);
14048         }
14049
14050         this.collapse();
14051         
14052     },
14053
14054     /**
14055      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14056      */
14057     expand : function(){
14058        
14059         if(this.isExpanded() || !this.hasFocus){
14060             return;
14061         }
14062         
14063         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14064         this.list.setWidth(lw);
14065         
14066         Roo.log('expand');
14067         
14068         this.list.show();
14069         
14070         this.restrictHeight();
14071         
14072         if(this.tickable){
14073             
14074             this.tickItems = Roo.apply([], this.item);
14075             
14076             this.okBtn.show();
14077             this.cancelBtn.show();
14078             this.trigger.hide();
14079             
14080             if(this.editable){
14081                 this.tickableInputEl().focus();
14082             }
14083             
14084         }
14085         
14086         Roo.get(document).on('mousedown', this.collapseIf, this);
14087         Roo.get(document).on('mousewheel', this.collapseIf, this);
14088         if (!this.editable) {
14089             Roo.get(document).on('keydown', this.listKeyPress, this);
14090         }
14091         
14092         this.fireEvent('expand', this);
14093     },
14094
14095     // private
14096     // Implements the default empty TriggerField.onTriggerClick function
14097     onTriggerClick : function(e)
14098     {
14099         Roo.log('trigger click');
14100         
14101         if(this.disabled || !this.triggerList){
14102             return;
14103         }
14104         
14105         this.page = 0;
14106         this.loadNext = false;
14107         
14108         if(this.isExpanded()){
14109             this.collapse();
14110             if (!this.blockFocus) {
14111                 this.inputEl().focus();
14112             }
14113             
14114         }else {
14115             this.hasFocus = true;
14116             if(this.triggerAction == 'all') {
14117                 this.doQuery(this.allQuery, true);
14118             } else {
14119                 this.doQuery(this.getRawValue());
14120             }
14121             if (!this.blockFocus) {
14122                 this.inputEl().focus();
14123             }
14124         }
14125     },
14126     
14127     onTickableTriggerClick : function(e)
14128     {
14129         if(this.disabled){
14130             return;
14131         }
14132         
14133         this.page = 0;
14134         this.loadNext = false;
14135         this.hasFocus = true;
14136         
14137         if(this.triggerAction == 'all') {
14138             this.doQuery(this.allQuery, true);
14139         } else {
14140             this.doQuery(this.getRawValue());
14141         }
14142     },
14143     
14144     onSearchFieldClick : function(e)
14145     {
14146         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14147             this.onTickableFooterButtonClick(e, false, false);
14148             return;
14149         }
14150         
14151         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14152             return;
14153         }
14154         
14155         this.page = 0;
14156         this.loadNext = false;
14157         this.hasFocus = true;
14158         
14159         if(this.triggerAction == 'all') {
14160             this.doQuery(this.allQuery, true);
14161         } else {
14162             this.doQuery(this.getRawValue());
14163         }
14164     },
14165     
14166     listKeyPress : function(e)
14167     {
14168         //Roo.log('listkeypress');
14169         // scroll to first matching element based on key pres..
14170         if (e.isSpecialKey()) {
14171             return false;
14172         }
14173         var k = String.fromCharCode(e.getKey()).toUpperCase();
14174         //Roo.log(k);
14175         var match  = false;
14176         var csel = this.view.getSelectedNodes();
14177         var cselitem = false;
14178         if (csel.length) {
14179             var ix = this.view.indexOf(csel[0]);
14180             cselitem  = this.store.getAt(ix);
14181             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14182                 cselitem = false;
14183             }
14184             
14185         }
14186         
14187         this.store.each(function(v) { 
14188             if (cselitem) {
14189                 // start at existing selection.
14190                 if (cselitem.id == v.id) {
14191                     cselitem = false;
14192                 }
14193                 return true;
14194             }
14195                 
14196             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14197                 match = this.store.indexOf(v);
14198                 return false;
14199             }
14200             return true;
14201         }, this);
14202         
14203         if (match === false) {
14204             return true; // no more action?
14205         }
14206         // scroll to?
14207         this.view.select(match);
14208         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14209         sn.scrollIntoView(sn.dom.parentNode, false);
14210     },
14211     
14212     onViewScroll : function(e, t){
14213         
14214         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){
14215             return;
14216         }
14217         
14218         this.hasQuery = true;
14219         
14220         this.loading = this.list.select('.loading', true).first();
14221         
14222         if(this.loading === null){
14223             this.list.createChild({
14224                 tag: 'div',
14225                 cls: 'loading roo-select2-more-results roo-select2-active',
14226                 html: 'Loading more results...'
14227             });
14228             
14229             this.loading = this.list.select('.loading', true).first();
14230             
14231             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14232             
14233             this.loading.hide();
14234         }
14235         
14236         this.loading.show();
14237         
14238         var _combo = this;
14239         
14240         this.page++;
14241         this.loadNext = true;
14242         
14243         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14244         
14245         return;
14246     },
14247     
14248     addItem : function(o)
14249     {   
14250         var dv = ''; // display value
14251         
14252         if (this.displayField) {
14253             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14254         } else {
14255             // this is an error condition!!!
14256             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14257         }
14258         
14259         if(!dv.length){
14260             return;
14261         }
14262         
14263         var choice = this.choices.createChild({
14264             tag: 'li',
14265             cls: 'roo-select2-search-choice',
14266             cn: [
14267                 {
14268                     tag: 'div',
14269                     html: dv
14270                 },
14271                 {
14272                     tag: 'a',
14273                     href: '#',
14274                     cls: 'roo-select2-search-choice-close',
14275                     tabindex: '-1'
14276                 }
14277             ]
14278             
14279         }, this.searchField);
14280         
14281         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14282         
14283         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14284         
14285         this.item.push(o);
14286         
14287         this.lastData = o;
14288         
14289         this.syncValue();
14290         
14291         this.inputEl().dom.value = '';
14292         
14293         this.validate();
14294     },
14295     
14296     onRemoveItem : function(e, _self, o)
14297     {
14298         e.preventDefault();
14299         
14300         this.lastItem = Roo.apply([], this.item);
14301         
14302         var index = this.item.indexOf(o.data) * 1;
14303         
14304         if( index < 0){
14305             Roo.log('not this item?!');
14306             return;
14307         }
14308         
14309         this.item.splice(index, 1);
14310         o.item.remove();
14311         
14312         this.syncValue();
14313         
14314         this.fireEvent('remove', this, e);
14315         
14316         this.validate();
14317         
14318     },
14319     
14320     syncValue : function()
14321     {
14322         if(!this.item.length){
14323             this.clearValue();
14324             return;
14325         }
14326             
14327         var value = [];
14328         var _this = this;
14329         Roo.each(this.item, function(i){
14330             if(_this.valueField){
14331                 value.push(i[_this.valueField]);
14332                 return;
14333             }
14334
14335             value.push(i);
14336         });
14337
14338         this.value = value.join(',');
14339
14340         if(this.hiddenField){
14341             this.hiddenField.dom.value = this.value;
14342         }
14343         
14344         this.store.fireEvent("datachanged", this.store);
14345         
14346         this.validate();
14347     },
14348     
14349     clearItem : function()
14350     {
14351         if(!this.multiple){
14352             return;
14353         }
14354         
14355         this.item = [];
14356         
14357         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14358            c.remove();
14359         });
14360         
14361         this.syncValue();
14362         
14363         this.validate();
14364         
14365         if(this.tickable && !Roo.isTouch){
14366             this.view.refresh();
14367         }
14368     },
14369     
14370     inputEl: function ()
14371     {
14372         if(Roo.isIOS && this.useNativeIOS){
14373             return this.el.select('select.roo-ios-select', true).first();
14374         }
14375         
14376         if(Roo.isTouch && this.mobileTouchView){
14377             return this.el.select('input.form-control',true).first();
14378         }
14379         
14380         if(this.tickable){
14381             return this.searchField;
14382         }
14383         
14384         return this.el.select('input.form-control',true).first();
14385     },
14386     
14387     onTickableFooterButtonClick : function(e, btn, el)
14388     {
14389         e.preventDefault();
14390         
14391         this.lastItem = Roo.apply([], this.item);
14392         
14393         if(btn && btn.name == 'cancel'){
14394             this.tickItems = Roo.apply([], this.item);
14395             this.collapse();
14396             return;
14397         }
14398         
14399         this.clearItem();
14400         
14401         var _this = this;
14402         
14403         Roo.each(this.tickItems, function(o){
14404             _this.addItem(o);
14405         });
14406         
14407         this.collapse();
14408         
14409     },
14410     
14411     validate : function()
14412     {
14413         var v = this.getRawValue();
14414         
14415         if(this.multiple){
14416             v = this.getValue();
14417         }
14418         
14419         if(this.disabled || this.allowBlank || v.length){
14420             this.markValid();
14421             return true;
14422         }
14423         
14424         this.markInvalid();
14425         return false;
14426     },
14427     
14428     tickableInputEl : function()
14429     {
14430         if(!this.tickable || !this.editable){
14431             return this.inputEl();
14432         }
14433         
14434         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14435     },
14436     
14437     
14438     getAutoCreateTouchView : function()
14439     {
14440         var id = Roo.id();
14441         
14442         var cfg = {
14443             cls: 'form-group' //input-group
14444         };
14445         
14446         var input =  {
14447             tag: 'input',
14448             id : id,
14449             type : this.inputType,
14450             cls : 'form-control x-combo-noedit',
14451             autocomplete: 'new-password',
14452             placeholder : this.placeholder || '',
14453             readonly : true
14454         };
14455         
14456         if (this.name) {
14457             input.name = this.name;
14458         }
14459         
14460         if (this.size) {
14461             input.cls += ' input-' + this.size;
14462         }
14463         
14464         if (this.disabled) {
14465             input.disabled = true;
14466         }
14467         
14468         var inputblock = {
14469             cls : '',
14470             cn : [
14471                 input
14472             ]
14473         };
14474         
14475         if(this.before){
14476             inputblock.cls += ' input-group';
14477             
14478             inputblock.cn.unshift({
14479                 tag :'span',
14480                 cls : 'input-group-addon',
14481                 html : this.before
14482             });
14483         }
14484         
14485         if(this.removable && !this.multiple){
14486             inputblock.cls += ' roo-removable';
14487             
14488             inputblock.cn.push({
14489                 tag: 'button',
14490                 html : 'x',
14491                 cls : 'roo-combo-removable-btn close'
14492             });
14493         }
14494
14495         if(this.hasFeedback && !this.allowBlank){
14496             
14497             inputblock.cls += ' has-feedback';
14498             
14499             inputblock.cn.push({
14500                 tag: 'span',
14501                 cls: 'glyphicon form-control-feedback'
14502             });
14503             
14504         }
14505         
14506         if (this.after) {
14507             
14508             inputblock.cls += (this.before) ? '' : ' input-group';
14509             
14510             inputblock.cn.push({
14511                 tag :'span',
14512                 cls : 'input-group-addon',
14513                 html : this.after
14514             });
14515         }
14516
14517         var box = {
14518             tag: 'div',
14519             cn: [
14520                 {
14521                     tag: 'input',
14522                     type : 'hidden',
14523                     cls: 'form-hidden-field'
14524                 },
14525                 inputblock
14526             ]
14527             
14528         };
14529         
14530         if(this.multiple){
14531             box = {
14532                 tag: 'div',
14533                 cn: [
14534                     {
14535                         tag: 'input',
14536                         type : 'hidden',
14537                         cls: 'form-hidden-field'
14538                     },
14539                     {
14540                         tag: 'ul',
14541                         cls: 'roo-select2-choices',
14542                         cn:[
14543                             {
14544                                 tag: 'li',
14545                                 cls: 'roo-select2-search-field',
14546                                 cn: [
14547
14548                                     inputblock
14549                                 ]
14550                             }
14551                         ]
14552                     }
14553                 ]
14554             }
14555         };
14556         
14557         var combobox = {
14558             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14559             cn: [
14560                 box
14561             ]
14562         };
14563         
14564         if(!this.multiple && this.showToggleBtn){
14565             
14566             var caret = {
14567                         tag: 'span',
14568                         cls: 'caret'
14569             };
14570             
14571             if (this.caret != false) {
14572                 caret = {
14573                      tag: 'i',
14574                      cls: 'fa fa-' + this.caret
14575                 };
14576                 
14577             }
14578             
14579             combobox.cn.push({
14580                 tag :'span',
14581                 cls : 'input-group-addon btn dropdown-toggle',
14582                 cn : [
14583                     caret,
14584                     {
14585                         tag: 'span',
14586                         cls: 'combobox-clear',
14587                         cn  : [
14588                             {
14589                                 tag : 'i',
14590                                 cls: 'icon-remove'
14591                             }
14592                         ]
14593                     }
14594                 ]
14595
14596             })
14597         }
14598         
14599         if(this.multiple){
14600             combobox.cls += ' roo-select2-container-multi';
14601         }
14602         
14603         var align = this.labelAlign || this.parentLabelAlign();
14604         
14605         if (align ==='left' && this.fieldLabel.length) {
14606
14607             cfg.cn = [
14608                 {
14609                    tag : 'i',
14610                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14611                    tooltip : 'This field is required'
14612                 },
14613                 {
14614                     tag: 'label',
14615                     cls : 'control-label',
14616                     html : this.fieldLabel
14617
14618                 },
14619                 {
14620                     cls : '', 
14621                     cn: [
14622                         combobox
14623                     ]
14624                 }
14625             ];
14626             
14627             var labelCfg = cfg.cn[1];
14628             var contentCfg = cfg.cn[2];
14629             
14630
14631             if(this.indicatorpos == 'right'){
14632                 cfg.cn = [
14633                     {
14634                         tag: 'label',
14635                         cls : 'control-label',
14636                         html : this.fieldLabel,
14637                         cn : [
14638                             {
14639                                tag : 'i',
14640                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14641                                tooltip : 'This field is required'
14642                             }
14643                         ]
14644                     },
14645                     {
14646                         cls : '', 
14647                         cn: [
14648                             combobox
14649                         ]
14650                     }
14651                 ];
14652             }
14653             
14654             labelCfg = cfg.cn[0];
14655             contentCfg = cfg.cn[2];
14656             
14657             if(this.labelWidth > 12){
14658                 labelCfg.style = "width: " + this.labelWidth + 'px';
14659             }
14660             
14661             if(this.labelWidth < 13 && this.labelmd == 0){
14662                 this.labelmd = this.labelWidth;
14663             }
14664             
14665             if(this.labellg > 0){
14666                 labelCfg.cls += ' col-lg-' + this.labellg;
14667                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14668             }
14669             
14670             if(this.labelmd > 0){
14671                 labelCfg.cls += ' col-md-' + this.labelmd;
14672                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14673             }
14674             
14675             if(this.labelsm > 0){
14676                 labelCfg.cls += ' col-sm-' + this.labelsm;
14677                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14678             }
14679             
14680             if(this.labelxs > 0){
14681                 labelCfg.cls += ' col-xs-' + this.labelxs;
14682                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14683             }
14684                 
14685                 
14686         } else if ( this.fieldLabel.length) {
14687             cfg.cn = [
14688                 {
14689                    tag : 'i',
14690                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14691                    tooltip : 'This field is required'
14692                 },
14693                 {
14694                     tag: 'label',
14695                     cls : 'control-label',
14696                     html : this.fieldLabel
14697
14698                 },
14699                 {
14700                     cls : '', 
14701                     cn: [
14702                         combobox
14703                     ]
14704                 }
14705             ];
14706             
14707             if(this.indicatorpos == 'right'){
14708                 cfg.cn = [
14709                     {
14710                         tag: 'label',
14711                         cls : 'control-label',
14712                         html : this.fieldLabel,
14713                         cn : [
14714                             {
14715                                tag : 'i',
14716                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14717                                tooltip : 'This field is required'
14718                             }
14719                         ]
14720                     },
14721                     {
14722                         cls : '', 
14723                         cn: [
14724                             combobox
14725                         ]
14726                     }
14727                 ];
14728             }
14729         } else {
14730             cfg.cn = combobox;    
14731         }
14732         
14733         
14734         var settings = this;
14735         
14736         ['xs','sm','md','lg'].map(function(size){
14737             if (settings[size]) {
14738                 cfg.cls += ' col-' + size + '-' + settings[size];
14739             }
14740         });
14741         
14742         return cfg;
14743     },
14744     
14745     initTouchView : function()
14746     {
14747         this.renderTouchView();
14748         
14749         this.touchViewEl.on('scroll', function(){
14750             this.el.dom.scrollTop = 0;
14751         }, this);
14752         
14753         this.originalValue = this.getValue();
14754         
14755         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14756         
14757         this.inputEl().on("click", this.showTouchView, this);
14758         if (this.triggerEl) {
14759             this.triggerEl.on("click", this.showTouchView, this);
14760         }
14761         
14762         
14763         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14764         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14765         
14766         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14767         
14768         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14769         this.store.on('load', this.onTouchViewLoad, this);
14770         this.store.on('loadexception', this.onTouchViewLoadException, this);
14771         
14772         if(this.hiddenName){
14773             
14774             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14775             
14776             this.hiddenField.dom.value =
14777                 this.hiddenValue !== undefined ? this.hiddenValue :
14778                 this.value !== undefined ? this.value : '';
14779         
14780             this.el.dom.removeAttribute('name');
14781             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14782         }
14783         
14784         if(this.multiple){
14785             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14786             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14787         }
14788         
14789         if(this.removable && !this.multiple){
14790             var close = this.closeTriggerEl();
14791             if(close){
14792                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14793                 close.on('click', this.removeBtnClick, this, close);
14794             }
14795         }
14796         /*
14797          * fix the bug in Safari iOS8
14798          */
14799         this.inputEl().on("focus", function(e){
14800             document.activeElement.blur();
14801         }, this);
14802         
14803         return;
14804         
14805         
14806     },
14807     
14808     renderTouchView : function()
14809     {
14810         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14811         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14812         
14813         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14814         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14815         
14816         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14817         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14818         this.touchViewBodyEl.setStyle('overflow', 'auto');
14819         
14820         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14821         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14822         
14823         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14824         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14825         
14826     },
14827     
14828     showTouchView : function()
14829     {
14830         if(this.disabled){
14831             return;
14832         }
14833         
14834         this.touchViewHeaderEl.hide();
14835
14836         if(this.modalTitle.length){
14837             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14838             this.touchViewHeaderEl.show();
14839         }
14840
14841         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
14842         this.touchViewEl.show();
14843
14844         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14845         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14846                 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14847
14848         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14849
14850         if(this.modalTitle.length){
14851             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14852         }
14853         
14854         this.touchViewBodyEl.setHeight(bodyHeight);
14855
14856         if(this.animate){
14857             var _this = this;
14858             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14859         }else{
14860             this.touchViewEl.addClass('in');
14861         }
14862
14863         this.doTouchViewQuery();
14864         
14865     },
14866     
14867     hideTouchView : function()
14868     {
14869         this.touchViewEl.removeClass('in');
14870
14871         if(this.animate){
14872             var _this = this;
14873             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14874         }else{
14875             this.touchViewEl.setStyle('display', 'none');
14876         }
14877         
14878     },
14879     
14880     setTouchViewValue : function()
14881     {
14882         if(this.multiple){
14883             this.clearItem();
14884         
14885             var _this = this;
14886
14887             Roo.each(this.tickItems, function(o){
14888                 this.addItem(o);
14889             }, this);
14890         }
14891         
14892         this.hideTouchView();
14893     },
14894     
14895     doTouchViewQuery : function()
14896     {
14897         var qe = {
14898             query: '',
14899             forceAll: true,
14900             combo: this,
14901             cancel:false
14902         };
14903         
14904         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14905             return false;
14906         }
14907         
14908         if(!this.alwaysQuery || this.mode == 'local'){
14909             this.onTouchViewLoad();
14910             return;
14911         }
14912         
14913         this.store.load();
14914     },
14915     
14916     onTouchViewBeforeLoad : function(combo,opts)
14917     {
14918         return;
14919     },
14920
14921     // private
14922     onTouchViewLoad : function()
14923     {
14924         if(this.store.getCount() < 1){
14925             this.onTouchViewEmptyResults();
14926             return;
14927         }
14928         
14929         this.clearTouchView();
14930         
14931         var rawValue = this.getRawValue();
14932         
14933         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14934         
14935         this.tickItems = [];
14936         
14937         this.store.data.each(function(d, rowIndex){
14938             var row = this.touchViewListGroup.createChild(template);
14939             
14940             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14941                 row.addClass(d.data.cls);
14942             }
14943             
14944             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14945                 var cfg = {
14946                     data : d.data,
14947                     html : d.data[this.displayField]
14948                 };
14949                 
14950                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14951                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14952                 }
14953             }
14954             row.removeClass('selected');
14955             if(!this.multiple && this.valueField &&
14956                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14957             {
14958                 // radio buttons..
14959                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14960                 row.addClass('selected');
14961             }
14962             
14963             if(this.multiple && this.valueField &&
14964                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14965             {
14966                 
14967                 // checkboxes...
14968                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14969                 this.tickItems.push(d.data);
14970             }
14971             
14972             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14973             
14974         }, this);
14975         
14976         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14977         
14978         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14979
14980         if(this.modalTitle.length){
14981             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14982         }
14983
14984         var listHeight = this.touchViewListGroup.getHeight();
14985         
14986         var _this = this;
14987         
14988         if(firstChecked && listHeight > bodyHeight){
14989             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14990         }
14991         
14992     },
14993     
14994     onTouchViewLoadException : function()
14995     {
14996         this.hideTouchView();
14997     },
14998     
14999     onTouchViewEmptyResults : function()
15000     {
15001         this.clearTouchView();
15002         
15003         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15004         
15005         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15006         
15007     },
15008     
15009     clearTouchView : function()
15010     {
15011         this.touchViewListGroup.dom.innerHTML = '';
15012     },
15013     
15014     onTouchViewClick : function(e, el, o)
15015     {
15016         e.preventDefault();
15017         
15018         var row = o.row;
15019         var rowIndex = o.rowIndex;
15020         
15021         var r = this.store.getAt(rowIndex);
15022         
15023         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15024             
15025             if(!this.multiple){
15026                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15027                     c.dom.removeAttribute('checked');
15028                 }, this);
15029
15030                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15031
15032                 this.setFromData(r.data);
15033
15034                 var close = this.closeTriggerEl();
15035
15036                 if(close){
15037                     close.show();
15038                 }
15039
15040                 this.hideTouchView();
15041
15042                 this.fireEvent('select', this, r, rowIndex);
15043
15044                 return;
15045             }
15046
15047             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15048                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15049                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15050                 return;
15051             }
15052
15053             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15054             this.addItem(r.data);
15055             this.tickItems.push(r.data);
15056         }
15057     },
15058     
15059     getAutoCreateNativeIOS : function()
15060     {
15061         var cfg = {
15062             cls: 'form-group' //input-group,
15063         };
15064         
15065         var combobox =  {
15066             tag: 'select',
15067             cls : 'roo-ios-select'
15068         };
15069         
15070         if (this.name) {
15071             combobox.name = this.name;
15072         }
15073         
15074         if (this.disabled) {
15075             combobox.disabled = true;
15076         }
15077         
15078         var settings = this;
15079         
15080         ['xs','sm','md','lg'].map(function(size){
15081             if (settings[size]) {
15082                 cfg.cls += ' col-' + size + '-' + settings[size];
15083             }
15084         });
15085         
15086         cfg.cn = combobox;
15087         
15088         return cfg;
15089         
15090     },
15091     
15092     initIOSView : function()
15093     {
15094         this.store.on('load', this.onIOSViewLoad, this);
15095         
15096         return;
15097     },
15098     
15099     onIOSViewLoad : function()
15100     {
15101         if(this.store.getCount() < 1){
15102             return;
15103         }
15104         
15105         this.clearIOSView();
15106         
15107         if(this.allowBlank) {
15108             
15109             var default_text = '-- SELECT --';
15110             
15111             var opt = this.inputEl().createChild({
15112                 tag: 'option',
15113                 value : 0,
15114                 html : default_text
15115             });
15116             
15117             var o = {};
15118             o[this.valueField] = 0;
15119             o[this.displayField] = default_text;
15120             
15121             this.ios_options.push({
15122                 data : o,
15123                 el : opt
15124             });
15125             
15126         }
15127         
15128         this.store.data.each(function(d, rowIndex){
15129             
15130             var html = '';
15131             
15132             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15133                 html = d.data[this.displayField];
15134             }
15135             
15136             var value = '';
15137             
15138             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15139                 value = d.data[this.valueField];
15140             }
15141             
15142             var option = {
15143                 tag: 'option',
15144                 value : value,
15145                 html : html
15146             };
15147             
15148             if(this.value == d.data[this.valueField]){
15149                 option['selected'] = true;
15150             }
15151             
15152             var opt = this.inputEl().createChild(option);
15153             
15154             this.ios_options.push({
15155                 data : d.data,
15156                 el : opt
15157             });
15158             
15159         }, this);
15160         
15161         this.inputEl().on('change', function(){
15162            this.fireEvent('select', this);
15163         }, this);
15164         
15165     },
15166     
15167     clearIOSView: function()
15168     {
15169         this.inputEl().dom.innerHTML = '';
15170         
15171         this.ios_options = [];
15172     },
15173     
15174     setIOSValue: function(v)
15175     {
15176         this.value = v;
15177         
15178         if(!this.ios_options){
15179             return;
15180         }
15181         
15182         Roo.each(this.ios_options, function(opts){
15183            
15184            opts.el.dom.removeAttribute('selected');
15185            
15186            if(opts.data[this.valueField] != v){
15187                return;
15188            }
15189            
15190            opts.el.dom.setAttribute('selected', true);
15191            
15192         }, this);
15193     }
15194
15195     /** 
15196     * @cfg {Boolean} grow 
15197     * @hide 
15198     */
15199     /** 
15200     * @cfg {Number} growMin 
15201     * @hide 
15202     */
15203     /** 
15204     * @cfg {Number} growMax 
15205     * @hide 
15206     */
15207     /**
15208      * @hide
15209      * @method autoSize
15210      */
15211 });
15212
15213 Roo.apply(Roo.bootstrap.ComboBox,  {
15214     
15215     header : {
15216         tag: 'div',
15217         cls: 'modal-header',
15218         cn: [
15219             {
15220                 tag: 'h4',
15221                 cls: 'modal-title'
15222             }
15223         ]
15224     },
15225     
15226     body : {
15227         tag: 'div',
15228         cls: 'modal-body',
15229         cn: [
15230             {
15231                 tag: 'ul',
15232                 cls: 'list-group'
15233             }
15234         ]
15235     },
15236     
15237     listItemRadio : {
15238         tag: 'li',
15239         cls: 'list-group-item',
15240         cn: [
15241             {
15242                 tag: 'span',
15243                 cls: 'roo-combobox-list-group-item-value'
15244             },
15245             {
15246                 tag: 'div',
15247                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15248                 cn: [
15249                     {
15250                         tag: 'input',
15251                         type: 'radio'
15252                     },
15253                     {
15254                         tag: 'label'
15255                     }
15256                 ]
15257             }
15258         ]
15259     },
15260     
15261     listItemCheckbox : {
15262         tag: 'li',
15263         cls: 'list-group-item',
15264         cn: [
15265             {
15266                 tag: 'span',
15267                 cls: 'roo-combobox-list-group-item-value'
15268             },
15269             {
15270                 tag: 'div',
15271                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15272                 cn: [
15273                     {
15274                         tag: 'input',
15275                         type: 'checkbox'
15276                     },
15277                     {
15278                         tag: 'label'
15279                     }
15280                 ]
15281             }
15282         ]
15283     },
15284     
15285     emptyResult : {
15286         tag: 'div',
15287         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15288     },
15289     
15290     footer : {
15291         tag: 'div',
15292         cls: 'modal-footer',
15293         cn: [
15294             {
15295                 tag: 'div',
15296                 cls: 'row',
15297                 cn: [
15298                     {
15299                         tag: 'div',
15300                         cls: 'col-xs-6 text-left',
15301                         cn: {
15302                             tag: 'button',
15303                             cls: 'btn btn-danger roo-touch-view-cancel',
15304                             html: 'Cancel'
15305                         }
15306                     },
15307                     {
15308                         tag: 'div',
15309                         cls: 'col-xs-6 text-right',
15310                         cn: {
15311                             tag: 'button',
15312                             cls: 'btn btn-success roo-touch-view-ok',
15313                             html: 'OK'
15314                         }
15315                     }
15316                 ]
15317             }
15318         ]
15319         
15320     }
15321 });
15322
15323 Roo.apply(Roo.bootstrap.ComboBox,  {
15324     
15325     touchViewTemplate : {
15326         tag: 'div',
15327         cls: 'modal fade roo-combobox-touch-view',
15328         cn: [
15329             {
15330                 tag: 'div',
15331                 cls: 'modal-dialog',
15332                 style : 'position:fixed', // we have to fix position....
15333                 cn: [
15334                     {
15335                         tag: 'div',
15336                         cls: 'modal-content',
15337                         cn: [
15338                             Roo.bootstrap.ComboBox.header,
15339                             Roo.bootstrap.ComboBox.body,
15340                             Roo.bootstrap.ComboBox.footer
15341                         ]
15342                     }
15343                 ]
15344             }
15345         ]
15346     }
15347 });/*
15348  * Based on:
15349  * Ext JS Library 1.1.1
15350  * Copyright(c) 2006-2007, Ext JS, LLC.
15351  *
15352  * Originally Released Under LGPL - original licence link has changed is not relivant.
15353  *
15354  * Fork - LGPL
15355  * <script type="text/javascript">
15356  */
15357
15358 /**
15359  * @class Roo.View
15360  * @extends Roo.util.Observable
15361  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15362  * This class also supports single and multi selection modes. <br>
15363  * Create a data model bound view:
15364  <pre><code>
15365  var store = new Roo.data.Store(...);
15366
15367  var view = new Roo.View({
15368     el : "my-element",
15369     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15370  
15371     singleSelect: true,
15372     selectedClass: "ydataview-selected",
15373     store: store
15374  });
15375
15376  // listen for node click?
15377  view.on("click", function(vw, index, node, e){
15378  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15379  });
15380
15381  // load XML data
15382  dataModel.load("foobar.xml");
15383  </code></pre>
15384  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15385  * <br><br>
15386  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15387  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15388  * 
15389  * Note: old style constructor is still suported (container, template, config)
15390  * 
15391  * @constructor
15392  * Create a new View
15393  * @param {Object} config The config object
15394  * 
15395  */
15396 Roo.View = function(config, depreciated_tpl, depreciated_config){
15397     
15398     this.parent = false;
15399     
15400     if (typeof(depreciated_tpl) == 'undefined') {
15401         // new way.. - universal constructor.
15402         Roo.apply(this, config);
15403         this.el  = Roo.get(this.el);
15404     } else {
15405         // old format..
15406         this.el  = Roo.get(config);
15407         this.tpl = depreciated_tpl;
15408         Roo.apply(this, depreciated_config);
15409     }
15410     this.wrapEl  = this.el.wrap().wrap();
15411     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15412     
15413     
15414     if(typeof(this.tpl) == "string"){
15415         this.tpl = new Roo.Template(this.tpl);
15416     } else {
15417         // support xtype ctors..
15418         this.tpl = new Roo.factory(this.tpl, Roo);
15419     }
15420     
15421     
15422     this.tpl.compile();
15423     
15424     /** @private */
15425     this.addEvents({
15426         /**
15427          * @event beforeclick
15428          * Fires before a click is processed. Returns false to cancel the default action.
15429          * @param {Roo.View} this
15430          * @param {Number} index The index of the target node
15431          * @param {HTMLElement} node The target node
15432          * @param {Roo.EventObject} e The raw event object
15433          */
15434             "beforeclick" : true,
15435         /**
15436          * @event click
15437          * Fires when a template node is clicked.
15438          * @param {Roo.View} this
15439          * @param {Number} index The index of the target node
15440          * @param {HTMLElement} node The target node
15441          * @param {Roo.EventObject} e The raw event object
15442          */
15443             "click" : true,
15444         /**
15445          * @event dblclick
15446          * Fires when a template node is double clicked.
15447          * @param {Roo.View} this
15448          * @param {Number} index The index of the target node
15449          * @param {HTMLElement} node The target node
15450          * @param {Roo.EventObject} e The raw event object
15451          */
15452             "dblclick" : true,
15453         /**
15454          * @event contextmenu
15455          * Fires when a template node is right clicked.
15456          * @param {Roo.View} this
15457          * @param {Number} index The index of the target node
15458          * @param {HTMLElement} node The target node
15459          * @param {Roo.EventObject} e The raw event object
15460          */
15461             "contextmenu" : true,
15462         /**
15463          * @event selectionchange
15464          * Fires when the selected nodes change.
15465          * @param {Roo.View} this
15466          * @param {Array} selections Array of the selected nodes
15467          */
15468             "selectionchange" : true,
15469     
15470         /**
15471          * @event beforeselect
15472          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15473          * @param {Roo.View} this
15474          * @param {HTMLElement} node The node to be selected
15475          * @param {Array} selections Array of currently selected nodes
15476          */
15477             "beforeselect" : true,
15478         /**
15479          * @event preparedata
15480          * Fires on every row to render, to allow you to change the data.
15481          * @param {Roo.View} this
15482          * @param {Object} data to be rendered (change this)
15483          */
15484           "preparedata" : true
15485           
15486           
15487         });
15488
15489
15490
15491     this.el.on({
15492         "click": this.onClick,
15493         "dblclick": this.onDblClick,
15494         "contextmenu": this.onContextMenu,
15495         scope:this
15496     });
15497
15498     this.selections = [];
15499     this.nodes = [];
15500     this.cmp = new Roo.CompositeElementLite([]);
15501     if(this.store){
15502         this.store = Roo.factory(this.store, Roo.data);
15503         this.setStore(this.store, true);
15504     }
15505     
15506     if ( this.footer && this.footer.xtype) {
15507            
15508          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15509         
15510         this.footer.dataSource = this.store;
15511         this.footer.container = fctr;
15512         this.footer = Roo.factory(this.footer, Roo);
15513         fctr.insertFirst(this.el);
15514         
15515         // this is a bit insane - as the paging toolbar seems to detach the el..
15516 //        dom.parentNode.parentNode.parentNode
15517          // they get detached?
15518     }
15519     
15520     
15521     Roo.View.superclass.constructor.call(this);
15522     
15523     
15524 };
15525
15526 Roo.extend(Roo.View, Roo.util.Observable, {
15527     
15528      /**
15529      * @cfg {Roo.data.Store} store Data store to load data from.
15530      */
15531     store : false,
15532     
15533     /**
15534      * @cfg {String|Roo.Element} el The container element.
15535      */
15536     el : '',
15537     
15538     /**
15539      * @cfg {String|Roo.Template} tpl The template used by this View 
15540      */
15541     tpl : false,
15542     /**
15543      * @cfg {String} dataName the named area of the template to use as the data area
15544      *                          Works with domtemplates roo-name="name"
15545      */
15546     dataName: false,
15547     /**
15548      * @cfg {String} selectedClass The css class to add to selected nodes
15549      */
15550     selectedClass : "x-view-selected",
15551      /**
15552      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15553      */
15554     emptyText : "",
15555     
15556     /**
15557      * @cfg {String} text to display on mask (default Loading)
15558      */
15559     mask : false,
15560     /**
15561      * @cfg {Boolean} multiSelect Allow multiple selection
15562      */
15563     multiSelect : false,
15564     /**
15565      * @cfg {Boolean} singleSelect Allow single selection
15566      */
15567     singleSelect:  false,
15568     
15569     /**
15570      * @cfg {Boolean} toggleSelect - selecting 
15571      */
15572     toggleSelect : false,
15573     
15574     /**
15575      * @cfg {Boolean} tickable - selecting 
15576      */
15577     tickable : false,
15578     
15579     /**
15580      * Returns the element this view is bound to.
15581      * @return {Roo.Element}
15582      */
15583     getEl : function(){
15584         return this.wrapEl;
15585     },
15586     
15587     
15588
15589     /**
15590      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15591      */
15592     refresh : function(){
15593         //Roo.log('refresh');
15594         var t = this.tpl;
15595         
15596         // if we are using something like 'domtemplate', then
15597         // the what gets used is:
15598         // t.applySubtemplate(NAME, data, wrapping data..)
15599         // the outer template then get' applied with
15600         //     the store 'extra data'
15601         // and the body get's added to the
15602         //      roo-name="data" node?
15603         //      <span class='roo-tpl-{name}'></span> ?????
15604         
15605         
15606         
15607         this.clearSelections();
15608         this.el.update("");
15609         var html = [];
15610         var records = this.store.getRange();
15611         if(records.length < 1) {
15612             
15613             // is this valid??  = should it render a template??
15614             
15615             this.el.update(this.emptyText);
15616             return;
15617         }
15618         var el = this.el;
15619         if (this.dataName) {
15620             this.el.update(t.apply(this.store.meta)); //????
15621             el = this.el.child('.roo-tpl-' + this.dataName);
15622         }
15623         
15624         for(var i = 0, len = records.length; i < len; i++){
15625             var data = this.prepareData(records[i].data, i, records[i]);
15626             this.fireEvent("preparedata", this, data, i, records[i]);
15627             
15628             var d = Roo.apply({}, data);
15629             
15630             if(this.tickable){
15631                 Roo.apply(d, {'roo-id' : Roo.id()});
15632                 
15633                 var _this = this;
15634             
15635                 Roo.each(this.parent.item, function(item){
15636                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15637                         return;
15638                     }
15639                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15640                 });
15641             }
15642             
15643             html[html.length] = Roo.util.Format.trim(
15644                 this.dataName ?
15645                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15646                     t.apply(d)
15647             );
15648         }
15649         
15650         
15651         
15652         el.update(html.join(""));
15653         this.nodes = el.dom.childNodes;
15654         this.updateIndexes(0);
15655     },
15656     
15657
15658     /**
15659      * Function to override to reformat the data that is sent to
15660      * the template for each node.
15661      * DEPRICATED - use the preparedata event handler.
15662      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15663      * a JSON object for an UpdateManager bound view).
15664      */
15665     prepareData : function(data, index, record)
15666     {
15667         this.fireEvent("preparedata", this, data, index, record);
15668         return data;
15669     },
15670
15671     onUpdate : function(ds, record){
15672         // Roo.log('on update');   
15673         this.clearSelections();
15674         var index = this.store.indexOf(record);
15675         var n = this.nodes[index];
15676         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15677         n.parentNode.removeChild(n);
15678         this.updateIndexes(index, index);
15679     },
15680
15681     
15682     
15683 // --------- FIXME     
15684     onAdd : function(ds, records, index)
15685     {
15686         //Roo.log(['on Add', ds, records, index] );        
15687         this.clearSelections();
15688         if(this.nodes.length == 0){
15689             this.refresh();
15690             return;
15691         }
15692         var n = this.nodes[index];
15693         for(var i = 0, len = records.length; i < len; i++){
15694             var d = this.prepareData(records[i].data, i, records[i]);
15695             if(n){
15696                 this.tpl.insertBefore(n, d);
15697             }else{
15698                 
15699                 this.tpl.append(this.el, d);
15700             }
15701         }
15702         this.updateIndexes(index);
15703     },
15704
15705     onRemove : function(ds, record, index){
15706        // Roo.log('onRemove');
15707         this.clearSelections();
15708         var el = this.dataName  ?
15709             this.el.child('.roo-tpl-' + this.dataName) :
15710             this.el; 
15711         
15712         el.dom.removeChild(this.nodes[index]);
15713         this.updateIndexes(index);
15714     },
15715
15716     /**
15717      * Refresh an individual node.
15718      * @param {Number} index
15719      */
15720     refreshNode : function(index){
15721         this.onUpdate(this.store, this.store.getAt(index));
15722     },
15723
15724     updateIndexes : function(startIndex, endIndex){
15725         var ns = this.nodes;
15726         startIndex = startIndex || 0;
15727         endIndex = endIndex || ns.length - 1;
15728         for(var i = startIndex; i <= endIndex; i++){
15729             ns[i].nodeIndex = i;
15730         }
15731     },
15732
15733     /**
15734      * Changes the data store this view uses and refresh the view.
15735      * @param {Store} store
15736      */
15737     setStore : function(store, initial){
15738         if(!initial && this.store){
15739             this.store.un("datachanged", this.refresh);
15740             this.store.un("add", this.onAdd);
15741             this.store.un("remove", this.onRemove);
15742             this.store.un("update", this.onUpdate);
15743             this.store.un("clear", this.refresh);
15744             this.store.un("beforeload", this.onBeforeLoad);
15745             this.store.un("load", this.onLoad);
15746             this.store.un("loadexception", this.onLoad);
15747         }
15748         if(store){
15749           
15750             store.on("datachanged", this.refresh, this);
15751             store.on("add", this.onAdd, this);
15752             store.on("remove", this.onRemove, this);
15753             store.on("update", this.onUpdate, this);
15754             store.on("clear", this.refresh, this);
15755             store.on("beforeload", this.onBeforeLoad, this);
15756             store.on("load", this.onLoad, this);
15757             store.on("loadexception", this.onLoad, this);
15758         }
15759         
15760         if(store){
15761             this.refresh();
15762         }
15763     },
15764     /**
15765      * onbeforeLoad - masks the loading area.
15766      *
15767      */
15768     onBeforeLoad : function(store,opts)
15769     {
15770          //Roo.log('onBeforeLoad');   
15771         if (!opts.add) {
15772             this.el.update("");
15773         }
15774         this.el.mask(this.mask ? this.mask : "Loading" ); 
15775     },
15776     onLoad : function ()
15777     {
15778         this.el.unmask();
15779     },
15780     
15781
15782     /**
15783      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15784      * @param {HTMLElement} node
15785      * @return {HTMLElement} The template node
15786      */
15787     findItemFromChild : function(node){
15788         var el = this.dataName  ?
15789             this.el.child('.roo-tpl-' + this.dataName,true) :
15790             this.el.dom; 
15791         
15792         if(!node || node.parentNode == el){
15793                     return node;
15794             }
15795             var p = node.parentNode;
15796             while(p && p != el){
15797             if(p.parentNode == el){
15798                 return p;
15799             }
15800             p = p.parentNode;
15801         }
15802             return null;
15803     },
15804
15805     /** @ignore */
15806     onClick : function(e){
15807         var item = this.findItemFromChild(e.getTarget());
15808         if(item){
15809             var index = this.indexOf(item);
15810             if(this.onItemClick(item, index, e) !== false){
15811                 this.fireEvent("click", this, index, item, e);
15812             }
15813         }else{
15814             this.clearSelections();
15815         }
15816     },
15817
15818     /** @ignore */
15819     onContextMenu : function(e){
15820         var item = this.findItemFromChild(e.getTarget());
15821         if(item){
15822             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15823         }
15824     },
15825
15826     /** @ignore */
15827     onDblClick : function(e){
15828         var item = this.findItemFromChild(e.getTarget());
15829         if(item){
15830             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15831         }
15832     },
15833
15834     onItemClick : function(item, index, e)
15835     {
15836         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15837             return false;
15838         }
15839         if (this.toggleSelect) {
15840             var m = this.isSelected(item) ? 'unselect' : 'select';
15841             //Roo.log(m);
15842             var _t = this;
15843             _t[m](item, true, false);
15844             return true;
15845         }
15846         if(this.multiSelect || this.singleSelect){
15847             if(this.multiSelect && e.shiftKey && this.lastSelection){
15848                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15849             }else{
15850                 this.select(item, this.multiSelect && e.ctrlKey);
15851                 this.lastSelection = item;
15852             }
15853             
15854             if(!this.tickable){
15855                 e.preventDefault();
15856             }
15857             
15858         }
15859         return true;
15860     },
15861
15862     /**
15863      * Get the number of selected nodes.
15864      * @return {Number}
15865      */
15866     getSelectionCount : function(){
15867         return this.selections.length;
15868     },
15869
15870     /**
15871      * Get the currently selected nodes.
15872      * @return {Array} An array of HTMLElements
15873      */
15874     getSelectedNodes : function(){
15875         return this.selections;
15876     },
15877
15878     /**
15879      * Get the indexes of the selected nodes.
15880      * @return {Array}
15881      */
15882     getSelectedIndexes : function(){
15883         var indexes = [], s = this.selections;
15884         for(var i = 0, len = s.length; i < len; i++){
15885             indexes.push(s[i].nodeIndex);
15886         }
15887         return indexes;
15888     },
15889
15890     /**
15891      * Clear all selections
15892      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15893      */
15894     clearSelections : function(suppressEvent){
15895         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15896             this.cmp.elements = this.selections;
15897             this.cmp.removeClass(this.selectedClass);
15898             this.selections = [];
15899             if(!suppressEvent){
15900                 this.fireEvent("selectionchange", this, this.selections);
15901             }
15902         }
15903     },
15904
15905     /**
15906      * Returns true if the passed node is selected
15907      * @param {HTMLElement/Number} node The node or node index
15908      * @return {Boolean}
15909      */
15910     isSelected : function(node){
15911         var s = this.selections;
15912         if(s.length < 1){
15913             return false;
15914         }
15915         node = this.getNode(node);
15916         return s.indexOf(node) !== -1;
15917     },
15918
15919     /**
15920      * Selects nodes.
15921      * @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
15922      * @param {Boolean} keepExisting (optional) true to keep existing selections
15923      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15924      */
15925     select : function(nodeInfo, keepExisting, suppressEvent){
15926         if(nodeInfo instanceof Array){
15927             if(!keepExisting){
15928                 this.clearSelections(true);
15929             }
15930             for(var i = 0, len = nodeInfo.length; i < len; i++){
15931                 this.select(nodeInfo[i], true, true);
15932             }
15933             return;
15934         } 
15935         var node = this.getNode(nodeInfo);
15936         if(!node || this.isSelected(node)){
15937             return; // already selected.
15938         }
15939         if(!keepExisting){
15940             this.clearSelections(true);
15941         }
15942         
15943         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15944             Roo.fly(node).addClass(this.selectedClass);
15945             this.selections.push(node);
15946             if(!suppressEvent){
15947                 this.fireEvent("selectionchange", this, this.selections);
15948             }
15949         }
15950         
15951         
15952     },
15953       /**
15954      * Unselects nodes.
15955      * @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
15956      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15957      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15958      */
15959     unselect : function(nodeInfo, keepExisting, suppressEvent)
15960     {
15961         if(nodeInfo instanceof Array){
15962             Roo.each(this.selections, function(s) {
15963                 this.unselect(s, nodeInfo);
15964             }, this);
15965             return;
15966         }
15967         var node = this.getNode(nodeInfo);
15968         if(!node || !this.isSelected(node)){
15969             //Roo.log("not selected");
15970             return; // not selected.
15971         }
15972         // fireevent???
15973         var ns = [];
15974         Roo.each(this.selections, function(s) {
15975             if (s == node ) {
15976                 Roo.fly(node).removeClass(this.selectedClass);
15977
15978                 return;
15979             }
15980             ns.push(s);
15981         },this);
15982         
15983         this.selections= ns;
15984         this.fireEvent("selectionchange", this, this.selections);
15985     },
15986
15987     /**
15988      * Gets a template node.
15989      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15990      * @return {HTMLElement} The node or null if it wasn't found
15991      */
15992     getNode : function(nodeInfo){
15993         if(typeof nodeInfo == "string"){
15994             return document.getElementById(nodeInfo);
15995         }else if(typeof nodeInfo == "number"){
15996             return this.nodes[nodeInfo];
15997         }
15998         return nodeInfo;
15999     },
16000
16001     /**
16002      * Gets a range template nodes.
16003      * @param {Number} startIndex
16004      * @param {Number} endIndex
16005      * @return {Array} An array of nodes
16006      */
16007     getNodes : function(start, end){
16008         var ns = this.nodes;
16009         start = start || 0;
16010         end = typeof end == "undefined" ? ns.length - 1 : end;
16011         var nodes = [];
16012         if(start <= end){
16013             for(var i = start; i <= end; i++){
16014                 nodes.push(ns[i]);
16015             }
16016         } else{
16017             for(var i = start; i >= end; i--){
16018                 nodes.push(ns[i]);
16019             }
16020         }
16021         return nodes;
16022     },
16023
16024     /**
16025      * Finds the index of the passed node
16026      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16027      * @return {Number} The index of the node or -1
16028      */
16029     indexOf : function(node){
16030         node = this.getNode(node);
16031         if(typeof node.nodeIndex == "number"){
16032             return node.nodeIndex;
16033         }
16034         var ns = this.nodes;
16035         for(var i = 0, len = ns.length; i < len; i++){
16036             if(ns[i] == node){
16037                 return i;
16038             }
16039         }
16040         return -1;
16041     }
16042 });
16043 /*
16044  * - LGPL
16045  *
16046  * based on jquery fullcalendar
16047  * 
16048  */
16049
16050 Roo.bootstrap = Roo.bootstrap || {};
16051 /**
16052  * @class Roo.bootstrap.Calendar
16053  * @extends Roo.bootstrap.Component
16054  * Bootstrap Calendar class
16055  * @cfg {Boolean} loadMask (true|false) default false
16056  * @cfg {Object} header generate the user specific header of the calendar, default false
16057
16058  * @constructor
16059  * Create a new Container
16060  * @param {Object} config The config object
16061  */
16062
16063
16064
16065 Roo.bootstrap.Calendar = function(config){
16066     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16067      this.addEvents({
16068         /**
16069              * @event select
16070              * Fires when a date is selected
16071              * @param {DatePicker} this
16072              * @param {Date} date The selected date
16073              */
16074         'select': true,
16075         /**
16076              * @event monthchange
16077              * Fires when the displayed month changes 
16078              * @param {DatePicker} this
16079              * @param {Date} date The selected month
16080              */
16081         'monthchange': true,
16082         /**
16083              * @event evententer
16084              * Fires when mouse over an event
16085              * @param {Calendar} this
16086              * @param {event} Event
16087              */
16088         'evententer': true,
16089         /**
16090              * @event eventleave
16091              * Fires when the mouse leaves an
16092              * @param {Calendar} this
16093              * @param {event}
16094              */
16095         'eventleave': true,
16096         /**
16097              * @event eventclick
16098              * Fires when the mouse click an
16099              * @param {Calendar} this
16100              * @param {event}
16101              */
16102         'eventclick': true
16103         
16104     });
16105
16106 };
16107
16108 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16109     
16110      /**
16111      * @cfg {Number} startDay
16112      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16113      */
16114     startDay : 0,
16115     
16116     loadMask : false,
16117     
16118     header : false,
16119       
16120     getAutoCreate : function(){
16121         
16122         
16123         var fc_button = function(name, corner, style, content ) {
16124             return Roo.apply({},{
16125                 tag : 'span',
16126                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16127                          (corner.length ?
16128                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16129                             ''
16130                         ),
16131                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16132                 unselectable: 'on'
16133             });
16134         };
16135         
16136         var header = {};
16137         
16138         if(!this.header){
16139             header = {
16140                 tag : 'table',
16141                 cls : 'fc-header',
16142                 style : 'width:100%',
16143                 cn : [
16144                     {
16145                         tag: 'tr',
16146                         cn : [
16147                             {
16148                                 tag : 'td',
16149                                 cls : 'fc-header-left',
16150                                 cn : [
16151                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16152                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16153                                     { tag: 'span', cls: 'fc-header-space' },
16154                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16155
16156
16157                                 ]
16158                             },
16159
16160                             {
16161                                 tag : 'td',
16162                                 cls : 'fc-header-center',
16163                                 cn : [
16164                                     {
16165                                         tag: 'span',
16166                                         cls: 'fc-header-title',
16167                                         cn : {
16168                                             tag: 'H2',
16169                                             html : 'month / year'
16170                                         }
16171                                     }
16172
16173                                 ]
16174                             },
16175                             {
16176                                 tag : 'td',
16177                                 cls : 'fc-header-right',
16178                                 cn : [
16179                               /*      fc_button('month', 'left', '', 'month' ),
16180                                     fc_button('week', '', '', 'week' ),
16181                                     fc_button('day', 'right', '', 'day' )
16182                                 */    
16183
16184                                 ]
16185                             }
16186
16187                         ]
16188                     }
16189                 ]
16190             };
16191         }
16192         
16193         header = this.header;
16194         
16195        
16196         var cal_heads = function() {
16197             var ret = [];
16198             // fixme - handle this.
16199             
16200             for (var i =0; i < Date.dayNames.length; i++) {
16201                 var d = Date.dayNames[i];
16202                 ret.push({
16203                     tag: 'th',
16204                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16205                     html : d.substring(0,3)
16206                 });
16207                 
16208             }
16209             ret[0].cls += ' fc-first';
16210             ret[6].cls += ' fc-last';
16211             return ret;
16212         };
16213         var cal_cell = function(n) {
16214             return  {
16215                 tag: 'td',
16216                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16217                 cn : [
16218                     {
16219                         cn : [
16220                             {
16221                                 cls: 'fc-day-number',
16222                                 html: 'D'
16223                             },
16224                             {
16225                                 cls: 'fc-day-content',
16226                              
16227                                 cn : [
16228                                      {
16229                                         style: 'position: relative;' // height: 17px;
16230                                     }
16231                                 ]
16232                             }
16233                             
16234                             
16235                         ]
16236                     }
16237                 ]
16238                 
16239             }
16240         };
16241         var cal_rows = function() {
16242             
16243             var ret = [];
16244             for (var r = 0; r < 6; r++) {
16245                 var row= {
16246                     tag : 'tr',
16247                     cls : 'fc-week',
16248                     cn : []
16249                 };
16250                 
16251                 for (var i =0; i < Date.dayNames.length; i++) {
16252                     var d = Date.dayNames[i];
16253                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16254
16255                 }
16256                 row.cn[0].cls+=' fc-first';
16257                 row.cn[0].cn[0].style = 'min-height:90px';
16258                 row.cn[6].cls+=' fc-last';
16259                 ret.push(row);
16260                 
16261             }
16262             ret[0].cls += ' fc-first';
16263             ret[4].cls += ' fc-prev-last';
16264             ret[5].cls += ' fc-last';
16265             return ret;
16266             
16267         };
16268         
16269         var cal_table = {
16270             tag: 'table',
16271             cls: 'fc-border-separate',
16272             style : 'width:100%',
16273             cellspacing  : 0,
16274             cn : [
16275                 { 
16276                     tag: 'thead',
16277                     cn : [
16278                         { 
16279                             tag: 'tr',
16280                             cls : 'fc-first fc-last',
16281                             cn : cal_heads()
16282                         }
16283                     ]
16284                 },
16285                 { 
16286                     tag: 'tbody',
16287                     cn : cal_rows()
16288                 }
16289                   
16290             ]
16291         };
16292          
16293          var cfg = {
16294             cls : 'fc fc-ltr',
16295             cn : [
16296                 header,
16297                 {
16298                     cls : 'fc-content',
16299                     style : "position: relative;",
16300                     cn : [
16301                         {
16302                             cls : 'fc-view fc-view-month fc-grid',
16303                             style : 'position: relative',
16304                             unselectable : 'on',
16305                             cn : [
16306                                 {
16307                                     cls : 'fc-event-container',
16308                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16309                                 },
16310                                 cal_table
16311                             ]
16312                         }
16313                     ]
16314     
16315                 }
16316            ] 
16317             
16318         };
16319         
16320          
16321         
16322         return cfg;
16323     },
16324     
16325     
16326     initEvents : function()
16327     {
16328         if(!this.store){
16329             throw "can not find store for calendar";
16330         }
16331         
16332         var mark = {
16333             tag: "div",
16334             cls:"x-dlg-mask",
16335             style: "text-align:center",
16336             cn: [
16337                 {
16338                     tag: "div",
16339                     style: "background-color:white;width:50%;margin:250 auto",
16340                     cn: [
16341                         {
16342                             tag: "img",
16343                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16344                         },
16345                         {
16346                             tag: "span",
16347                             html: "Loading"
16348                         }
16349                         
16350                     ]
16351                 }
16352             ]
16353         };
16354         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16355         
16356         var size = this.el.select('.fc-content', true).first().getSize();
16357         this.maskEl.setSize(size.width, size.height);
16358         this.maskEl.enableDisplayMode("block");
16359         if(!this.loadMask){
16360             this.maskEl.hide();
16361         }
16362         
16363         this.store = Roo.factory(this.store, Roo.data);
16364         this.store.on('load', this.onLoad, this);
16365         this.store.on('beforeload', this.onBeforeLoad, this);
16366         
16367         this.resize();
16368         
16369         this.cells = this.el.select('.fc-day',true);
16370         //Roo.log(this.cells);
16371         this.textNodes = this.el.query('.fc-day-number');
16372         this.cells.addClassOnOver('fc-state-hover');
16373         
16374         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16375         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16376         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16377         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16378         
16379         this.on('monthchange', this.onMonthChange, this);
16380         
16381         this.update(new Date().clearTime());
16382     },
16383     
16384     resize : function() {
16385         var sz  = this.el.getSize();
16386         
16387         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16388         this.el.select('.fc-day-content div',true).setHeight(34);
16389     },
16390     
16391     
16392     // private
16393     showPrevMonth : function(e){
16394         this.update(this.activeDate.add("mo", -1));
16395     },
16396     showToday : function(e){
16397         this.update(new Date().clearTime());
16398     },
16399     // private
16400     showNextMonth : function(e){
16401         this.update(this.activeDate.add("mo", 1));
16402     },
16403
16404     // private
16405     showPrevYear : function(){
16406         this.update(this.activeDate.add("y", -1));
16407     },
16408
16409     // private
16410     showNextYear : function(){
16411         this.update(this.activeDate.add("y", 1));
16412     },
16413
16414     
16415    // private
16416     update : function(date)
16417     {
16418         var vd = this.activeDate;
16419         this.activeDate = date;
16420 //        if(vd && this.el){
16421 //            var t = date.getTime();
16422 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16423 //                Roo.log('using add remove');
16424 //                
16425 //                this.fireEvent('monthchange', this, date);
16426 //                
16427 //                this.cells.removeClass("fc-state-highlight");
16428 //                this.cells.each(function(c){
16429 //                   if(c.dateValue == t){
16430 //                       c.addClass("fc-state-highlight");
16431 //                       setTimeout(function(){
16432 //                            try{c.dom.firstChild.focus();}catch(e){}
16433 //                       }, 50);
16434 //                       return false;
16435 //                   }
16436 //                   return true;
16437 //                });
16438 //                return;
16439 //            }
16440 //        }
16441         
16442         var days = date.getDaysInMonth();
16443         
16444         var firstOfMonth = date.getFirstDateOfMonth();
16445         var startingPos = firstOfMonth.getDay()-this.startDay;
16446         
16447         if(startingPos < this.startDay){
16448             startingPos += 7;
16449         }
16450         
16451         var pm = date.add(Date.MONTH, -1);
16452         var prevStart = pm.getDaysInMonth()-startingPos;
16453 //        
16454         this.cells = this.el.select('.fc-day',true);
16455         this.textNodes = this.el.query('.fc-day-number');
16456         this.cells.addClassOnOver('fc-state-hover');
16457         
16458         var cells = this.cells.elements;
16459         var textEls = this.textNodes;
16460         
16461         Roo.each(cells, function(cell){
16462             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16463         });
16464         
16465         days += startingPos;
16466
16467         // convert everything to numbers so it's fast
16468         var day = 86400000;
16469         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16470         //Roo.log(d);
16471         //Roo.log(pm);
16472         //Roo.log(prevStart);
16473         
16474         var today = new Date().clearTime().getTime();
16475         var sel = date.clearTime().getTime();
16476         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16477         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16478         var ddMatch = this.disabledDatesRE;
16479         var ddText = this.disabledDatesText;
16480         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16481         var ddaysText = this.disabledDaysText;
16482         var format = this.format;
16483         
16484         var setCellClass = function(cal, cell){
16485             cell.row = 0;
16486             cell.events = [];
16487             cell.more = [];
16488             //Roo.log('set Cell Class');
16489             cell.title = "";
16490             var t = d.getTime();
16491             
16492             //Roo.log(d);
16493             
16494             cell.dateValue = t;
16495             if(t == today){
16496                 cell.className += " fc-today";
16497                 cell.className += " fc-state-highlight";
16498                 cell.title = cal.todayText;
16499             }
16500             if(t == sel){
16501                 // disable highlight in other month..
16502                 //cell.className += " fc-state-highlight";
16503                 
16504             }
16505             // disabling
16506             if(t < min) {
16507                 cell.className = " fc-state-disabled";
16508                 cell.title = cal.minText;
16509                 return;
16510             }
16511             if(t > max) {
16512                 cell.className = " fc-state-disabled";
16513                 cell.title = cal.maxText;
16514                 return;
16515             }
16516             if(ddays){
16517                 if(ddays.indexOf(d.getDay()) != -1){
16518                     cell.title = ddaysText;
16519                     cell.className = " fc-state-disabled";
16520                 }
16521             }
16522             if(ddMatch && format){
16523                 var fvalue = d.dateFormat(format);
16524                 if(ddMatch.test(fvalue)){
16525                     cell.title = ddText.replace("%0", fvalue);
16526                     cell.className = " fc-state-disabled";
16527                 }
16528             }
16529             
16530             if (!cell.initialClassName) {
16531                 cell.initialClassName = cell.dom.className;
16532             }
16533             
16534             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16535         };
16536
16537         var i = 0;
16538         
16539         for(; i < startingPos; i++) {
16540             textEls[i].innerHTML = (++prevStart);
16541             d.setDate(d.getDate()+1);
16542             
16543             cells[i].className = "fc-past fc-other-month";
16544             setCellClass(this, cells[i]);
16545         }
16546         
16547         var intDay = 0;
16548         
16549         for(; i < days; i++){
16550             intDay = i - startingPos + 1;
16551             textEls[i].innerHTML = (intDay);
16552             d.setDate(d.getDate()+1);
16553             
16554             cells[i].className = ''; // "x-date-active";
16555             setCellClass(this, cells[i]);
16556         }
16557         var extraDays = 0;
16558         
16559         for(; i < 42; i++) {
16560             textEls[i].innerHTML = (++extraDays);
16561             d.setDate(d.getDate()+1);
16562             
16563             cells[i].className = "fc-future fc-other-month";
16564             setCellClass(this, cells[i]);
16565         }
16566         
16567         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16568         
16569         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16570         
16571         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16572         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16573         
16574         if(totalRows != 6){
16575             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16576             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16577         }
16578         
16579         this.fireEvent('monthchange', this, date);
16580         
16581         
16582         /*
16583         if(!this.internalRender){
16584             var main = this.el.dom.firstChild;
16585             var w = main.offsetWidth;
16586             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16587             Roo.fly(main).setWidth(w);
16588             this.internalRender = true;
16589             // opera does not respect the auto grow header center column
16590             // then, after it gets a width opera refuses to recalculate
16591             // without a second pass
16592             if(Roo.isOpera && !this.secondPass){
16593                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16594                 this.secondPass = true;
16595                 this.update.defer(10, this, [date]);
16596             }
16597         }
16598         */
16599         
16600     },
16601     
16602     findCell : function(dt) {
16603         dt = dt.clearTime().getTime();
16604         var ret = false;
16605         this.cells.each(function(c){
16606             //Roo.log("check " +c.dateValue + '?=' + dt);
16607             if(c.dateValue == dt){
16608                 ret = c;
16609                 return false;
16610             }
16611             return true;
16612         });
16613         
16614         return ret;
16615     },
16616     
16617     findCells : function(ev) {
16618         var s = ev.start.clone().clearTime().getTime();
16619        // Roo.log(s);
16620         var e= ev.end.clone().clearTime().getTime();
16621        // Roo.log(e);
16622         var ret = [];
16623         this.cells.each(function(c){
16624              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16625             
16626             if(c.dateValue > e){
16627                 return ;
16628             }
16629             if(c.dateValue < s){
16630                 return ;
16631             }
16632             ret.push(c);
16633         });
16634         
16635         return ret;    
16636     },
16637     
16638 //    findBestRow: function(cells)
16639 //    {
16640 //        var ret = 0;
16641 //        
16642 //        for (var i =0 ; i < cells.length;i++) {
16643 //            ret  = Math.max(cells[i].rows || 0,ret);
16644 //        }
16645 //        return ret;
16646 //        
16647 //    },
16648     
16649     
16650     addItem : function(ev)
16651     {
16652         // look for vertical location slot in
16653         var cells = this.findCells(ev);
16654         
16655 //        ev.row = this.findBestRow(cells);
16656         
16657         // work out the location.
16658         
16659         var crow = false;
16660         var rows = [];
16661         for(var i =0; i < cells.length; i++) {
16662             
16663             cells[i].row = cells[0].row;
16664             
16665             if(i == 0){
16666                 cells[i].row = cells[i].row + 1;
16667             }
16668             
16669             if (!crow) {
16670                 crow = {
16671                     start : cells[i],
16672                     end :  cells[i]
16673                 };
16674                 continue;
16675             }
16676             if (crow.start.getY() == cells[i].getY()) {
16677                 // on same row.
16678                 crow.end = cells[i];
16679                 continue;
16680             }
16681             // different row.
16682             rows.push(crow);
16683             crow = {
16684                 start: cells[i],
16685                 end : cells[i]
16686             };
16687             
16688         }
16689         
16690         rows.push(crow);
16691         ev.els = [];
16692         ev.rows = rows;
16693         ev.cells = cells;
16694         
16695         cells[0].events.push(ev);
16696         
16697         this.calevents.push(ev);
16698     },
16699     
16700     clearEvents: function() {
16701         
16702         if(!this.calevents){
16703             return;
16704         }
16705         
16706         Roo.each(this.cells.elements, function(c){
16707             c.row = 0;
16708             c.events = [];
16709             c.more = [];
16710         });
16711         
16712         Roo.each(this.calevents, function(e) {
16713             Roo.each(e.els, function(el) {
16714                 el.un('mouseenter' ,this.onEventEnter, this);
16715                 el.un('mouseleave' ,this.onEventLeave, this);
16716                 el.remove();
16717             },this);
16718         },this);
16719         
16720         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16721             e.remove();
16722         });
16723         
16724     },
16725     
16726     renderEvents: function()
16727     {   
16728         var _this = this;
16729         
16730         this.cells.each(function(c) {
16731             
16732             if(c.row < 5){
16733                 return;
16734             }
16735             
16736             var ev = c.events;
16737             
16738             var r = 4;
16739             if(c.row != c.events.length){
16740                 r = 4 - (4 - (c.row - c.events.length));
16741             }
16742             
16743             c.events = ev.slice(0, r);
16744             c.more = ev.slice(r);
16745             
16746             if(c.more.length && c.more.length == 1){
16747                 c.events.push(c.more.pop());
16748             }
16749             
16750             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16751             
16752         });
16753             
16754         this.cells.each(function(c) {
16755             
16756             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16757             
16758             
16759             for (var e = 0; e < c.events.length; e++){
16760                 var ev = c.events[e];
16761                 var rows = ev.rows;
16762                 
16763                 for(var i = 0; i < rows.length; i++) {
16764                 
16765                     // how many rows should it span..
16766
16767                     var  cfg = {
16768                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16769                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16770
16771                         unselectable : "on",
16772                         cn : [
16773                             {
16774                                 cls: 'fc-event-inner',
16775                                 cn : [
16776     //                                {
16777     //                                  tag:'span',
16778     //                                  cls: 'fc-event-time',
16779     //                                  html : cells.length > 1 ? '' : ev.time
16780     //                                },
16781                                     {
16782                                       tag:'span',
16783                                       cls: 'fc-event-title',
16784                                       html : String.format('{0}', ev.title)
16785                                     }
16786
16787
16788                                 ]
16789                             },
16790                             {
16791                                 cls: 'ui-resizable-handle ui-resizable-e',
16792                                 html : '&nbsp;&nbsp;&nbsp'
16793                             }
16794
16795                         ]
16796                     };
16797
16798                     if (i == 0) {
16799                         cfg.cls += ' fc-event-start';
16800                     }
16801                     if ((i+1) == rows.length) {
16802                         cfg.cls += ' fc-event-end';
16803                     }
16804
16805                     var ctr = _this.el.select('.fc-event-container',true).first();
16806                     var cg = ctr.createChild(cfg);
16807
16808                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16809                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16810
16811                     var r = (c.more.length) ? 1 : 0;
16812                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16813                     cg.setWidth(ebox.right - sbox.x -2);
16814
16815                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16816                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16817                     cg.on('click', _this.onEventClick, _this, ev);
16818
16819                     ev.els.push(cg);
16820                     
16821                 }
16822                 
16823             }
16824             
16825             
16826             if(c.more.length){
16827                 var  cfg = {
16828                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16829                     style : 'position: absolute',
16830                     unselectable : "on",
16831                     cn : [
16832                         {
16833                             cls: 'fc-event-inner',
16834                             cn : [
16835                                 {
16836                                   tag:'span',
16837                                   cls: 'fc-event-title',
16838                                   html : 'More'
16839                                 }
16840
16841
16842                             ]
16843                         },
16844                         {
16845                             cls: 'ui-resizable-handle ui-resizable-e',
16846                             html : '&nbsp;&nbsp;&nbsp'
16847                         }
16848
16849                     ]
16850                 };
16851
16852                 var ctr = _this.el.select('.fc-event-container',true).first();
16853                 var cg = ctr.createChild(cfg);
16854
16855                 var sbox = c.select('.fc-day-content',true).first().getBox();
16856                 var ebox = c.select('.fc-day-content',true).first().getBox();
16857                 //Roo.log(cg);
16858                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
16859                 cg.setWidth(ebox.right - sbox.x -2);
16860
16861                 cg.on('click', _this.onMoreEventClick, _this, c.more);
16862                 
16863             }
16864             
16865         });
16866         
16867         
16868         
16869     },
16870     
16871     onEventEnter: function (e, el,event,d) {
16872         this.fireEvent('evententer', this, el, event);
16873     },
16874     
16875     onEventLeave: function (e, el,event,d) {
16876         this.fireEvent('eventleave', this, el, event);
16877     },
16878     
16879     onEventClick: function (e, el,event,d) {
16880         this.fireEvent('eventclick', this, el, event);
16881     },
16882     
16883     onMonthChange: function () {
16884         this.store.load();
16885     },
16886     
16887     onMoreEventClick: function(e, el, more)
16888     {
16889         var _this = this;
16890         
16891         this.calpopover.placement = 'right';
16892         this.calpopover.setTitle('More');
16893         
16894         this.calpopover.setContent('');
16895         
16896         var ctr = this.calpopover.el.select('.popover-content', true).first();
16897         
16898         Roo.each(more, function(m){
16899             var cfg = {
16900                 cls : 'fc-event-hori fc-event-draggable',
16901                 html : m.title
16902             };
16903             var cg = ctr.createChild(cfg);
16904             
16905             cg.on('click', _this.onEventClick, _this, m);
16906         });
16907         
16908         this.calpopover.show(el);
16909         
16910         
16911     },
16912     
16913     onLoad: function () 
16914     {   
16915         this.calevents = [];
16916         var cal = this;
16917         
16918         if(this.store.getCount() > 0){
16919             this.store.data.each(function(d){
16920                cal.addItem({
16921                     id : d.data.id,
16922                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16923                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16924                     time : d.data.start_time,
16925                     title : d.data.title,
16926                     description : d.data.description,
16927                     venue : d.data.venue
16928                 });
16929             });
16930         }
16931         
16932         this.renderEvents();
16933         
16934         if(this.calevents.length && this.loadMask){
16935             this.maskEl.hide();
16936         }
16937     },
16938     
16939     onBeforeLoad: function()
16940     {
16941         this.clearEvents();
16942         if(this.loadMask){
16943             this.maskEl.show();
16944         }
16945     }
16946 });
16947
16948  
16949  /*
16950  * - LGPL
16951  *
16952  * element
16953  * 
16954  */
16955
16956 /**
16957  * @class Roo.bootstrap.Popover
16958  * @extends Roo.bootstrap.Component
16959  * Bootstrap Popover class
16960  * @cfg {String} html contents of the popover   (or false to use children..)
16961  * @cfg {String} title of popover (or false to hide)
16962  * @cfg {String} placement how it is placed
16963  * @cfg {String} trigger click || hover (or false to trigger manually)
16964  * @cfg {String} over what (parent or false to trigger manually.)
16965  * @cfg {Number} delay - delay before showing
16966  
16967  * @constructor
16968  * Create a new Popover
16969  * @param {Object} config The config object
16970  */
16971
16972 Roo.bootstrap.Popover = function(config){
16973     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16974     
16975     this.addEvents({
16976         // raw events
16977          /**
16978          * @event show
16979          * After the popover show
16980          * 
16981          * @param {Roo.bootstrap.Popover} this
16982          */
16983         "show" : true,
16984         /**
16985          * @event hide
16986          * After the popover hide
16987          * 
16988          * @param {Roo.bootstrap.Popover} this
16989          */
16990         "hide" : true
16991     });
16992 };
16993
16994 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
16995     
16996     title: 'Fill in a title',
16997     html: false,
16998     
16999     placement : 'right',
17000     trigger : 'hover', // hover
17001     
17002     delay : 0,
17003     
17004     over: 'parent',
17005     
17006     can_build_overlaid : false,
17007     
17008     getChildContainer : function()
17009     {
17010         return this.el.select('.popover-content',true).first();
17011     },
17012     
17013     getAutoCreate : function(){
17014          
17015         var cfg = {
17016            cls : 'popover roo-dynamic',
17017            style: 'display:block',
17018            cn : [
17019                 {
17020                     cls : 'arrow'
17021                 },
17022                 {
17023                     cls : 'popover-inner',
17024                     cn : [
17025                         {
17026                             tag: 'h3',
17027                             cls: 'popover-title',
17028                             html : this.title
17029                         },
17030                         {
17031                             cls : 'popover-content',
17032                             html : this.html
17033                         }
17034                     ]
17035                     
17036                 }
17037            ]
17038         };
17039         
17040         return cfg;
17041     },
17042     setTitle: function(str)
17043     {
17044         this.title = str;
17045         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17046     },
17047     setContent: function(str)
17048     {
17049         this.html = str;
17050         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17051     },
17052     // as it get's added to the bottom of the page.
17053     onRender : function(ct, position)
17054     {
17055         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17056         if(!this.el){
17057             var cfg = Roo.apply({},  this.getAutoCreate());
17058             cfg.id = Roo.id();
17059             
17060             if (this.cls) {
17061                 cfg.cls += ' ' + this.cls;
17062             }
17063             if (this.style) {
17064                 cfg.style = this.style;
17065             }
17066             //Roo.log("adding to ");
17067             this.el = Roo.get(document.body).createChild(cfg, position);
17068 //            Roo.log(this.el);
17069         }
17070         this.initEvents();
17071     },
17072     
17073     initEvents : function()
17074     {
17075         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17076         this.el.enableDisplayMode('block');
17077         this.el.hide();
17078         if (this.over === false) {
17079             return; 
17080         }
17081         if (this.triggers === false) {
17082             return;
17083         }
17084         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17085         var triggers = this.trigger ? this.trigger.split(' ') : [];
17086         Roo.each(triggers, function(trigger) {
17087         
17088             if (trigger == 'click') {
17089                 on_el.on('click', this.toggle, this);
17090             } else if (trigger != 'manual') {
17091                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17092                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17093       
17094                 on_el.on(eventIn  ,this.enter, this);
17095                 on_el.on(eventOut, this.leave, this);
17096             }
17097         }, this);
17098         
17099     },
17100     
17101     
17102     // private
17103     timeout : null,
17104     hoverState : null,
17105     
17106     toggle : function () {
17107         this.hoverState == 'in' ? this.leave() : this.enter();
17108     },
17109     
17110     enter : function () {
17111         
17112         clearTimeout(this.timeout);
17113     
17114         this.hoverState = 'in';
17115     
17116         if (!this.delay || !this.delay.show) {
17117             this.show();
17118             return;
17119         }
17120         var _t = this;
17121         this.timeout = setTimeout(function () {
17122             if (_t.hoverState == 'in') {
17123                 _t.show();
17124             }
17125         }, this.delay.show)
17126     },
17127     
17128     leave : function() {
17129         clearTimeout(this.timeout);
17130     
17131         this.hoverState = 'out';
17132     
17133         if (!this.delay || !this.delay.hide) {
17134             this.hide();
17135             return;
17136         }
17137         var _t = this;
17138         this.timeout = setTimeout(function () {
17139             if (_t.hoverState == 'out') {
17140                 _t.hide();
17141             }
17142         }, this.delay.hide)
17143     },
17144     
17145     show : function (on_el)
17146     {
17147         if (!on_el) {
17148             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17149         }
17150         
17151         // set content.
17152         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17153         if (this.html !== false) {
17154             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17155         }
17156         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17157         if (!this.title.length) {
17158             this.el.select('.popover-title',true).hide();
17159         }
17160         
17161         var placement = typeof this.placement == 'function' ?
17162             this.placement.call(this, this.el, on_el) :
17163             this.placement;
17164             
17165         var autoToken = /\s?auto?\s?/i;
17166         var autoPlace = autoToken.test(placement);
17167         if (autoPlace) {
17168             placement = placement.replace(autoToken, '') || 'top';
17169         }
17170         
17171         //this.el.detach()
17172         //this.el.setXY([0,0]);
17173         this.el.show();
17174         this.el.dom.style.display='block';
17175         this.el.addClass(placement);
17176         
17177         //this.el.appendTo(on_el);
17178         
17179         var p = this.getPosition();
17180         var box = this.el.getBox();
17181         
17182         if (autoPlace) {
17183             // fixme..
17184         }
17185         var align = Roo.bootstrap.Popover.alignment[placement];
17186         this.el.alignTo(on_el, align[0],align[1]);
17187         //var arrow = this.el.select('.arrow',true).first();
17188         //arrow.set(align[2], 
17189         
17190         this.el.addClass('in');
17191         
17192         
17193         if (this.el.hasClass('fade')) {
17194             // fade it?
17195         }
17196         
17197         this.hoverState = 'in';
17198         
17199         this.fireEvent('show', this);
17200         
17201     },
17202     hide : function()
17203     {
17204         this.el.setXY([0,0]);
17205         this.el.removeClass('in');
17206         this.el.hide();
17207         this.hoverState = null;
17208         
17209         this.fireEvent('hide', this);
17210     }
17211     
17212 });
17213
17214 Roo.bootstrap.Popover.alignment = {
17215     'left' : ['r-l', [-10,0], 'right'],
17216     'right' : ['l-r', [10,0], 'left'],
17217     'bottom' : ['t-b', [0,10], 'top'],
17218     'top' : [ 'b-t', [0,-10], 'bottom']
17219 };
17220
17221  /*
17222  * - LGPL
17223  *
17224  * Progress
17225  * 
17226  */
17227
17228 /**
17229  * @class Roo.bootstrap.Progress
17230  * @extends Roo.bootstrap.Component
17231  * Bootstrap Progress class
17232  * @cfg {Boolean} striped striped of the progress bar
17233  * @cfg {Boolean} active animated of the progress bar
17234  * 
17235  * 
17236  * @constructor
17237  * Create a new Progress
17238  * @param {Object} config The config object
17239  */
17240
17241 Roo.bootstrap.Progress = function(config){
17242     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17243 };
17244
17245 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17246     
17247     striped : false,
17248     active: false,
17249     
17250     getAutoCreate : function(){
17251         var cfg = {
17252             tag: 'div',
17253             cls: 'progress'
17254         };
17255         
17256         
17257         if(this.striped){
17258             cfg.cls += ' progress-striped';
17259         }
17260       
17261         if(this.active){
17262             cfg.cls += ' active';
17263         }
17264         
17265         
17266         return cfg;
17267     }
17268    
17269 });
17270
17271  
17272
17273  /*
17274  * - LGPL
17275  *
17276  * ProgressBar
17277  * 
17278  */
17279
17280 /**
17281  * @class Roo.bootstrap.ProgressBar
17282  * @extends Roo.bootstrap.Component
17283  * Bootstrap ProgressBar class
17284  * @cfg {Number} aria_valuenow aria-value now
17285  * @cfg {Number} aria_valuemin aria-value min
17286  * @cfg {Number} aria_valuemax aria-value max
17287  * @cfg {String} label label for the progress bar
17288  * @cfg {String} panel (success | info | warning | danger )
17289  * @cfg {String} role role of the progress bar
17290  * @cfg {String} sr_only text
17291  * 
17292  * 
17293  * @constructor
17294  * Create a new ProgressBar
17295  * @param {Object} config The config object
17296  */
17297
17298 Roo.bootstrap.ProgressBar = function(config){
17299     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17300 };
17301
17302 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17303     
17304     aria_valuenow : 0,
17305     aria_valuemin : 0,
17306     aria_valuemax : 100,
17307     label : false,
17308     panel : false,
17309     role : false,
17310     sr_only: false,
17311     
17312     getAutoCreate : function()
17313     {
17314         
17315         var cfg = {
17316             tag: 'div',
17317             cls: 'progress-bar',
17318             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17319         };
17320         
17321         if(this.sr_only){
17322             cfg.cn = {
17323                 tag: 'span',
17324                 cls: 'sr-only',
17325                 html: this.sr_only
17326             }
17327         }
17328         
17329         if(this.role){
17330             cfg.role = this.role;
17331         }
17332         
17333         if(this.aria_valuenow){
17334             cfg['aria-valuenow'] = this.aria_valuenow;
17335         }
17336         
17337         if(this.aria_valuemin){
17338             cfg['aria-valuemin'] = this.aria_valuemin;
17339         }
17340         
17341         if(this.aria_valuemax){
17342             cfg['aria-valuemax'] = this.aria_valuemax;
17343         }
17344         
17345         if(this.label && !this.sr_only){
17346             cfg.html = this.label;
17347         }
17348         
17349         if(this.panel){
17350             cfg.cls += ' progress-bar-' + this.panel;
17351         }
17352         
17353         return cfg;
17354     },
17355     
17356     update : function(aria_valuenow)
17357     {
17358         this.aria_valuenow = aria_valuenow;
17359         
17360         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17361     }
17362    
17363 });
17364
17365  
17366
17367  /*
17368  * - LGPL
17369  *
17370  * column
17371  * 
17372  */
17373
17374 /**
17375  * @class Roo.bootstrap.TabGroup
17376  * @extends Roo.bootstrap.Column
17377  * Bootstrap Column class
17378  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17379  * @cfg {Boolean} carousel true to make the group behave like a carousel
17380  * @cfg {Boolean} bullets show bullets for the panels
17381  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17382  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17383  * @cfg {Boolean} showarrow (true|false) show arrow default true
17384  * 
17385  * @constructor
17386  * Create a new TabGroup
17387  * @param {Object} config The config object
17388  */
17389
17390 Roo.bootstrap.TabGroup = function(config){
17391     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17392     if (!this.navId) {
17393         this.navId = Roo.id();
17394     }
17395     this.tabs = [];
17396     Roo.bootstrap.TabGroup.register(this);
17397     
17398 };
17399
17400 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17401     
17402     carousel : false,
17403     transition : false,
17404     bullets : 0,
17405     timer : 0,
17406     autoslide : false,
17407     slideFn : false,
17408     slideOnTouch : false,
17409     showarrow : true,
17410     
17411     getAutoCreate : function()
17412     {
17413         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17414         
17415         cfg.cls += ' tab-content';
17416         
17417         if (this.carousel) {
17418             cfg.cls += ' carousel slide';
17419             
17420             cfg.cn = [{
17421                cls : 'carousel-inner',
17422                cn : []
17423             }];
17424         
17425             if(this.bullets  && !Roo.isTouch){
17426                 
17427                 var bullets = {
17428                     cls : 'carousel-bullets',
17429                     cn : []
17430                 };
17431                
17432                 if(this.bullets_cls){
17433                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17434                 }
17435                 
17436                 bullets.cn.push({
17437                     cls : 'clear'
17438                 });
17439                 
17440                 cfg.cn[0].cn.push(bullets);
17441             }
17442             
17443             if(this.showarrow){
17444                 cfg.cn[0].cn.push({
17445                     tag : 'div',
17446                     class : 'carousel-arrow',
17447                     cn : [
17448                         {
17449                             tag : 'div',
17450                             class : 'carousel-prev',
17451                             cn : [
17452                                 {
17453                                     tag : 'i',
17454                                     class : 'fa fa-chevron-left'
17455                                 }
17456                             ]
17457                         },
17458                         {
17459                             tag : 'div',
17460                             class : 'carousel-next',
17461                             cn : [
17462                                 {
17463                                     tag : 'i',
17464                                     class : 'fa fa-chevron-right'
17465                                 }
17466                             ]
17467                         }
17468                     ]
17469                 });
17470             }
17471             
17472         }
17473         
17474         return cfg;
17475     },
17476     
17477     initEvents:  function()
17478     {
17479 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17480 //            this.el.on("touchstart", this.onTouchStart, this);
17481 //        }
17482         
17483         if(this.autoslide){
17484             var _this = this;
17485             
17486             this.slideFn = window.setInterval(function() {
17487                 _this.showPanelNext();
17488             }, this.timer);
17489         }
17490         
17491         if(this.showarrow){
17492             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17493             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17494         }
17495         
17496         
17497     },
17498     
17499 //    onTouchStart : function(e, el, o)
17500 //    {
17501 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17502 //            return;
17503 //        }
17504 //        
17505 //        this.showPanelNext();
17506 //    },
17507     
17508     
17509     getChildContainer : function()
17510     {
17511         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17512     },
17513     
17514     /**
17515     * register a Navigation item
17516     * @param {Roo.bootstrap.NavItem} the navitem to add
17517     */
17518     register : function(item)
17519     {
17520         this.tabs.push( item);
17521         item.navId = this.navId; // not really needed..
17522         this.addBullet();
17523     
17524     },
17525     
17526     getActivePanel : function()
17527     {
17528         var r = false;
17529         Roo.each(this.tabs, function(t) {
17530             if (t.active) {
17531                 r = t;
17532                 return false;
17533             }
17534             return null;
17535         });
17536         return r;
17537         
17538     },
17539     getPanelByName : function(n)
17540     {
17541         var r = false;
17542         Roo.each(this.tabs, function(t) {
17543             if (t.tabId == n) {
17544                 r = t;
17545                 return false;
17546             }
17547             return null;
17548         });
17549         return r;
17550     },
17551     indexOfPanel : function(p)
17552     {
17553         var r = false;
17554         Roo.each(this.tabs, function(t,i) {
17555             if (t.tabId == p.tabId) {
17556                 r = i;
17557                 return false;
17558             }
17559             return null;
17560         });
17561         return r;
17562     },
17563     /**
17564      * show a specific panel
17565      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17566      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17567      */
17568     showPanel : function (pan)
17569     {
17570         if(this.transition || typeof(pan) == 'undefined'){
17571             Roo.log("waiting for the transitionend");
17572             return;
17573         }
17574         
17575         if (typeof(pan) == 'number') {
17576             pan = this.tabs[pan];
17577         }
17578         
17579         if (typeof(pan) == 'string') {
17580             pan = this.getPanelByName(pan);
17581         }
17582         
17583         var cur = this.getActivePanel();
17584         
17585         if(!pan || !cur){
17586             Roo.log('pan or acitve pan is undefined');
17587             return false;
17588         }
17589         
17590         if (pan.tabId == this.getActivePanel().tabId) {
17591             return true;
17592         }
17593         
17594         if (false === cur.fireEvent('beforedeactivate')) {
17595             return false;
17596         }
17597         
17598         if(this.bullets > 0 && !Roo.isTouch){
17599             this.setActiveBullet(this.indexOfPanel(pan));
17600         }
17601         
17602         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17603             
17604             this.transition = true;
17605             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17606             var lr = dir == 'next' ? 'left' : 'right';
17607             pan.el.addClass(dir); // or prev
17608             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17609             cur.el.addClass(lr); // or right
17610             pan.el.addClass(lr);
17611             
17612             var _this = this;
17613             cur.el.on('transitionend', function() {
17614                 Roo.log("trans end?");
17615                 
17616                 pan.el.removeClass([lr,dir]);
17617                 pan.setActive(true);
17618                 
17619                 cur.el.removeClass([lr]);
17620                 cur.setActive(false);
17621                 
17622                 _this.transition = false;
17623                 
17624             }, this, { single:  true } );
17625             
17626             return true;
17627         }
17628         
17629         cur.setActive(false);
17630         pan.setActive(true);
17631         
17632         return true;
17633         
17634     },
17635     showPanelNext : function()
17636     {
17637         var i = this.indexOfPanel(this.getActivePanel());
17638         
17639         if (i >= this.tabs.length - 1 && !this.autoslide) {
17640             return;
17641         }
17642         
17643         if (i >= this.tabs.length - 1 && this.autoslide) {
17644             i = -1;
17645         }
17646         
17647         this.showPanel(this.tabs[i+1]);
17648     },
17649     
17650     showPanelPrev : function()
17651     {
17652         var i = this.indexOfPanel(this.getActivePanel());
17653         
17654         if (i  < 1 && !this.autoslide) {
17655             return;
17656         }
17657         
17658         if (i < 1 && this.autoslide) {
17659             i = this.tabs.length;
17660         }
17661         
17662         this.showPanel(this.tabs[i-1]);
17663     },
17664     
17665     
17666     addBullet: function()
17667     {
17668         if(!this.bullets || Roo.isTouch){
17669             return;
17670         }
17671         var ctr = this.el.select('.carousel-bullets',true).first();
17672         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17673         var bullet = ctr.createChild({
17674             cls : 'bullet bullet-' + i
17675         },ctr.dom.lastChild);
17676         
17677         
17678         var _this = this;
17679         
17680         bullet.on('click', (function(e, el, o, ii, t){
17681
17682             e.preventDefault();
17683
17684             this.showPanel(ii);
17685
17686             if(this.autoslide && this.slideFn){
17687                 clearInterval(this.slideFn);
17688                 this.slideFn = window.setInterval(function() {
17689                     _this.showPanelNext();
17690                 }, this.timer);
17691             }
17692
17693         }).createDelegate(this, [i, bullet], true));
17694                 
17695         
17696     },
17697      
17698     setActiveBullet : function(i)
17699     {
17700         if(Roo.isTouch){
17701             return;
17702         }
17703         
17704         Roo.each(this.el.select('.bullet', true).elements, function(el){
17705             el.removeClass('selected');
17706         });
17707
17708         var bullet = this.el.select('.bullet-' + i, true).first();
17709         
17710         if(!bullet){
17711             return;
17712         }
17713         
17714         bullet.addClass('selected');
17715     }
17716     
17717     
17718   
17719 });
17720
17721  
17722
17723  
17724  
17725 Roo.apply(Roo.bootstrap.TabGroup, {
17726     
17727     groups: {},
17728      /**
17729     * register a Navigation Group
17730     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17731     */
17732     register : function(navgrp)
17733     {
17734         this.groups[navgrp.navId] = navgrp;
17735         
17736     },
17737     /**
17738     * fetch a Navigation Group based on the navigation ID
17739     * if one does not exist , it will get created.
17740     * @param {string} the navgroup to add
17741     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17742     */
17743     get: function(navId) {
17744         if (typeof(this.groups[navId]) == 'undefined') {
17745             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17746         }
17747         return this.groups[navId] ;
17748     }
17749     
17750     
17751     
17752 });
17753
17754  /*
17755  * - LGPL
17756  *
17757  * TabPanel
17758  * 
17759  */
17760
17761 /**
17762  * @class Roo.bootstrap.TabPanel
17763  * @extends Roo.bootstrap.Component
17764  * Bootstrap TabPanel class
17765  * @cfg {Boolean} active panel active
17766  * @cfg {String} html panel content
17767  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17768  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17769  * @cfg {String} href click to link..
17770  * 
17771  * 
17772  * @constructor
17773  * Create a new TabPanel
17774  * @param {Object} config The config object
17775  */
17776
17777 Roo.bootstrap.TabPanel = function(config){
17778     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17779     this.addEvents({
17780         /**
17781              * @event changed
17782              * Fires when the active status changes
17783              * @param {Roo.bootstrap.TabPanel} this
17784              * @param {Boolean} state the new state
17785             
17786          */
17787         'changed': true,
17788         /**
17789              * @event beforedeactivate
17790              * Fires before a tab is de-activated - can be used to do validation on a form.
17791              * @param {Roo.bootstrap.TabPanel} this
17792              * @return {Boolean} false if there is an error
17793             
17794          */
17795         'beforedeactivate': true
17796      });
17797     
17798     this.tabId = this.tabId || Roo.id();
17799   
17800 };
17801
17802 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17803     
17804     active: false,
17805     html: false,
17806     tabId: false,
17807     navId : false,
17808     href : '',
17809     
17810     getAutoCreate : function(){
17811         var cfg = {
17812             tag: 'div',
17813             // item is needed for carousel - not sure if it has any effect otherwise
17814             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17815             html: this.html || ''
17816         };
17817         
17818         if(this.active){
17819             cfg.cls += ' active';
17820         }
17821         
17822         if(this.tabId){
17823             cfg.tabId = this.tabId;
17824         }
17825         
17826         
17827         return cfg;
17828     },
17829     
17830     initEvents:  function()
17831     {
17832         var p = this.parent();
17833         
17834         this.navId = this.navId || p.navId;
17835         
17836         if (typeof(this.navId) != 'undefined') {
17837             // not really needed.. but just in case.. parent should be a NavGroup.
17838             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17839             
17840             tg.register(this);
17841             
17842             var i = tg.tabs.length - 1;
17843             
17844             if(this.active && tg.bullets > 0 && i < tg.bullets){
17845                 tg.setActiveBullet(i);
17846             }
17847         }
17848         
17849         this.el.on('click', this.onClick, this);
17850         
17851         if(Roo.isTouch){
17852             this.el.on("touchstart", this.onTouchStart, this);
17853             this.el.on("touchmove", this.onTouchMove, this);
17854             this.el.on("touchend", this.onTouchEnd, this);
17855         }
17856         
17857     },
17858     
17859     onRender : function(ct, position)
17860     {
17861         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17862     },
17863     
17864     setActive : function(state)
17865     {
17866         Roo.log("panel - set active " + this.tabId + "=" + state);
17867         
17868         this.active = state;
17869         if (!state) {
17870             this.el.removeClass('active');
17871             
17872         } else  if (!this.el.hasClass('active')) {
17873             this.el.addClass('active');
17874         }
17875         
17876         this.fireEvent('changed', this, state);
17877     },
17878     
17879     onClick : function(e)
17880     {
17881         e.preventDefault();
17882         
17883         if(!this.href.length){
17884             return;
17885         }
17886         
17887         window.location.href = this.href;
17888     },
17889     
17890     startX : 0,
17891     startY : 0,
17892     endX : 0,
17893     endY : 0,
17894     swiping : false,
17895     
17896     onTouchStart : function(e)
17897     {
17898         this.swiping = false;
17899         
17900         this.startX = e.browserEvent.touches[0].clientX;
17901         this.startY = e.browserEvent.touches[0].clientY;
17902     },
17903     
17904     onTouchMove : function(e)
17905     {
17906         this.swiping = true;
17907         
17908         this.endX = e.browserEvent.touches[0].clientX;
17909         this.endY = e.browserEvent.touches[0].clientY;
17910     },
17911     
17912     onTouchEnd : function(e)
17913     {
17914         if(!this.swiping){
17915             this.onClick(e);
17916             return;
17917         }
17918         
17919         var tabGroup = this.parent();
17920         
17921         if(this.endX > this.startX){ // swiping right
17922             tabGroup.showPanelPrev();
17923             return;
17924         }
17925         
17926         if(this.startX > this.endX){ // swiping left
17927             tabGroup.showPanelNext();
17928             return;
17929         }
17930     }
17931     
17932     
17933 });
17934  
17935
17936  
17937
17938  /*
17939  * - LGPL
17940  *
17941  * DateField
17942  * 
17943  */
17944
17945 /**
17946  * @class Roo.bootstrap.DateField
17947  * @extends Roo.bootstrap.Input
17948  * Bootstrap DateField class
17949  * @cfg {Number} weekStart default 0
17950  * @cfg {String} viewMode default empty, (months|years)
17951  * @cfg {String} minViewMode default empty, (months|years)
17952  * @cfg {Number} startDate default -Infinity
17953  * @cfg {Number} endDate default Infinity
17954  * @cfg {Boolean} todayHighlight default false
17955  * @cfg {Boolean} todayBtn default false
17956  * @cfg {Boolean} calendarWeeks default false
17957  * @cfg {Object} daysOfWeekDisabled default empty
17958  * @cfg {Boolean} singleMode default false (true | false)
17959  * 
17960  * @cfg {Boolean} keyboardNavigation default true
17961  * @cfg {String} language default en
17962  * 
17963  * @constructor
17964  * Create a new DateField
17965  * @param {Object} config The config object
17966  */
17967
17968 Roo.bootstrap.DateField = function(config){
17969     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
17970      this.addEvents({
17971             /**
17972              * @event show
17973              * Fires when this field show.
17974              * @param {Roo.bootstrap.DateField} this
17975              * @param {Mixed} date The date value
17976              */
17977             show : true,
17978             /**
17979              * @event show
17980              * Fires when this field hide.
17981              * @param {Roo.bootstrap.DateField} this
17982              * @param {Mixed} date The date value
17983              */
17984             hide : true,
17985             /**
17986              * @event select
17987              * Fires when select a date.
17988              * @param {Roo.bootstrap.DateField} this
17989              * @param {Mixed} date The date value
17990              */
17991             select : true,
17992             /**
17993              * @event beforeselect
17994              * Fires when before select a date.
17995              * @param {Roo.bootstrap.DateField} this
17996              * @param {Mixed} date The date value
17997              */
17998             beforeselect : true
17999         });
18000 };
18001
18002 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18003     
18004     /**
18005      * @cfg {String} format
18006      * The default date format string which can be overriden for localization support.  The format must be
18007      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18008      */
18009     format : "m/d/y",
18010     /**
18011      * @cfg {String} altFormats
18012      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18013      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18014      */
18015     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18016     
18017     weekStart : 0,
18018     
18019     viewMode : '',
18020     
18021     minViewMode : '',
18022     
18023     todayHighlight : false,
18024     
18025     todayBtn: false,
18026     
18027     language: 'en',
18028     
18029     keyboardNavigation: true,
18030     
18031     calendarWeeks: false,
18032     
18033     startDate: -Infinity,
18034     
18035     endDate: Infinity,
18036     
18037     daysOfWeekDisabled: [],
18038     
18039     _events: [],
18040     
18041     singleMode : false,
18042     
18043     UTCDate: function()
18044     {
18045         return new Date(Date.UTC.apply(Date, arguments));
18046     },
18047     
18048     UTCToday: function()
18049     {
18050         var today = new Date();
18051         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18052     },
18053     
18054     getDate: function() {
18055             var d = this.getUTCDate();
18056             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18057     },
18058     
18059     getUTCDate: function() {
18060             return this.date;
18061     },
18062     
18063     setDate: function(d) {
18064             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18065     },
18066     
18067     setUTCDate: function(d) {
18068             this.date = d;
18069             this.setValue(this.formatDate(this.date));
18070     },
18071         
18072     onRender: function(ct, position)
18073     {
18074         
18075         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18076         
18077         this.language = this.language || 'en';
18078         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18079         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18080         
18081         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18082         this.format = this.format || 'm/d/y';
18083         this.isInline = false;
18084         this.isInput = true;
18085         this.component = this.el.select('.add-on', true).first() || false;
18086         this.component = (this.component && this.component.length === 0) ? false : this.component;
18087         this.hasInput = this.component && this.inputEl().length;
18088         
18089         if (typeof(this.minViewMode === 'string')) {
18090             switch (this.minViewMode) {
18091                 case 'months':
18092                     this.minViewMode = 1;
18093                     break;
18094                 case 'years':
18095                     this.minViewMode = 2;
18096                     break;
18097                 default:
18098                     this.minViewMode = 0;
18099                     break;
18100             }
18101         }
18102         
18103         if (typeof(this.viewMode === 'string')) {
18104             switch (this.viewMode) {
18105                 case 'months':
18106                     this.viewMode = 1;
18107                     break;
18108                 case 'years':
18109                     this.viewMode = 2;
18110                     break;
18111                 default:
18112                     this.viewMode = 0;
18113                     break;
18114             }
18115         }
18116                 
18117         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18118         
18119 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18120         
18121         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18122         
18123         this.picker().on('mousedown', this.onMousedown, this);
18124         this.picker().on('click', this.onClick, this);
18125         
18126         this.picker().addClass('datepicker-dropdown');
18127         
18128         this.startViewMode = this.viewMode;
18129         
18130         if(this.singleMode){
18131             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18132                 v.setVisibilityMode(Roo.Element.DISPLAY);
18133                 v.hide();
18134             });
18135             
18136             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18137                 v.setStyle('width', '189px');
18138             });
18139         }
18140         
18141         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18142             if(!this.calendarWeeks){
18143                 v.remove();
18144                 return;
18145             }
18146             
18147             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18148             v.attr('colspan', function(i, val){
18149                 return parseInt(val) + 1;
18150             });
18151         });
18152                         
18153         
18154         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18155         
18156         this.setStartDate(this.startDate);
18157         this.setEndDate(this.endDate);
18158         
18159         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18160         
18161         this.fillDow();
18162         this.fillMonths();
18163         this.update();
18164         this.showMode();
18165         
18166         if(this.isInline) {
18167             this.show();
18168         }
18169     },
18170     
18171     picker : function()
18172     {
18173         return this.pickerEl;
18174 //        return this.el.select('.datepicker', true).first();
18175     },
18176     
18177     fillDow: function()
18178     {
18179         var dowCnt = this.weekStart;
18180         
18181         var dow = {
18182             tag: 'tr',
18183             cn: [
18184                 
18185             ]
18186         };
18187         
18188         if(this.calendarWeeks){
18189             dow.cn.push({
18190                 tag: 'th',
18191                 cls: 'cw',
18192                 html: '&nbsp;'
18193             })
18194         }
18195         
18196         while (dowCnt < this.weekStart + 7) {
18197             dow.cn.push({
18198                 tag: 'th',
18199                 cls: 'dow',
18200                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18201             });
18202         }
18203         
18204         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18205     },
18206     
18207     fillMonths: function()
18208     {    
18209         var i = 0;
18210         var months = this.picker().select('>.datepicker-months td', true).first();
18211         
18212         months.dom.innerHTML = '';
18213         
18214         while (i < 12) {
18215             var month = {
18216                 tag: 'span',
18217                 cls: 'month',
18218                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18219             };
18220             
18221             months.createChild(month);
18222         }
18223         
18224     },
18225     
18226     update: function()
18227     {
18228         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;
18229         
18230         if (this.date < this.startDate) {
18231             this.viewDate = new Date(this.startDate);
18232         } else if (this.date > this.endDate) {
18233             this.viewDate = new Date(this.endDate);
18234         } else {
18235             this.viewDate = new Date(this.date);
18236         }
18237         
18238         this.fill();
18239     },
18240     
18241     fill: function() 
18242     {
18243         var d = new Date(this.viewDate),
18244                 year = d.getUTCFullYear(),
18245                 month = d.getUTCMonth(),
18246                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18247                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18248                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18249                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18250                 currentDate = this.date && this.date.valueOf(),
18251                 today = this.UTCToday();
18252         
18253         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18254         
18255 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18256         
18257 //        this.picker.select('>tfoot th.today').
18258 //                                              .text(dates[this.language].today)
18259 //                                              .toggle(this.todayBtn !== false);
18260     
18261         this.updateNavArrows();
18262         this.fillMonths();
18263                                                 
18264         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18265         
18266         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18267          
18268         prevMonth.setUTCDate(day);
18269         
18270         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18271         
18272         var nextMonth = new Date(prevMonth);
18273         
18274         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18275         
18276         nextMonth = nextMonth.valueOf();
18277         
18278         var fillMonths = false;
18279         
18280         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18281         
18282         while(prevMonth.valueOf() < nextMonth) {
18283             var clsName = '';
18284             
18285             if (prevMonth.getUTCDay() === this.weekStart) {
18286                 if(fillMonths){
18287                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18288                 }
18289                     
18290                 fillMonths = {
18291                     tag: 'tr',
18292                     cn: []
18293                 };
18294                 
18295                 if(this.calendarWeeks){
18296                     // ISO 8601: First week contains first thursday.
18297                     // ISO also states week starts on Monday, but we can be more abstract here.
18298                     var
18299                     // Start of current week: based on weekstart/current date
18300                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18301                     // Thursday of this week
18302                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18303                     // First Thursday of year, year from thursday
18304                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18305                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18306                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18307                     
18308                     fillMonths.cn.push({
18309                         tag: 'td',
18310                         cls: 'cw',
18311                         html: calWeek
18312                     });
18313                 }
18314             }
18315             
18316             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18317                 clsName += ' old';
18318             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18319                 clsName += ' new';
18320             }
18321             if (this.todayHighlight &&
18322                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18323                 prevMonth.getUTCMonth() == today.getMonth() &&
18324                 prevMonth.getUTCDate() == today.getDate()) {
18325                 clsName += ' today';
18326             }
18327             
18328             if (currentDate && prevMonth.valueOf() === currentDate) {
18329                 clsName += ' active';
18330             }
18331             
18332             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18333                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18334                     clsName += ' disabled';
18335             }
18336             
18337             fillMonths.cn.push({
18338                 tag: 'td',
18339                 cls: 'day ' + clsName,
18340                 html: prevMonth.getDate()
18341             });
18342             
18343             prevMonth.setDate(prevMonth.getDate()+1);
18344         }
18345           
18346         var currentYear = this.date && this.date.getUTCFullYear();
18347         var currentMonth = this.date && this.date.getUTCMonth();
18348         
18349         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18350         
18351         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18352             v.removeClass('active');
18353             
18354             if(currentYear === year && k === currentMonth){
18355                 v.addClass('active');
18356             }
18357             
18358             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18359                 v.addClass('disabled');
18360             }
18361             
18362         });
18363         
18364         
18365         year = parseInt(year/10, 10) * 10;
18366         
18367         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18368         
18369         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18370         
18371         year -= 1;
18372         for (var i = -1; i < 11; i++) {
18373             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18374                 tag: 'span',
18375                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18376                 html: year
18377             });
18378             
18379             year += 1;
18380         }
18381     },
18382     
18383     showMode: function(dir) 
18384     {
18385         if (dir) {
18386             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18387         }
18388         
18389         Roo.each(this.picker().select('>div',true).elements, function(v){
18390             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18391             v.hide();
18392         });
18393         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18394     },
18395     
18396     place: function()
18397     {
18398         if(this.isInline) {
18399             return;
18400         }
18401         
18402         this.picker().removeClass(['bottom', 'top']);
18403         
18404         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18405             /*
18406              * place to the top of element!
18407              *
18408              */
18409             
18410             this.picker().addClass('top');
18411             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18412             
18413             return;
18414         }
18415         
18416         this.picker().addClass('bottom');
18417         
18418         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18419     },
18420     
18421     parseDate : function(value)
18422     {
18423         if(!value || value instanceof Date){
18424             return value;
18425         }
18426         var v = Date.parseDate(value, this.format);
18427         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18428             v = Date.parseDate(value, 'Y-m-d');
18429         }
18430         if(!v && this.altFormats){
18431             if(!this.altFormatsArray){
18432                 this.altFormatsArray = this.altFormats.split("|");
18433             }
18434             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18435                 v = Date.parseDate(value, this.altFormatsArray[i]);
18436             }
18437         }
18438         return v;
18439     },
18440     
18441     formatDate : function(date, fmt)
18442     {   
18443         return (!date || !(date instanceof Date)) ?
18444         date : date.dateFormat(fmt || this.format);
18445     },
18446     
18447     onFocus : function()
18448     {
18449         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18450         this.show();
18451     },
18452     
18453     onBlur : function()
18454     {
18455         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18456         
18457         var d = this.inputEl().getValue();
18458         
18459         this.setValue(d);
18460                 
18461         this.hide();
18462     },
18463     
18464     show : function()
18465     {
18466         this.picker().show();
18467         this.update();
18468         this.place();
18469         
18470         this.fireEvent('show', this, this.date);
18471     },
18472     
18473     hide : function()
18474     {
18475         if(this.isInline) {
18476             return;
18477         }
18478         this.picker().hide();
18479         this.viewMode = this.startViewMode;
18480         this.showMode();
18481         
18482         this.fireEvent('hide', this, this.date);
18483         
18484     },
18485     
18486     onMousedown: function(e)
18487     {
18488         e.stopPropagation();
18489         e.preventDefault();
18490     },
18491     
18492     keyup: function(e)
18493     {
18494         Roo.bootstrap.DateField.superclass.keyup.call(this);
18495         this.update();
18496     },
18497
18498     setValue: function(v)
18499     {
18500         if(this.fireEvent('beforeselect', this, v) !== false){
18501             var d = new Date(this.parseDate(v) ).clearTime();
18502         
18503             if(isNaN(d.getTime())){
18504                 this.date = this.viewDate = '';
18505                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18506                 return;
18507             }
18508
18509             v = this.formatDate(d);
18510
18511             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18512
18513             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18514
18515             this.update();
18516
18517             this.fireEvent('select', this, this.date);
18518         }
18519     },
18520     
18521     getValue: function()
18522     {
18523         return this.formatDate(this.date);
18524     },
18525     
18526     fireKey: function(e)
18527     {
18528         if (!this.picker().isVisible()){
18529             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18530                 this.show();
18531             }
18532             return;
18533         }
18534         
18535         var dateChanged = false,
18536         dir, day, month,
18537         newDate, newViewDate;
18538         
18539         switch(e.keyCode){
18540             case 27: // escape
18541                 this.hide();
18542                 e.preventDefault();
18543                 break;
18544             case 37: // left
18545             case 39: // right
18546                 if (!this.keyboardNavigation) {
18547                     break;
18548                 }
18549                 dir = e.keyCode == 37 ? -1 : 1;
18550                 
18551                 if (e.ctrlKey){
18552                     newDate = this.moveYear(this.date, dir);
18553                     newViewDate = this.moveYear(this.viewDate, dir);
18554                 } else if (e.shiftKey){
18555                     newDate = this.moveMonth(this.date, dir);
18556                     newViewDate = this.moveMonth(this.viewDate, dir);
18557                 } else {
18558                     newDate = new Date(this.date);
18559                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18560                     newViewDate = new Date(this.viewDate);
18561                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18562                 }
18563                 if (this.dateWithinRange(newDate)){
18564                     this.date = newDate;
18565                     this.viewDate = newViewDate;
18566                     this.setValue(this.formatDate(this.date));
18567 //                    this.update();
18568                     e.preventDefault();
18569                     dateChanged = true;
18570                 }
18571                 break;
18572             case 38: // up
18573             case 40: // down
18574                 if (!this.keyboardNavigation) {
18575                     break;
18576                 }
18577                 dir = e.keyCode == 38 ? -1 : 1;
18578                 if (e.ctrlKey){
18579                     newDate = this.moveYear(this.date, dir);
18580                     newViewDate = this.moveYear(this.viewDate, dir);
18581                 } else if (e.shiftKey){
18582                     newDate = this.moveMonth(this.date, dir);
18583                     newViewDate = this.moveMonth(this.viewDate, dir);
18584                 } else {
18585                     newDate = new Date(this.date);
18586                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18587                     newViewDate = new Date(this.viewDate);
18588                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18589                 }
18590                 if (this.dateWithinRange(newDate)){
18591                     this.date = newDate;
18592                     this.viewDate = newViewDate;
18593                     this.setValue(this.formatDate(this.date));
18594 //                    this.update();
18595                     e.preventDefault();
18596                     dateChanged = true;
18597                 }
18598                 break;
18599             case 13: // enter
18600                 this.setValue(this.formatDate(this.date));
18601                 this.hide();
18602                 e.preventDefault();
18603                 break;
18604             case 9: // tab
18605                 this.setValue(this.formatDate(this.date));
18606                 this.hide();
18607                 break;
18608             case 16: // shift
18609             case 17: // ctrl
18610             case 18: // alt
18611                 break;
18612             default :
18613                 this.hide();
18614                 
18615         }
18616     },
18617     
18618     
18619     onClick: function(e) 
18620     {
18621         e.stopPropagation();
18622         e.preventDefault();
18623         
18624         var target = e.getTarget();
18625         
18626         if(target.nodeName.toLowerCase() === 'i'){
18627             target = Roo.get(target).dom.parentNode;
18628         }
18629         
18630         var nodeName = target.nodeName;
18631         var className = target.className;
18632         var html = target.innerHTML;
18633         //Roo.log(nodeName);
18634         
18635         switch(nodeName.toLowerCase()) {
18636             case 'th':
18637                 switch(className) {
18638                     case 'switch':
18639                         this.showMode(1);
18640                         break;
18641                     case 'prev':
18642                     case 'next':
18643                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18644                         switch(this.viewMode){
18645                                 case 0:
18646                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18647                                         break;
18648                                 case 1:
18649                                 case 2:
18650                                         this.viewDate = this.moveYear(this.viewDate, dir);
18651                                         break;
18652                         }
18653                         this.fill();
18654                         break;
18655                     case 'today':
18656                         var date = new Date();
18657                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18658 //                        this.fill()
18659                         this.setValue(this.formatDate(this.date));
18660                         
18661                         this.hide();
18662                         break;
18663                 }
18664                 break;
18665             case 'span':
18666                 if (className.indexOf('disabled') < 0) {
18667                     this.viewDate.setUTCDate(1);
18668                     if (className.indexOf('month') > -1) {
18669                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18670                     } else {
18671                         var year = parseInt(html, 10) || 0;
18672                         this.viewDate.setUTCFullYear(year);
18673                         
18674                     }
18675                     
18676                     if(this.singleMode){
18677                         this.setValue(this.formatDate(this.viewDate));
18678                         this.hide();
18679                         return;
18680                     }
18681                     
18682                     this.showMode(-1);
18683                     this.fill();
18684                 }
18685                 break;
18686                 
18687             case 'td':
18688                 //Roo.log(className);
18689                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18690                     var day = parseInt(html, 10) || 1;
18691                     var year = this.viewDate.getUTCFullYear(),
18692                         month = this.viewDate.getUTCMonth();
18693
18694                     if (className.indexOf('old') > -1) {
18695                         if(month === 0 ){
18696                             month = 11;
18697                             year -= 1;
18698                         }else{
18699                             month -= 1;
18700                         }
18701                     } else if (className.indexOf('new') > -1) {
18702                         if (month == 11) {
18703                             month = 0;
18704                             year += 1;
18705                         } else {
18706                             month += 1;
18707                         }
18708                     }
18709                     //Roo.log([year,month,day]);
18710                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18711                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18712 //                    this.fill();
18713                     //Roo.log(this.formatDate(this.date));
18714                     this.setValue(this.formatDate(this.date));
18715                     this.hide();
18716                 }
18717                 break;
18718         }
18719     },
18720     
18721     setStartDate: function(startDate)
18722     {
18723         this.startDate = startDate || -Infinity;
18724         if (this.startDate !== -Infinity) {
18725             this.startDate = this.parseDate(this.startDate);
18726         }
18727         this.update();
18728         this.updateNavArrows();
18729     },
18730
18731     setEndDate: function(endDate)
18732     {
18733         this.endDate = endDate || Infinity;
18734         if (this.endDate !== Infinity) {
18735             this.endDate = this.parseDate(this.endDate);
18736         }
18737         this.update();
18738         this.updateNavArrows();
18739     },
18740     
18741     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18742     {
18743         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18744         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18745             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18746         }
18747         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18748             return parseInt(d, 10);
18749         });
18750         this.update();
18751         this.updateNavArrows();
18752     },
18753     
18754     updateNavArrows: function() 
18755     {
18756         if(this.singleMode){
18757             return;
18758         }
18759         
18760         var d = new Date(this.viewDate),
18761         year = d.getUTCFullYear(),
18762         month = d.getUTCMonth();
18763         
18764         Roo.each(this.picker().select('.prev', true).elements, function(v){
18765             v.show();
18766             switch (this.viewMode) {
18767                 case 0:
18768
18769                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18770                         v.hide();
18771                     }
18772                     break;
18773                 case 1:
18774                 case 2:
18775                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18776                         v.hide();
18777                     }
18778                     break;
18779             }
18780         });
18781         
18782         Roo.each(this.picker().select('.next', true).elements, function(v){
18783             v.show();
18784             switch (this.viewMode) {
18785                 case 0:
18786
18787                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18788                         v.hide();
18789                     }
18790                     break;
18791                 case 1:
18792                 case 2:
18793                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18794                         v.hide();
18795                     }
18796                     break;
18797             }
18798         })
18799     },
18800     
18801     moveMonth: function(date, dir)
18802     {
18803         if (!dir) {
18804             return date;
18805         }
18806         var new_date = new Date(date.valueOf()),
18807         day = new_date.getUTCDate(),
18808         month = new_date.getUTCMonth(),
18809         mag = Math.abs(dir),
18810         new_month, test;
18811         dir = dir > 0 ? 1 : -1;
18812         if (mag == 1){
18813             test = dir == -1
18814             // If going back one month, make sure month is not current month
18815             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18816             ? function(){
18817                 return new_date.getUTCMonth() == month;
18818             }
18819             // If going forward one month, make sure month is as expected
18820             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18821             : function(){
18822                 return new_date.getUTCMonth() != new_month;
18823             };
18824             new_month = month + dir;
18825             new_date.setUTCMonth(new_month);
18826             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18827             if (new_month < 0 || new_month > 11) {
18828                 new_month = (new_month + 12) % 12;
18829             }
18830         } else {
18831             // For magnitudes >1, move one month at a time...
18832             for (var i=0; i<mag; i++) {
18833                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18834                 new_date = this.moveMonth(new_date, dir);
18835             }
18836             // ...then reset the day, keeping it in the new month
18837             new_month = new_date.getUTCMonth();
18838             new_date.setUTCDate(day);
18839             test = function(){
18840                 return new_month != new_date.getUTCMonth();
18841             };
18842         }
18843         // Common date-resetting loop -- if date is beyond end of month, make it
18844         // end of month
18845         while (test()){
18846             new_date.setUTCDate(--day);
18847             new_date.setUTCMonth(new_month);
18848         }
18849         return new_date;
18850     },
18851
18852     moveYear: function(date, dir)
18853     {
18854         return this.moveMonth(date, dir*12);
18855     },
18856
18857     dateWithinRange: function(date)
18858     {
18859         return date >= this.startDate && date <= this.endDate;
18860     },
18861
18862     
18863     remove: function() 
18864     {
18865         this.picker().remove();
18866     },
18867     
18868     validateValue : function(value)
18869     {
18870         if(value.length < 1)  {
18871             if(this.allowBlank){
18872                 return true;
18873             }
18874             return false;
18875         }
18876         
18877         if(value.length < this.minLength){
18878             return false;
18879         }
18880         if(value.length > this.maxLength){
18881             return false;
18882         }
18883         if(this.vtype){
18884             var vt = Roo.form.VTypes;
18885             if(!vt[this.vtype](value, this)){
18886                 return false;
18887             }
18888         }
18889         if(typeof this.validator == "function"){
18890             var msg = this.validator(value);
18891             if(msg !== true){
18892                 return false;
18893             }
18894         }
18895         
18896         if(this.regex && !this.regex.test(value)){
18897             return false;
18898         }
18899         
18900         if(typeof(this.parseDate(value)) == 'undefined'){
18901             return false;
18902         }
18903         
18904         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
18905             return false;
18906         }      
18907         
18908         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
18909             return false;
18910         } 
18911         
18912         
18913         return true;
18914     }
18915    
18916 });
18917
18918 Roo.apply(Roo.bootstrap.DateField,  {
18919     
18920     head : {
18921         tag: 'thead',
18922         cn: [
18923         {
18924             tag: 'tr',
18925             cn: [
18926             {
18927                 tag: 'th',
18928                 cls: 'prev',
18929                 html: '<i class="fa fa-arrow-left"/>'
18930             },
18931             {
18932                 tag: 'th',
18933                 cls: 'switch',
18934                 colspan: '5'
18935             },
18936             {
18937                 tag: 'th',
18938                 cls: 'next',
18939                 html: '<i class="fa fa-arrow-right"/>'
18940             }
18941
18942             ]
18943         }
18944         ]
18945     },
18946     
18947     content : {
18948         tag: 'tbody',
18949         cn: [
18950         {
18951             tag: 'tr',
18952             cn: [
18953             {
18954                 tag: 'td',
18955                 colspan: '7'
18956             }
18957             ]
18958         }
18959         ]
18960     },
18961     
18962     footer : {
18963         tag: 'tfoot',
18964         cn: [
18965         {
18966             tag: 'tr',
18967             cn: [
18968             {
18969                 tag: 'th',
18970                 colspan: '7',
18971                 cls: 'today'
18972             }
18973                     
18974             ]
18975         }
18976         ]
18977     },
18978     
18979     dates:{
18980         en: {
18981             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
18982             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
18983             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
18984             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18985             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
18986             today: "Today"
18987         }
18988     },
18989     
18990     modes: [
18991     {
18992         clsName: 'days',
18993         navFnc: 'Month',
18994         navStep: 1
18995     },
18996     {
18997         clsName: 'months',
18998         navFnc: 'FullYear',
18999         navStep: 1
19000     },
19001     {
19002         clsName: 'years',
19003         navFnc: 'FullYear',
19004         navStep: 10
19005     }]
19006 });
19007
19008 Roo.apply(Roo.bootstrap.DateField,  {
19009   
19010     template : {
19011         tag: 'div',
19012         cls: 'datepicker dropdown-menu roo-dynamic',
19013         cn: [
19014         {
19015             tag: 'div',
19016             cls: 'datepicker-days',
19017             cn: [
19018             {
19019                 tag: 'table',
19020                 cls: 'table-condensed',
19021                 cn:[
19022                 Roo.bootstrap.DateField.head,
19023                 {
19024                     tag: 'tbody'
19025                 },
19026                 Roo.bootstrap.DateField.footer
19027                 ]
19028             }
19029             ]
19030         },
19031         {
19032             tag: 'div',
19033             cls: 'datepicker-months',
19034             cn: [
19035             {
19036                 tag: 'table',
19037                 cls: 'table-condensed',
19038                 cn:[
19039                 Roo.bootstrap.DateField.head,
19040                 Roo.bootstrap.DateField.content,
19041                 Roo.bootstrap.DateField.footer
19042                 ]
19043             }
19044             ]
19045         },
19046         {
19047             tag: 'div',
19048             cls: 'datepicker-years',
19049             cn: [
19050             {
19051                 tag: 'table',
19052                 cls: 'table-condensed',
19053                 cn:[
19054                 Roo.bootstrap.DateField.head,
19055                 Roo.bootstrap.DateField.content,
19056                 Roo.bootstrap.DateField.footer
19057                 ]
19058             }
19059             ]
19060         }
19061         ]
19062     }
19063 });
19064
19065  
19066
19067  /*
19068  * - LGPL
19069  *
19070  * TimeField
19071  * 
19072  */
19073
19074 /**
19075  * @class Roo.bootstrap.TimeField
19076  * @extends Roo.bootstrap.Input
19077  * Bootstrap DateField class
19078  * 
19079  * 
19080  * @constructor
19081  * Create a new TimeField
19082  * @param {Object} config The config object
19083  */
19084
19085 Roo.bootstrap.TimeField = function(config){
19086     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19087     this.addEvents({
19088             /**
19089              * @event show
19090              * Fires when this field show.
19091              * @param {Roo.bootstrap.DateField} thisthis
19092              * @param {Mixed} date The date value
19093              */
19094             show : true,
19095             /**
19096              * @event show
19097              * Fires when this field hide.
19098              * @param {Roo.bootstrap.DateField} this
19099              * @param {Mixed} date The date value
19100              */
19101             hide : true,
19102             /**
19103              * @event select
19104              * Fires when select a date.
19105              * @param {Roo.bootstrap.DateField} this
19106              * @param {Mixed} date The date value
19107              */
19108             select : true
19109         });
19110 };
19111
19112 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19113     
19114     /**
19115      * @cfg {String} format
19116      * The default time format string which can be overriden for localization support.  The format must be
19117      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19118      */
19119     format : "H:i",
19120        
19121     onRender: function(ct, position)
19122     {
19123         
19124         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19125                 
19126         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19127         
19128         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19129         
19130         this.pop = this.picker().select('>.datepicker-time',true).first();
19131         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19132         
19133         this.picker().on('mousedown', this.onMousedown, this);
19134         this.picker().on('click', this.onClick, this);
19135         
19136         this.picker().addClass('datepicker-dropdown');
19137     
19138         this.fillTime();
19139         this.update();
19140             
19141         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19142         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19143         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19144         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19145         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19146         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19147
19148     },
19149     
19150     fireKey: function(e){
19151         if (!this.picker().isVisible()){
19152             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19153                 this.show();
19154             }
19155             return;
19156         }
19157
19158         e.preventDefault();
19159         
19160         switch(e.keyCode){
19161             case 27: // escape
19162                 this.hide();
19163                 break;
19164             case 37: // left
19165             case 39: // right
19166                 this.onTogglePeriod();
19167                 break;
19168             case 38: // up
19169                 this.onIncrementMinutes();
19170                 break;
19171             case 40: // down
19172                 this.onDecrementMinutes();
19173                 break;
19174             case 13: // enter
19175             case 9: // tab
19176                 this.setTime();
19177                 break;
19178         }
19179     },
19180     
19181     onClick: function(e) {
19182         e.stopPropagation();
19183         e.preventDefault();
19184     },
19185     
19186     picker : function()
19187     {
19188         return this.el.select('.datepicker', true).first();
19189     },
19190     
19191     fillTime: function()
19192     {    
19193         var time = this.pop.select('tbody', true).first();
19194         
19195         time.dom.innerHTML = '';
19196         
19197         time.createChild({
19198             tag: 'tr',
19199             cn: [
19200                 {
19201                     tag: 'td',
19202                     cn: [
19203                         {
19204                             tag: 'a',
19205                             href: '#',
19206                             cls: 'btn',
19207                             cn: [
19208                                 {
19209                                     tag: 'span',
19210                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19211                                 }
19212                             ]
19213                         } 
19214                     ]
19215                 },
19216                 {
19217                     tag: 'td',
19218                     cls: 'separator'
19219                 },
19220                 {
19221                     tag: 'td',
19222                     cn: [
19223                         {
19224                             tag: 'a',
19225                             href: '#',
19226                             cls: 'btn',
19227                             cn: [
19228                                 {
19229                                     tag: 'span',
19230                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19231                                 }
19232                             ]
19233                         }
19234                     ]
19235                 },
19236                 {
19237                     tag: 'td',
19238                     cls: 'separator'
19239                 }
19240             ]
19241         });
19242         
19243         time.createChild({
19244             tag: 'tr',
19245             cn: [
19246                 {
19247                     tag: 'td',
19248                     cn: [
19249                         {
19250                             tag: 'span',
19251                             cls: 'timepicker-hour',
19252                             html: '00'
19253                         }  
19254                     ]
19255                 },
19256                 {
19257                     tag: 'td',
19258                     cls: 'separator',
19259                     html: ':'
19260                 },
19261                 {
19262                     tag: 'td',
19263                     cn: [
19264                         {
19265                             tag: 'span',
19266                             cls: 'timepicker-minute',
19267                             html: '00'
19268                         }  
19269                     ]
19270                 },
19271                 {
19272                     tag: 'td',
19273                     cls: 'separator'
19274                 },
19275                 {
19276                     tag: 'td',
19277                     cn: [
19278                         {
19279                             tag: 'button',
19280                             type: 'button',
19281                             cls: 'btn btn-primary period',
19282                             html: 'AM'
19283                             
19284                         }
19285                     ]
19286                 }
19287             ]
19288         });
19289         
19290         time.createChild({
19291             tag: 'tr',
19292             cn: [
19293                 {
19294                     tag: 'td',
19295                     cn: [
19296                         {
19297                             tag: 'a',
19298                             href: '#',
19299                             cls: 'btn',
19300                             cn: [
19301                                 {
19302                                     tag: 'span',
19303                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19304                                 }
19305                             ]
19306                         }
19307                     ]
19308                 },
19309                 {
19310                     tag: 'td',
19311                     cls: 'separator'
19312                 },
19313                 {
19314                     tag: 'td',
19315                     cn: [
19316                         {
19317                             tag: 'a',
19318                             href: '#',
19319                             cls: 'btn',
19320                             cn: [
19321                                 {
19322                                     tag: 'span',
19323                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19324                                 }
19325                             ]
19326                         }
19327                     ]
19328                 },
19329                 {
19330                     tag: 'td',
19331                     cls: 'separator'
19332                 }
19333             ]
19334         });
19335         
19336     },
19337     
19338     update: function()
19339     {
19340         
19341         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19342         
19343         this.fill();
19344     },
19345     
19346     fill: function() 
19347     {
19348         var hours = this.time.getHours();
19349         var minutes = this.time.getMinutes();
19350         var period = 'AM';
19351         
19352         if(hours > 11){
19353             period = 'PM';
19354         }
19355         
19356         if(hours == 0){
19357             hours = 12;
19358         }
19359         
19360         
19361         if(hours > 12){
19362             hours = hours - 12;
19363         }
19364         
19365         if(hours < 10){
19366             hours = '0' + hours;
19367         }
19368         
19369         if(minutes < 10){
19370             minutes = '0' + minutes;
19371         }
19372         
19373         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19374         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19375         this.pop.select('button', true).first().dom.innerHTML = period;
19376         
19377     },
19378     
19379     place: function()
19380     {   
19381         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19382         
19383         var cls = ['bottom'];
19384         
19385         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19386             cls.pop();
19387             cls.push('top');
19388         }
19389         
19390         cls.push('right');
19391         
19392         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19393             cls.pop();
19394             cls.push('left');
19395         }
19396         
19397         this.picker().addClass(cls.join('-'));
19398         
19399         var _this = this;
19400         
19401         Roo.each(cls, function(c){
19402             if(c == 'bottom'){
19403                 _this.picker().setTop(_this.inputEl().getHeight());
19404                 return;
19405             }
19406             if(c == 'top'){
19407                 _this.picker().setTop(0 - _this.picker().getHeight());
19408                 return;
19409             }
19410             
19411             if(c == 'left'){
19412                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19413                 return;
19414             }
19415             if(c == 'right'){
19416                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19417                 return;
19418             }
19419         });
19420         
19421     },
19422   
19423     onFocus : function()
19424     {
19425         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19426         this.show();
19427     },
19428     
19429     onBlur : function()
19430     {
19431         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19432         this.hide();
19433     },
19434     
19435     show : function()
19436     {
19437         this.picker().show();
19438         this.pop.show();
19439         this.update();
19440         this.place();
19441         
19442         this.fireEvent('show', this, this.date);
19443     },
19444     
19445     hide : function()
19446     {
19447         this.picker().hide();
19448         this.pop.hide();
19449         
19450         this.fireEvent('hide', this, this.date);
19451     },
19452     
19453     setTime : function()
19454     {
19455         this.hide();
19456         this.setValue(this.time.format(this.format));
19457         
19458         this.fireEvent('select', this, this.date);
19459         
19460         
19461     },
19462     
19463     onMousedown: function(e){
19464         e.stopPropagation();
19465         e.preventDefault();
19466     },
19467     
19468     onIncrementHours: function()
19469     {
19470         Roo.log('onIncrementHours');
19471         this.time = this.time.add(Date.HOUR, 1);
19472         this.update();
19473         
19474     },
19475     
19476     onDecrementHours: function()
19477     {
19478         Roo.log('onDecrementHours');
19479         this.time = this.time.add(Date.HOUR, -1);
19480         this.update();
19481     },
19482     
19483     onIncrementMinutes: function()
19484     {
19485         Roo.log('onIncrementMinutes');
19486         this.time = this.time.add(Date.MINUTE, 1);
19487         this.update();
19488     },
19489     
19490     onDecrementMinutes: function()
19491     {
19492         Roo.log('onDecrementMinutes');
19493         this.time = this.time.add(Date.MINUTE, -1);
19494         this.update();
19495     },
19496     
19497     onTogglePeriod: function()
19498     {
19499         Roo.log('onTogglePeriod');
19500         this.time = this.time.add(Date.HOUR, 12);
19501         this.update();
19502     }
19503     
19504    
19505 });
19506
19507 Roo.apply(Roo.bootstrap.TimeField,  {
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     footer : {
19525         tag: 'tfoot',
19526         cn: [
19527             {
19528                 tag: 'tr',
19529                 cn: [
19530                 {
19531                     tag: 'th',
19532                     colspan: '7',
19533                     cls: '',
19534                     cn: [
19535                         {
19536                             tag: 'button',
19537                             cls: 'btn btn-info ok',
19538                             html: 'OK'
19539                         }
19540                     ]
19541                 }
19542
19543                 ]
19544             }
19545         ]
19546     }
19547 });
19548
19549 Roo.apply(Roo.bootstrap.TimeField,  {
19550   
19551     template : {
19552         tag: 'div',
19553         cls: 'datepicker dropdown-menu',
19554         cn: [
19555             {
19556                 tag: 'div',
19557                 cls: 'datepicker-time',
19558                 cn: [
19559                 {
19560                     tag: 'table',
19561                     cls: 'table-condensed',
19562                     cn:[
19563                     Roo.bootstrap.TimeField.content,
19564                     Roo.bootstrap.TimeField.footer
19565                     ]
19566                 }
19567                 ]
19568             }
19569         ]
19570     }
19571 });
19572
19573  
19574
19575  /*
19576  * - LGPL
19577  *
19578  * MonthField
19579  * 
19580  */
19581
19582 /**
19583  * @class Roo.bootstrap.MonthField
19584  * @extends Roo.bootstrap.Input
19585  * Bootstrap MonthField class
19586  * 
19587  * @cfg {String} language default en
19588  * 
19589  * @constructor
19590  * Create a new MonthField
19591  * @param {Object} config The config object
19592  */
19593
19594 Roo.bootstrap.MonthField = function(config){
19595     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19596     
19597     this.addEvents({
19598         /**
19599          * @event show
19600          * Fires when this field show.
19601          * @param {Roo.bootstrap.MonthField} this
19602          * @param {Mixed} date The date value
19603          */
19604         show : true,
19605         /**
19606          * @event show
19607          * Fires when this field hide.
19608          * @param {Roo.bootstrap.MonthField} this
19609          * @param {Mixed} date The date value
19610          */
19611         hide : true,
19612         /**
19613          * @event select
19614          * Fires when select a date.
19615          * @param {Roo.bootstrap.MonthField} this
19616          * @param {String} oldvalue The old value
19617          * @param {String} newvalue The new value
19618          */
19619         select : true
19620     });
19621 };
19622
19623 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19624     
19625     onRender: function(ct, position)
19626     {
19627         
19628         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19629         
19630         this.language = this.language || 'en';
19631         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19632         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19633         
19634         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19635         this.isInline = false;
19636         this.isInput = true;
19637         this.component = this.el.select('.add-on', true).first() || false;
19638         this.component = (this.component && this.component.length === 0) ? false : this.component;
19639         this.hasInput = this.component && this.inputEL().length;
19640         
19641         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19642         
19643         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19644         
19645         this.picker().on('mousedown', this.onMousedown, this);
19646         this.picker().on('click', this.onClick, this);
19647         
19648         this.picker().addClass('datepicker-dropdown');
19649         
19650         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19651             v.setStyle('width', '189px');
19652         });
19653         
19654         this.fillMonths();
19655         
19656         this.update();
19657         
19658         if(this.isInline) {
19659             this.show();
19660         }
19661         
19662     },
19663     
19664     setValue: function(v, suppressEvent)
19665     {   
19666         var o = this.getValue();
19667         
19668         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19669         
19670         this.update();
19671
19672         if(suppressEvent !== true){
19673             this.fireEvent('select', this, o, v);
19674         }
19675         
19676     },
19677     
19678     getValue: function()
19679     {
19680         return this.value;
19681     },
19682     
19683     onClick: function(e) 
19684     {
19685         e.stopPropagation();
19686         e.preventDefault();
19687         
19688         var target = e.getTarget();
19689         
19690         if(target.nodeName.toLowerCase() === 'i'){
19691             target = Roo.get(target).dom.parentNode;
19692         }
19693         
19694         var nodeName = target.nodeName;
19695         var className = target.className;
19696         var html = target.innerHTML;
19697         
19698         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19699             return;
19700         }
19701         
19702         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19703         
19704         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19705         
19706         this.hide();
19707                         
19708     },
19709     
19710     picker : function()
19711     {
19712         return this.pickerEl;
19713     },
19714     
19715     fillMonths: function()
19716     {    
19717         var i = 0;
19718         var months = this.picker().select('>.datepicker-months td', true).first();
19719         
19720         months.dom.innerHTML = '';
19721         
19722         while (i < 12) {
19723             var month = {
19724                 tag: 'span',
19725                 cls: 'month',
19726                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19727             };
19728             
19729             months.createChild(month);
19730         }
19731         
19732     },
19733     
19734     update: function()
19735     {
19736         var _this = this;
19737         
19738         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19739             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19740         }
19741         
19742         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19743             e.removeClass('active');
19744             
19745             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19746                 e.addClass('active');
19747             }
19748         })
19749     },
19750     
19751     place: function()
19752     {
19753         if(this.isInline) {
19754             return;
19755         }
19756         
19757         this.picker().removeClass(['bottom', 'top']);
19758         
19759         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19760             /*
19761              * place to the top of element!
19762              *
19763              */
19764             
19765             this.picker().addClass('top');
19766             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19767             
19768             return;
19769         }
19770         
19771         this.picker().addClass('bottom');
19772         
19773         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19774     },
19775     
19776     onFocus : function()
19777     {
19778         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19779         this.show();
19780     },
19781     
19782     onBlur : function()
19783     {
19784         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19785         
19786         var d = this.inputEl().getValue();
19787         
19788         this.setValue(d);
19789                 
19790         this.hide();
19791     },
19792     
19793     show : function()
19794     {
19795         this.picker().show();
19796         this.picker().select('>.datepicker-months', true).first().show();
19797         this.update();
19798         this.place();
19799         
19800         this.fireEvent('show', this, this.date);
19801     },
19802     
19803     hide : function()
19804     {
19805         if(this.isInline) {
19806             return;
19807         }
19808         this.picker().hide();
19809         this.fireEvent('hide', this, this.date);
19810         
19811     },
19812     
19813     onMousedown: function(e)
19814     {
19815         e.stopPropagation();
19816         e.preventDefault();
19817     },
19818     
19819     keyup: function(e)
19820     {
19821         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19822         this.update();
19823     },
19824
19825     fireKey: function(e)
19826     {
19827         if (!this.picker().isVisible()){
19828             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19829                 this.show();
19830             }
19831             return;
19832         }
19833         
19834         var dir;
19835         
19836         switch(e.keyCode){
19837             case 27: // escape
19838                 this.hide();
19839                 e.preventDefault();
19840                 break;
19841             case 37: // left
19842             case 39: // right
19843                 dir = e.keyCode == 37 ? -1 : 1;
19844                 
19845                 this.vIndex = this.vIndex + dir;
19846                 
19847                 if(this.vIndex < 0){
19848                     this.vIndex = 0;
19849                 }
19850                 
19851                 if(this.vIndex > 11){
19852                     this.vIndex = 11;
19853                 }
19854                 
19855                 if(isNaN(this.vIndex)){
19856                     this.vIndex = 0;
19857                 }
19858                 
19859                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19860                 
19861                 break;
19862             case 38: // up
19863             case 40: // down
19864                 
19865                 dir = e.keyCode == 38 ? -1 : 1;
19866                 
19867                 this.vIndex = this.vIndex + dir * 4;
19868                 
19869                 if(this.vIndex < 0){
19870                     this.vIndex = 0;
19871                 }
19872                 
19873                 if(this.vIndex > 11){
19874                     this.vIndex = 11;
19875                 }
19876                 
19877                 if(isNaN(this.vIndex)){
19878                     this.vIndex = 0;
19879                 }
19880                 
19881                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19882                 break;
19883                 
19884             case 13: // enter
19885                 
19886                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19887                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19888                 }
19889                 
19890                 this.hide();
19891                 e.preventDefault();
19892                 break;
19893             case 9: // tab
19894                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19895                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19896                 }
19897                 this.hide();
19898                 break;
19899             case 16: // shift
19900             case 17: // ctrl
19901             case 18: // alt
19902                 break;
19903             default :
19904                 this.hide();
19905                 
19906         }
19907     },
19908     
19909     remove: function() 
19910     {
19911         this.picker().remove();
19912     }
19913    
19914 });
19915
19916 Roo.apply(Roo.bootstrap.MonthField,  {
19917     
19918     content : {
19919         tag: 'tbody',
19920         cn: [
19921         {
19922             tag: 'tr',
19923             cn: [
19924             {
19925                 tag: 'td',
19926                 colspan: '7'
19927             }
19928             ]
19929         }
19930         ]
19931     },
19932     
19933     dates:{
19934         en: {
19935             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19936             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19937         }
19938     }
19939 });
19940
19941 Roo.apply(Roo.bootstrap.MonthField,  {
19942   
19943     template : {
19944         tag: 'div',
19945         cls: 'datepicker dropdown-menu roo-dynamic',
19946         cn: [
19947             {
19948                 tag: 'div',
19949                 cls: 'datepicker-months',
19950                 cn: [
19951                 {
19952                     tag: 'table',
19953                     cls: 'table-condensed',
19954                     cn:[
19955                         Roo.bootstrap.DateField.content
19956                     ]
19957                 }
19958                 ]
19959             }
19960         ]
19961     }
19962 });
19963
19964  
19965
19966  
19967  /*
19968  * - LGPL
19969  *
19970  * CheckBox
19971  * 
19972  */
19973
19974 /**
19975  * @class Roo.bootstrap.CheckBox
19976  * @extends Roo.bootstrap.Input
19977  * Bootstrap CheckBox class
19978  * 
19979  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
19980  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
19981  * @cfg {String} boxLabel The text that appears beside the checkbox
19982  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
19983  * @cfg {Boolean} checked initnal the element
19984  * @cfg {Boolean} inline inline the element (default false)
19985  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
19986  * 
19987  * @constructor
19988  * Create a new CheckBox
19989  * @param {Object} config The config object
19990  */
19991
19992 Roo.bootstrap.CheckBox = function(config){
19993     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
19994    
19995     this.addEvents({
19996         /**
19997         * @event check
19998         * Fires when the element is checked or unchecked.
19999         * @param {Roo.bootstrap.CheckBox} this This input
20000         * @param {Boolean} checked The new checked value
20001         */
20002        check : true
20003     });
20004     
20005 };
20006
20007 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20008   
20009     inputType: 'checkbox',
20010     inputValue: 1,
20011     valueOff: 0,
20012     boxLabel: false,
20013     checked: false,
20014     weight : false,
20015     inline: false,
20016     
20017     getAutoCreate : function()
20018     {
20019         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20020         
20021         var id = Roo.id();
20022         
20023         var cfg = {};
20024         
20025         cfg.cls = 'form-group ' + this.inputType; //input-group
20026         
20027         if(this.inline){
20028             cfg.cls += ' ' + this.inputType + '-inline';
20029         }
20030         
20031         var input =  {
20032             tag: 'input',
20033             id : id,
20034             type : this.inputType,
20035             value : this.inputValue,
20036             cls : 'roo-' + this.inputType, //'form-box',
20037             placeholder : this.placeholder || ''
20038             
20039         };
20040         
20041         if(this.inputType != 'radio'){
20042             var hidden =  {
20043                 tag: 'input',
20044                 type : 'hidden',
20045                 cls : 'roo-hidden-value',
20046                 value : this.checked ? this.valueOff : this.inputValue
20047             };
20048         }
20049         
20050             
20051         if (this.weight) { // Validity check?
20052             cfg.cls += " " + this.inputType + "-" + this.weight;
20053         }
20054         
20055         if (this.disabled) {
20056             input.disabled=true;
20057         }
20058         
20059         if(this.checked){
20060             input.checked = this.checked;
20061             
20062         }
20063         
20064         
20065         if (this.name) {
20066             
20067             input.name = this.name;
20068             
20069             if(this.inputType != 'radio'){
20070                 hidden.name = this.name;
20071                 input.name = '_hidden_' + this.name;
20072             }
20073         }
20074         
20075         if (this.size) {
20076             input.cls += ' input-' + this.size;
20077         }
20078         
20079         var settings=this;
20080         
20081         ['xs','sm','md','lg'].map(function(size){
20082             if (settings[size]) {
20083                 cfg.cls += ' col-' + size + '-' + settings[size];
20084             }
20085         });
20086         
20087         var inputblock = input;
20088          
20089         if (this.before || this.after) {
20090             
20091             inputblock = {
20092                 cls : 'input-group',
20093                 cn :  [] 
20094             };
20095             
20096             if (this.before) {
20097                 inputblock.cn.push({
20098                     tag :'span',
20099                     cls : 'input-group-addon',
20100                     html : this.before
20101                 });
20102             }
20103             
20104             inputblock.cn.push(input);
20105             
20106             if(this.inputType != 'radio'){
20107                 inputblock.cn.push(hidden);
20108             }
20109             
20110             if (this.after) {
20111                 inputblock.cn.push({
20112                     tag :'span',
20113                     cls : 'input-group-addon',
20114                     html : this.after
20115                 });
20116             }
20117             
20118         }
20119         
20120         if (align ==='left' && this.fieldLabel.length) {
20121 //                Roo.log("left and has label");
20122             cfg.cn = [
20123                 {
20124                     tag: 'label',
20125                     'for' :  id,
20126                     cls : 'control-label',
20127                     html : this.fieldLabel
20128
20129                 },
20130                 {
20131                     cls : "", 
20132                     cn: [
20133                         inputblock
20134                     ]
20135                 }
20136             ];
20137             
20138             if(this.labelWidth > 12){
20139                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20140             }
20141             
20142             if(this.labelWidth < 13 && this.labelmd == 0){
20143                 this.labelmd = this.labelWidth;
20144             }
20145             
20146             if(this.labellg > 0){
20147                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20148                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20149             }
20150             
20151             if(this.labelmd > 0){
20152                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20153                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20154             }
20155             
20156             if(this.labelsm > 0){
20157                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20158                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20159             }
20160             
20161             if(this.labelxs > 0){
20162                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20163                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20164             }
20165             
20166         } else if ( this.fieldLabel.length) {
20167 //                Roo.log(" label");
20168                 cfg.cn = [
20169                    
20170                     {
20171                         tag: this.boxLabel ? 'span' : 'label',
20172                         'for': id,
20173                         cls: 'control-label box-input-label',
20174                         //cls : 'input-group-addon',
20175                         html : this.fieldLabel
20176                         
20177                     },
20178                     
20179                     inputblock
20180                     
20181                 ];
20182
20183         } else {
20184             
20185 //                Roo.log(" no label && no align");
20186                 cfg.cn = [  inputblock ] ;
20187                 
20188                 
20189         }
20190         
20191         if(this.boxLabel){
20192              var boxLabelCfg = {
20193                 tag: 'label',
20194                 //'for': id, // box label is handled by onclick - so no for...
20195                 cls: 'box-label',
20196                 html: this.boxLabel
20197             };
20198             
20199             if(this.tooltip){
20200                 boxLabelCfg.tooltip = this.tooltip;
20201             }
20202              
20203             cfg.cn.push(boxLabelCfg);
20204         }
20205         
20206         if(this.inputType != 'radio'){
20207             cfg.cn.push(hidden);
20208         }
20209         
20210         return cfg;
20211         
20212     },
20213     
20214     /**
20215      * return the real input element.
20216      */
20217     inputEl: function ()
20218     {
20219         return this.el.select('input.roo-' + this.inputType,true).first();
20220     },
20221     hiddenEl: function ()
20222     {
20223         return this.el.select('input.roo-hidden-value',true).first();
20224     },
20225     
20226     labelEl: function()
20227     {
20228         return this.el.select('label.control-label',true).first();
20229     },
20230     /* depricated... */
20231     
20232     label: function()
20233     {
20234         return this.labelEl();
20235     },
20236     
20237     boxLabelEl: function()
20238     {
20239         return this.el.select('label.box-label',true).first();
20240     },
20241     
20242     initEvents : function()
20243     {
20244 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20245         
20246         this.inputEl().on('click', this.onClick,  this);
20247         
20248         if (this.boxLabel) { 
20249             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20250         }
20251         
20252         this.startValue = this.getValue();
20253         
20254         if(this.groupId){
20255             Roo.bootstrap.CheckBox.register(this);
20256         }
20257     },
20258     
20259     onClick : function()
20260     {   
20261         this.setChecked(!this.checked);
20262     },
20263     
20264     setChecked : function(state,suppressEvent)
20265     {
20266         this.startValue = this.getValue();
20267
20268         if(this.inputType == 'radio'){
20269             
20270             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20271                 e.dom.checked = false;
20272             });
20273             
20274             this.inputEl().dom.checked = true;
20275             
20276             this.inputEl().dom.value = this.inputValue;
20277             
20278             if(suppressEvent !== true){
20279                 this.fireEvent('check', this, true);
20280             }
20281             
20282             this.validate();
20283             
20284             return;
20285         }
20286         
20287         this.checked = state;
20288         
20289         this.inputEl().dom.checked = state;
20290         
20291         
20292         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20293         
20294         if(suppressEvent !== true){
20295             this.fireEvent('check', this, state);
20296         }
20297         
20298         this.validate();
20299     },
20300     
20301     getValue : function()
20302     {
20303         if(this.inputType == 'radio'){
20304             return this.getGroupValue();
20305         }
20306         
20307         return this.hiddenEl().dom.value;
20308         
20309     },
20310     
20311     getGroupValue : function()
20312     {
20313         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20314             return '';
20315         }
20316         
20317         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20318     },
20319     
20320     setValue : function(v,suppressEvent)
20321     {
20322         if(this.inputType == 'radio'){
20323             this.setGroupValue(v, suppressEvent);
20324             return;
20325         }
20326         
20327         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20328         
20329         this.validate();
20330     },
20331     
20332     setGroupValue : function(v, suppressEvent)
20333     {
20334         this.startValue = this.getValue();
20335         
20336         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20337             e.dom.checked = false;
20338             
20339             if(e.dom.value == v){
20340                 e.dom.checked = true;
20341             }
20342         });
20343         
20344         if(suppressEvent !== true){
20345             this.fireEvent('check', this, true);
20346         }
20347
20348         this.validate();
20349         
20350         return;
20351     },
20352     
20353     validate : function()
20354     {
20355         if(
20356                 this.disabled || 
20357                 (this.inputType == 'radio' && this.validateRadio()) ||
20358                 (this.inputType == 'checkbox' && this.validateCheckbox())
20359         ){
20360             this.markValid();
20361             return true;
20362         }
20363         
20364         this.markInvalid();
20365         return false;
20366     },
20367     
20368     validateRadio : function()
20369     {
20370         if(this.allowBlank){
20371             return true;
20372         }
20373         
20374         var valid = false;
20375         
20376         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20377             if(!e.dom.checked){
20378                 return;
20379             }
20380             
20381             valid = true;
20382             
20383             return false;
20384         });
20385         
20386         return valid;
20387     },
20388     
20389     validateCheckbox : function()
20390     {
20391         if(!this.groupId){
20392             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20393             //return (this.getValue() == this.inputValue) ? true : false;
20394         }
20395         
20396         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20397         
20398         if(!group){
20399             return false;
20400         }
20401         
20402         var r = false;
20403         
20404         for(var i in group){
20405             if(r){
20406                 break;
20407             }
20408             
20409             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20410         }
20411         
20412         return r;
20413     },
20414     
20415     /**
20416      * Mark this field as valid
20417      */
20418     markValid : function()
20419     {
20420         var _this = this;
20421         
20422         this.fireEvent('valid', this);
20423         
20424         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20425         
20426         if(this.groupId){
20427             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20428         }
20429         
20430         if(label){
20431             label.markValid();
20432         }
20433
20434         if(this.inputType == 'radio'){
20435             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20436                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20437                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20438             });
20439             
20440             return;
20441         }
20442
20443         if(!this.groupId){
20444             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20445             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20446             return;
20447         }
20448         
20449         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20450         
20451         if(!group){
20452             return;
20453         }
20454         
20455         for(var i in group){
20456             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20457             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20458         }
20459     },
20460     
20461      /**
20462      * Mark this field as invalid
20463      * @param {String} msg The validation message
20464      */
20465     markInvalid : function(msg)
20466     {
20467         if(this.allowBlank){
20468             return;
20469         }
20470         
20471         var _this = this;
20472         
20473         this.fireEvent('invalid', this, msg);
20474         
20475         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20476         
20477         if(this.groupId){
20478             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20479         }
20480         
20481         if(label){
20482             label.markInvalid();
20483         }
20484             
20485         if(this.inputType == 'radio'){
20486             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20487                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20488                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20489             });
20490             
20491             return;
20492         }
20493         
20494         if(!this.groupId){
20495             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20496             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20497             return;
20498         }
20499         
20500         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20501         
20502         if(!group){
20503             return;
20504         }
20505         
20506         for(var i in group){
20507             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20508             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20509         }
20510         
20511     },
20512     
20513     clearInvalid : function()
20514     {
20515         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20516         
20517         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20518         
20519         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20520         
20521         if (label) {
20522             label.iconEl.removeClass(label.validClass);
20523             label.iconEl.removeClass(label.invalidClass);
20524         }
20525     },
20526     
20527     disable : function()
20528     {
20529         if(this.inputType != 'radio'){
20530             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20531             return;
20532         }
20533         
20534         var _this = this;
20535         
20536         if(this.rendered){
20537             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20538                 _this.getActionEl().addClass(this.disabledClass);
20539                 e.dom.disabled = true;
20540             });
20541         }
20542         
20543         this.disabled = true;
20544         this.fireEvent("disable", this);
20545         return this;
20546     },
20547
20548     enable : function()
20549     {
20550         if(this.inputType != 'radio'){
20551             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20552             return;
20553         }
20554         
20555         var _this = this;
20556         
20557         if(this.rendered){
20558             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20559                 _this.getActionEl().removeClass(this.disabledClass);
20560                 e.dom.disabled = false;
20561             });
20562         }
20563         
20564         this.disabled = false;
20565         this.fireEvent("enable", this);
20566         return this;
20567     }
20568
20569 });
20570
20571 Roo.apply(Roo.bootstrap.CheckBox, {
20572     
20573     groups: {},
20574     
20575      /**
20576     * register a CheckBox Group
20577     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20578     */
20579     register : function(checkbox)
20580     {
20581         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20582             this.groups[checkbox.groupId] = {};
20583         }
20584         
20585         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20586             return;
20587         }
20588         
20589         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20590         
20591     },
20592     /**
20593     * fetch a CheckBox Group based on the group ID
20594     * @param {string} the group ID
20595     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20596     */
20597     get: function(groupId) {
20598         if (typeof(this.groups[groupId]) == 'undefined') {
20599             return false;
20600         }
20601         
20602         return this.groups[groupId] ;
20603     }
20604     
20605     
20606 });
20607 /*
20608  * - LGPL
20609  *
20610  * RadioItem
20611  * 
20612  */
20613
20614 /**
20615  * @class Roo.bootstrap.Radio
20616  * @extends Roo.bootstrap.Component
20617  * Bootstrap Radio class
20618  * @cfg {String} boxLabel - the label associated
20619  * @cfg {String} value - the value of radio
20620  * 
20621  * @constructor
20622  * Create a new Radio
20623  * @param {Object} config The config object
20624  */
20625 Roo.bootstrap.Radio = function(config){
20626     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20627     
20628 };
20629
20630 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20631     
20632     boxLabel : '',
20633     
20634     value : '',
20635     
20636     getAutoCreate : function()
20637     {
20638         var cfg = {
20639             tag : 'div',
20640             cls : 'form-group radio',
20641             cn : [
20642                 {
20643                     tag : 'label',
20644                     cls : 'box-label',
20645                     html : this.boxLabel
20646                 }
20647             ]
20648         };
20649         
20650         return cfg;
20651     },
20652     
20653     initEvents : function() 
20654     {
20655         this.parent().register(this);
20656         
20657         this.el.on('click', this.onClick, this);
20658         
20659     },
20660     
20661     onClick : function()
20662     {
20663         this.setChecked(true);
20664     },
20665     
20666     setChecked : function(state, suppressEvent)
20667     {
20668         this.parent().setValue(this.value, suppressEvent);
20669         
20670     }
20671     
20672 });
20673  
20674
20675  /*
20676  * - LGPL
20677  *
20678  * Input
20679  * 
20680  */
20681
20682 /**
20683  * @class Roo.bootstrap.SecurePass
20684  * @extends Roo.bootstrap.Input
20685  * Bootstrap SecurePass class
20686  *
20687  * 
20688  * @constructor
20689  * Create a new SecurePass
20690  * @param {Object} config The config object
20691  */
20692  
20693 Roo.bootstrap.SecurePass = function (config) {
20694     // these go here, so the translation tool can replace them..
20695     this.errors = {
20696         PwdEmpty: "Please type a password, and then retype it to confirm.",
20697         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20698         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20699         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20700         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20701         FNInPwd: "Your password can't contain your first name. Please type a different password.",
20702         LNInPwd: "Your password can't contain your last name. Please type a different password.",
20703         TooWeak: "Your password is Too Weak."
20704     },
20705     this.meterLabel = "Password strength:";
20706     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
20707     this.meterClass = ["roo-password-meter-tooweak", 
20708                        "roo-password-meter-weak", 
20709                        "roo-password-meter-medium", 
20710                        "roo-password-meter-strong", 
20711                        "roo-password-meter-grey"],    
20712     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
20713 }
20714
20715 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
20716     /**
20717      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
20718      * {
20719      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
20720      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20721      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20722      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20723      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20724      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
20725      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
20726      * })
20727      */
20728     // private
20729     
20730     meterWidth: 300,
20731     errorMsg :'',    
20732     errors: {},
20733     imageRoot: '/',
20734     /**
20735      * @cfg {String/Object} Label for the strength meter (defaults to
20736      * 'Password strength:')
20737      */
20738     // private
20739     meterLabel: '',
20740     /**
20741      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
20742      * ['Weak', 'Medium', 'Strong'])
20743      */
20744     // private    
20745     pwdStrengths: [],    
20746     // private
20747     strength: 0,
20748     // private
20749     _lastPwd: null,
20750     // private
20751     kCapitalLetter: 0,
20752     kSmallLetter: 1,
20753     kDigit: 2,
20754     kPunctuation: 3,
20755     
20756     insecure: false,
20757     // private
20758     initEvents: function () {
20759         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
20760
20761         if (this.el.is('input[type=password]') && Roo.isSafari) {
20762             this.el.on('keydown', this.SafariOnKeyDown, this);
20763         }
20764
20765         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
20766     },
20767     // private
20768     onRender: function (ct, position) {
20769         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
20770         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
20771         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
20772
20773         this.trigger.createChild({
20774                    cn: [
20775                     {
20776                     //id: 'PwdMeter',
20777                     tag: 'div',
20778                     cls: 'roo-password-meter-grey col-xs-12',
20779                     style: {
20780                         //width: 0,
20781                         //width: this.meterWidth + 'px'                                                
20782                         }
20783                     },
20784                     {                            
20785                          cls: 'roo-password-meter-text'                          
20786                     }
20787                 ]            
20788         });
20789
20790         /*
20791         this.trigger.createChild({
20792             tag: 'div',
20793             cls: 'roo-password-meter-container col-xs-12',
20794             style: {               
20795                 //width: this.meterWidth + 'px'
20796             },
20797             cn: {
20798                 tag: 'div',
20799                 cls: 'roo-password-meter-grey',
20800                 style: {
20801                     //width: this.meterWidth + 'px'                                        
20802                 },
20803                 cn: [
20804                     {
20805                     //id: 'PwdMeter',
20806                     tag: 'div',
20807                     cls: 'roo-password-meter-grey col-xs-12',
20808                     style: {
20809                         //width: 0,
20810                         //width: this.meterWidth + 'px'                                                
20811                         }
20812                     },
20813                     {                            
20814                          cls: 'roo-password-meter-text'                          
20815                     }
20816                 ]                
20817             }
20818         });
20819         */
20820         if (this.hideTrigger) {
20821             this.trigger.setDisplayed(false);
20822         }
20823         this.setSize(this.width || '', this.height || '');
20824     },
20825     // private
20826     onDestroy: function () {
20827         if (this.trigger) {
20828             this.trigger.removeAllListeners();
20829             this.trigger.remove();
20830         }
20831         if (this.wrap) {
20832             this.wrap.remove();
20833         }
20834         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
20835     },
20836     // private
20837     checkStrength: function () {
20838         var pwd = this.inputEl().getValue();
20839         if (pwd == this._lastPwd) {
20840             return;
20841         }
20842
20843         var strength;
20844         if (this.ClientSideStrongPassword(pwd)) {
20845             strength = 3;
20846         } else if (this.ClientSideMediumPassword(pwd)) {
20847             strength = 2;
20848         } else if (this.ClientSideWeakPassword(pwd)) {
20849             strength = 1;
20850         } else {
20851             strength = 0;
20852         }
20853         
20854         console.log('strength1: ' + strength);
20855         
20856         //var pm = this.trigger.child('div/div/div').dom;
20857         var pm = this.trigger.child('div/div');
20858         pm.removeClass(this.meterClass);
20859         pm.addClass(this.meterClass[strength]);
20860                 
20861         
20862         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
20863                 
20864         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
20865         
20866         this._lastPwd = pwd;
20867     },
20868     reset: function () {
20869         Roo.bootstrap.SecurePass.superclass.reset.call(this);
20870         
20871         this._lastPwd = '';
20872         
20873         var pm = this.trigger.child('div/div');
20874         pm.removeClass(this.meterClass);
20875         pm.addClass('roo-password-meter-grey');        
20876         
20877         
20878         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
20879         
20880         pt.innerHTML = '';
20881         this.inputEl().dom.type='password';
20882     },
20883     // private
20884     validateValue: function (value) {
20885         
20886         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
20887             return false;
20888         }
20889         if (value.length == 0) {
20890             if (this.allowBlank) {
20891                 this.clearInvalid();
20892                 return true;
20893             }
20894
20895             this.markInvalid(this.errors.PwdEmpty);
20896             this.errorMsg = this.errors.PwdEmpty;
20897             return false;
20898         }
20899         
20900         if(this.insecure){
20901             return true;
20902         }
20903         
20904         if ('[\x21-\x7e]*'.match(value)) {
20905             this.markInvalid(this.errors.PwdBadChar);
20906             this.errorMsg = this.errors.PwdBadChar;
20907             return false;
20908         }
20909         if (value.length < 6) {
20910             this.markInvalid(this.errors.PwdShort);
20911             this.errorMsg = this.errors.PwdShort;
20912             return false;
20913         }
20914         if (value.length > 16) {
20915             this.markInvalid(this.errors.PwdLong);
20916             this.errorMsg = this.errors.PwdLong;
20917             return false;
20918         }
20919         var strength;
20920         if (this.ClientSideStrongPassword(value)) {
20921             strength = 3;
20922         } else if (this.ClientSideMediumPassword(value)) {
20923             strength = 2;
20924         } else if (this.ClientSideWeakPassword(value)) {
20925             strength = 1;
20926         } else {
20927             strength = 0;
20928         }
20929
20930         
20931         if (strength < 2) {
20932             //this.markInvalid(this.errors.TooWeak);
20933             this.errorMsg = this.errors.TooWeak;
20934             //return false;
20935         }
20936         
20937         
20938         console.log('strength2: ' + strength);
20939         
20940         //var pm = this.trigger.child('div/div/div').dom;
20941         
20942         var pm = this.trigger.child('div/div');
20943         pm.removeClass(this.meterClass);
20944         pm.addClass(this.meterClass[strength]);
20945                 
20946         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
20947                 
20948         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
20949         
20950         this.errorMsg = ''; 
20951         return true;
20952     },
20953     // private
20954     CharacterSetChecks: function (type) {
20955         this.type = type;
20956         this.fResult = false;
20957     },
20958     // private
20959     isctype: function (character, type) {
20960         switch (type) { //why needed break after return in js ? very odd bug
20961             case this.kCapitalLetter:
20962                 if (character >= 'A' && character <= 'Z') {
20963                     return true;
20964                 }
20965                 break;
20966             case this.kSmallLetter:
20967                 if (character >= 'a' && character <= 'z') {
20968                     return true;
20969                 }
20970                 break;
20971             case this.kDigit:
20972                 if (character >= '0' && character <= '9') {
20973                     return true;
20974                 }
20975                 break;
20976             case this.kPunctuation:
20977                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
20978                     return true;
20979                 }
20980                 break;
20981             default:
20982                 return false;
20983         }
20984
20985     },
20986     // private
20987     IsLongEnough: function (pwd, size) {
20988         return !(pwd == null || isNaN(size) || pwd.length < size);
20989     },
20990     // private
20991     SpansEnoughCharacterSets: function (word, nb) {
20992         if (!this.IsLongEnough(word, nb))
20993         {
20994             return false;
20995         }
20996
20997         var characterSetChecks = new Array(
20998                 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
20999                 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation));
21000         for (var index = 0; index < word.length; ++index) {
21001             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21002                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21003                     characterSetChecks[nCharSet].fResult = true;
21004                     break;
21005                 }
21006             }
21007         }
21008
21009         var nCharSets = 0;
21010         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21011             if (characterSetChecks[nCharSet].fResult) {
21012                 ++nCharSets;
21013             }
21014         }
21015
21016         if (nCharSets < nb) {
21017             return false;
21018         }
21019         return true;
21020     },
21021     // private
21022     ClientSideStrongPassword: function (pwd) {
21023         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21024     },
21025     // private
21026     ClientSideMediumPassword: function (pwd) {
21027         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21028     },
21029     // private
21030     ClientSideWeakPassword: function (pwd) {
21031         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21032     }
21033           
21034 })//<script type="text/javascript">
21035
21036 /*
21037  * Based  Ext JS Library 1.1.1
21038  * Copyright(c) 2006-2007, Ext JS, LLC.
21039  * LGPL
21040  *
21041  */
21042  
21043 /**
21044  * @class Roo.HtmlEditorCore
21045  * @extends Roo.Component
21046  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21047  *
21048  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21049  */
21050
21051 Roo.HtmlEditorCore = function(config){
21052     
21053     
21054     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21055     
21056     
21057     this.addEvents({
21058         /**
21059          * @event initialize
21060          * Fires when the editor is fully initialized (including the iframe)
21061          * @param {Roo.HtmlEditorCore} this
21062          */
21063         initialize: true,
21064         /**
21065          * @event activate
21066          * Fires when the editor is first receives the focus. Any insertion must wait
21067          * until after this event.
21068          * @param {Roo.HtmlEditorCore} this
21069          */
21070         activate: true,
21071          /**
21072          * @event beforesync
21073          * Fires before the textarea is updated with content from the editor iframe. Return false
21074          * to cancel the sync.
21075          * @param {Roo.HtmlEditorCore} this
21076          * @param {String} html
21077          */
21078         beforesync: true,
21079          /**
21080          * @event beforepush
21081          * Fires before the iframe editor is updated with content from the textarea. Return false
21082          * to cancel the push.
21083          * @param {Roo.HtmlEditorCore} this
21084          * @param {String} html
21085          */
21086         beforepush: true,
21087          /**
21088          * @event sync
21089          * Fires when the textarea is updated with content from the editor iframe.
21090          * @param {Roo.HtmlEditorCore} this
21091          * @param {String} html
21092          */
21093         sync: true,
21094          /**
21095          * @event push
21096          * Fires when the iframe editor is updated with content from the textarea.
21097          * @param {Roo.HtmlEditorCore} this
21098          * @param {String} html
21099          */
21100         push: true,
21101         
21102         /**
21103          * @event editorevent
21104          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21105          * @param {Roo.HtmlEditorCore} this
21106          */
21107         editorevent: true
21108         
21109     });
21110     
21111     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21112     
21113     // defaults : white / black...
21114     this.applyBlacklists();
21115     
21116     
21117     
21118 };
21119
21120
21121 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21122
21123
21124      /**
21125      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21126      */
21127     
21128     owner : false,
21129     
21130      /**
21131      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21132      *                        Roo.resizable.
21133      */
21134     resizable : false,
21135      /**
21136      * @cfg {Number} height (in pixels)
21137      */   
21138     height: 300,
21139    /**
21140      * @cfg {Number} width (in pixels)
21141      */   
21142     width: 500,
21143     
21144     /**
21145      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21146      * 
21147      */
21148     stylesheets: false,
21149     
21150     // id of frame..
21151     frameId: false,
21152     
21153     // private properties
21154     validationEvent : false,
21155     deferHeight: true,
21156     initialized : false,
21157     activated : false,
21158     sourceEditMode : false,
21159     onFocus : Roo.emptyFn,
21160     iframePad:3,
21161     hideMode:'offsets',
21162     
21163     clearUp: true,
21164     
21165     // blacklist + whitelisted elements..
21166     black: false,
21167     white: false,
21168      
21169     
21170
21171     /**
21172      * Protected method that will not generally be called directly. It
21173      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21174      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21175      */
21176     getDocMarkup : function(){
21177         // body styles..
21178         var st = '';
21179         
21180         // inherit styels from page...?? 
21181         if (this.stylesheets === false) {
21182             
21183             Roo.get(document.head).select('style').each(function(node) {
21184                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21185             });
21186             
21187             Roo.get(document.head).select('link').each(function(node) { 
21188                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21189             });
21190             
21191         } else if (!this.stylesheets.length) {
21192                 // simple..
21193                 st = '<style type="text/css">' +
21194                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21195                    '</style>';
21196         } else { 
21197             
21198         }
21199         
21200         st +=  '<style type="text/css">' +
21201             'IMG { cursor: pointer } ' +
21202         '</style>';
21203
21204         
21205         return '<html><head>' + st  +
21206             //<style type="text/css">' +
21207             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21208             //'</style>' +
21209             ' </head><body class="roo-htmleditor-body"></body></html>';
21210     },
21211
21212     // private
21213     onRender : function(ct, position)
21214     {
21215         var _t = this;
21216         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21217         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21218         
21219         
21220         this.el.dom.style.border = '0 none';
21221         this.el.dom.setAttribute('tabIndex', -1);
21222         this.el.addClass('x-hidden hide');
21223         
21224         
21225         
21226         if(Roo.isIE){ // fix IE 1px bogus margin
21227             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21228         }
21229        
21230         
21231         this.frameId = Roo.id();
21232         
21233          
21234         
21235         var iframe = this.owner.wrap.createChild({
21236             tag: 'iframe',
21237             cls: 'form-control', // bootstrap..
21238             id: this.frameId,
21239             name: this.frameId,
21240             frameBorder : 'no',
21241             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21242         }, this.el
21243         );
21244         
21245         
21246         this.iframe = iframe.dom;
21247
21248          this.assignDocWin();
21249         
21250         this.doc.designMode = 'on';
21251        
21252         this.doc.open();
21253         this.doc.write(this.getDocMarkup());
21254         this.doc.close();
21255
21256         
21257         var task = { // must defer to wait for browser to be ready
21258             run : function(){
21259                 //console.log("run task?" + this.doc.readyState);
21260                 this.assignDocWin();
21261                 if(this.doc.body || this.doc.readyState == 'complete'){
21262                     try {
21263                         this.doc.designMode="on";
21264                     } catch (e) {
21265                         return;
21266                     }
21267                     Roo.TaskMgr.stop(task);
21268                     this.initEditor.defer(10, this);
21269                 }
21270             },
21271             interval : 10,
21272             duration: 10000,
21273             scope: this
21274         };
21275         Roo.TaskMgr.start(task);
21276
21277     },
21278
21279     // private
21280     onResize : function(w, h)
21281     {
21282          Roo.log('resize: ' +w + ',' + h );
21283         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21284         if(!this.iframe){
21285             return;
21286         }
21287         if(typeof w == 'number'){
21288             
21289             this.iframe.style.width = w + 'px';
21290         }
21291         if(typeof h == 'number'){
21292             
21293             this.iframe.style.height = h + 'px';
21294             if(this.doc){
21295                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21296             }
21297         }
21298         
21299     },
21300
21301     /**
21302      * Toggles the editor between standard and source edit mode.
21303      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21304      */
21305     toggleSourceEdit : function(sourceEditMode){
21306         
21307         this.sourceEditMode = sourceEditMode === true;
21308         
21309         if(this.sourceEditMode){
21310  
21311             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21312             
21313         }else{
21314             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21315             //this.iframe.className = '';
21316             this.deferFocus();
21317         }
21318         //this.setSize(this.owner.wrap.getSize());
21319         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21320     },
21321
21322     
21323   
21324
21325     /**
21326      * Protected method that will not generally be called directly. If you need/want
21327      * custom HTML cleanup, this is the method you should override.
21328      * @param {String} html The HTML to be cleaned
21329      * return {String} The cleaned HTML
21330      */
21331     cleanHtml : function(html){
21332         html = String(html);
21333         if(html.length > 5){
21334             if(Roo.isSafari){ // strip safari nonsense
21335                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21336             }
21337         }
21338         if(html == '&nbsp;'){
21339             html = '';
21340         }
21341         return html;
21342     },
21343
21344     /**
21345      * HTML Editor -> Textarea
21346      * Protected method that will not generally be called directly. Syncs the contents
21347      * of the editor iframe with the textarea.
21348      */
21349     syncValue : function(){
21350         if(this.initialized){
21351             var bd = (this.doc.body || this.doc.documentElement);
21352             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21353             var html = bd.innerHTML;
21354             if(Roo.isSafari){
21355                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21356                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21357                 if(m && m[1]){
21358                     html = '<div style="'+m[0]+'">' + html + '</div>';
21359                 }
21360             }
21361             html = this.cleanHtml(html);
21362             // fix up the special chars.. normaly like back quotes in word...
21363             // however we do not want to do this with chinese..
21364             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21365                 var cc = b.charCodeAt();
21366                 if (
21367                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21368                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21369                     (cc >= 0xf900 && cc < 0xfb00 )
21370                 ) {
21371                         return b;
21372                 }
21373                 return "&#"+cc+";" 
21374             });
21375             if(this.owner.fireEvent('beforesync', this, html) !== false){
21376                 this.el.dom.value = html;
21377                 this.owner.fireEvent('sync', this, html);
21378             }
21379         }
21380     },
21381
21382     /**
21383      * Protected method that will not generally be called directly. Pushes the value of the textarea
21384      * into the iframe editor.
21385      */
21386     pushValue : function(){
21387         if(this.initialized){
21388             var v = this.el.dom.value.trim();
21389             
21390 //            if(v.length < 1){
21391 //                v = '&#160;';
21392 //            }
21393             
21394             if(this.owner.fireEvent('beforepush', this, v) !== false){
21395                 var d = (this.doc.body || this.doc.documentElement);
21396                 d.innerHTML = v;
21397                 this.cleanUpPaste();
21398                 this.el.dom.value = d.innerHTML;
21399                 this.owner.fireEvent('push', this, v);
21400             }
21401         }
21402     },
21403
21404     // private
21405     deferFocus : function(){
21406         this.focus.defer(10, this);
21407     },
21408
21409     // doc'ed in Field
21410     focus : function(){
21411         if(this.win && !this.sourceEditMode){
21412             this.win.focus();
21413         }else{
21414             this.el.focus();
21415         }
21416     },
21417     
21418     assignDocWin: function()
21419     {
21420         var iframe = this.iframe;
21421         
21422          if(Roo.isIE){
21423             this.doc = iframe.contentWindow.document;
21424             this.win = iframe.contentWindow;
21425         } else {
21426 //            if (!Roo.get(this.frameId)) {
21427 //                return;
21428 //            }
21429 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21430 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21431             
21432             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21433                 return;
21434             }
21435             
21436             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21437             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21438         }
21439     },
21440     
21441     // private
21442     initEditor : function(){
21443         //console.log("INIT EDITOR");
21444         this.assignDocWin();
21445         
21446         
21447         
21448         this.doc.designMode="on";
21449         this.doc.open();
21450         this.doc.write(this.getDocMarkup());
21451         this.doc.close();
21452         
21453         var dbody = (this.doc.body || this.doc.documentElement);
21454         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21455         // this copies styles from the containing element into thsi one..
21456         // not sure why we need all of this..
21457         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21458         
21459         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21460         //ss['background-attachment'] = 'fixed'; // w3c
21461         dbody.bgProperties = 'fixed'; // ie
21462         //Roo.DomHelper.applyStyles(dbody, ss);
21463         Roo.EventManager.on(this.doc, {
21464             //'mousedown': this.onEditorEvent,
21465             'mouseup': this.onEditorEvent,
21466             'dblclick': this.onEditorEvent,
21467             'click': this.onEditorEvent,
21468             'keyup': this.onEditorEvent,
21469             buffer:100,
21470             scope: this
21471         });
21472         if(Roo.isGecko){
21473             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21474         }
21475         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21476             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21477         }
21478         this.initialized = true;
21479
21480         this.owner.fireEvent('initialize', this);
21481         this.pushValue();
21482     },
21483
21484     // private
21485     onDestroy : function(){
21486         
21487         
21488         
21489         if(this.rendered){
21490             
21491             //for (var i =0; i < this.toolbars.length;i++) {
21492             //    // fixme - ask toolbars for heights?
21493             //    this.toolbars[i].onDestroy();
21494            // }
21495             
21496             //this.wrap.dom.innerHTML = '';
21497             //this.wrap.remove();
21498         }
21499     },
21500
21501     // private
21502     onFirstFocus : function(){
21503         
21504         this.assignDocWin();
21505         
21506         
21507         this.activated = true;
21508          
21509     
21510         if(Roo.isGecko){ // prevent silly gecko errors
21511             this.win.focus();
21512             var s = this.win.getSelection();
21513             if(!s.focusNode || s.focusNode.nodeType != 3){
21514                 var r = s.getRangeAt(0);
21515                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21516                 r.collapse(true);
21517                 this.deferFocus();
21518             }
21519             try{
21520                 this.execCmd('useCSS', true);
21521                 this.execCmd('styleWithCSS', false);
21522             }catch(e){}
21523         }
21524         this.owner.fireEvent('activate', this);
21525     },
21526
21527     // private
21528     adjustFont: function(btn){
21529         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21530         //if(Roo.isSafari){ // safari
21531         //    adjust *= 2;
21532        // }
21533         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21534         if(Roo.isSafari){ // safari
21535             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21536             v =  (v < 10) ? 10 : v;
21537             v =  (v > 48) ? 48 : v;
21538             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21539             
21540         }
21541         
21542         
21543         v = Math.max(1, v+adjust);
21544         
21545         this.execCmd('FontSize', v  );
21546     },
21547
21548     onEditorEvent : function(e)
21549     {
21550         this.owner.fireEvent('editorevent', this, e);
21551       //  this.updateToolbar();
21552         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21553     },
21554
21555     insertTag : function(tg)
21556     {
21557         // could be a bit smarter... -> wrap the current selected tRoo..
21558         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21559             
21560             range = this.createRange(this.getSelection());
21561             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21562             wrappingNode.appendChild(range.extractContents());
21563             range.insertNode(wrappingNode);
21564
21565             return;
21566             
21567             
21568             
21569         }
21570         this.execCmd("formatblock",   tg);
21571         
21572     },
21573     
21574     insertText : function(txt)
21575     {
21576         
21577         
21578         var range = this.createRange();
21579         range.deleteContents();
21580                //alert(Sender.getAttribute('label'));
21581                
21582         range.insertNode(this.doc.createTextNode(txt));
21583     } ,
21584     
21585      
21586
21587     /**
21588      * Executes a Midas editor command on the editor document and performs necessary focus and
21589      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21590      * @param {String} cmd The Midas command
21591      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21592      */
21593     relayCmd : function(cmd, value){
21594         this.win.focus();
21595         this.execCmd(cmd, value);
21596         this.owner.fireEvent('editorevent', this);
21597         //this.updateToolbar();
21598         this.owner.deferFocus();
21599     },
21600
21601     /**
21602      * Executes a Midas editor command directly on the editor document.
21603      * For visual commands, you should use {@link #relayCmd} instead.
21604      * <b>This should only be called after the editor is initialized.</b>
21605      * @param {String} cmd The Midas command
21606      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21607      */
21608     execCmd : function(cmd, value){
21609         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21610         this.syncValue();
21611     },
21612  
21613  
21614    
21615     /**
21616      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21617      * to insert tRoo.
21618      * @param {String} text | dom node.. 
21619      */
21620     insertAtCursor : function(text)
21621     {
21622         
21623         
21624         
21625         if(!this.activated){
21626             return;
21627         }
21628         /*
21629         if(Roo.isIE){
21630             this.win.focus();
21631             var r = this.doc.selection.createRange();
21632             if(r){
21633                 r.collapse(true);
21634                 r.pasteHTML(text);
21635                 this.syncValue();
21636                 this.deferFocus();
21637             
21638             }
21639             return;
21640         }
21641         */
21642         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21643             this.win.focus();
21644             
21645             
21646             // from jquery ui (MIT licenced)
21647             var range, node;
21648             var win = this.win;
21649             
21650             if (win.getSelection && win.getSelection().getRangeAt) {
21651                 range = win.getSelection().getRangeAt(0);
21652                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21653                 range.insertNode(node);
21654             } else if (win.document.selection && win.document.selection.createRange) {
21655                 // no firefox support
21656                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21657                 win.document.selection.createRange().pasteHTML(txt);
21658             } else {
21659                 // no firefox support
21660                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21661                 this.execCmd('InsertHTML', txt);
21662             } 
21663             
21664             this.syncValue();
21665             
21666             this.deferFocus();
21667         }
21668     },
21669  // private
21670     mozKeyPress : function(e){
21671         if(e.ctrlKey){
21672             var c = e.getCharCode(), cmd;
21673           
21674             if(c > 0){
21675                 c = String.fromCharCode(c).toLowerCase();
21676                 switch(c){
21677                     case 'b':
21678                         cmd = 'bold';
21679                         break;
21680                     case 'i':
21681                         cmd = 'italic';
21682                         break;
21683                     
21684                     case 'u':
21685                         cmd = 'underline';
21686                         break;
21687                     
21688                     case 'v':
21689                         this.cleanUpPaste.defer(100, this);
21690                         return;
21691                         
21692                 }
21693                 if(cmd){
21694                     this.win.focus();
21695                     this.execCmd(cmd);
21696                     this.deferFocus();
21697                     e.preventDefault();
21698                 }
21699                 
21700             }
21701         }
21702     },
21703
21704     // private
21705     fixKeys : function(){ // load time branching for fastest keydown performance
21706         if(Roo.isIE){
21707             return function(e){
21708                 var k = e.getKey(), r;
21709                 if(k == e.TAB){
21710                     e.stopEvent();
21711                     r = this.doc.selection.createRange();
21712                     if(r){
21713                         r.collapse(true);
21714                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21715                         this.deferFocus();
21716                     }
21717                     return;
21718                 }
21719                 
21720                 if(k == e.ENTER){
21721                     r = this.doc.selection.createRange();
21722                     if(r){
21723                         var target = r.parentElement();
21724                         if(!target || target.tagName.toLowerCase() != 'li'){
21725                             e.stopEvent();
21726                             r.pasteHTML('<br />');
21727                             r.collapse(false);
21728                             r.select();
21729                         }
21730                     }
21731                 }
21732                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21733                     this.cleanUpPaste.defer(100, this);
21734                     return;
21735                 }
21736                 
21737                 
21738             };
21739         }else if(Roo.isOpera){
21740             return function(e){
21741                 var k = e.getKey();
21742                 if(k == e.TAB){
21743                     e.stopEvent();
21744                     this.win.focus();
21745                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21746                     this.deferFocus();
21747                 }
21748                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21749                     this.cleanUpPaste.defer(100, this);
21750                     return;
21751                 }
21752                 
21753             };
21754         }else if(Roo.isSafari){
21755             return function(e){
21756                 var k = e.getKey();
21757                 
21758                 if(k == e.TAB){
21759                     e.stopEvent();
21760                     this.execCmd('InsertText','\t');
21761                     this.deferFocus();
21762                     return;
21763                 }
21764                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21765                     this.cleanUpPaste.defer(100, this);
21766                     return;
21767                 }
21768                 
21769              };
21770         }
21771     }(),
21772     
21773     getAllAncestors: function()
21774     {
21775         var p = this.getSelectedNode();
21776         var a = [];
21777         if (!p) {
21778             a.push(p); // push blank onto stack..
21779             p = this.getParentElement();
21780         }
21781         
21782         
21783         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21784             a.push(p);
21785             p = p.parentNode;
21786         }
21787         a.push(this.doc.body);
21788         return a;
21789     },
21790     lastSel : false,
21791     lastSelNode : false,
21792     
21793     
21794     getSelection : function() 
21795     {
21796         this.assignDocWin();
21797         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21798     },
21799     
21800     getSelectedNode: function() 
21801     {
21802         // this may only work on Gecko!!!
21803         
21804         // should we cache this!!!!
21805         
21806         
21807         
21808          
21809         var range = this.createRange(this.getSelection()).cloneRange();
21810         
21811         if (Roo.isIE) {
21812             var parent = range.parentElement();
21813             while (true) {
21814                 var testRange = range.duplicate();
21815                 testRange.moveToElementText(parent);
21816                 if (testRange.inRange(range)) {
21817                     break;
21818                 }
21819                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21820                     break;
21821                 }
21822                 parent = parent.parentElement;
21823             }
21824             return parent;
21825         }
21826         
21827         // is ancestor a text element.
21828         var ac =  range.commonAncestorContainer;
21829         if (ac.nodeType == 3) {
21830             ac = ac.parentNode;
21831         }
21832         
21833         var ar = ac.childNodes;
21834          
21835         var nodes = [];
21836         var other_nodes = [];
21837         var has_other_nodes = false;
21838         for (var i=0;i<ar.length;i++) {
21839             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21840                 continue;
21841             }
21842             // fullly contained node.
21843             
21844             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21845                 nodes.push(ar[i]);
21846                 continue;
21847             }
21848             
21849             // probably selected..
21850             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21851                 other_nodes.push(ar[i]);
21852                 continue;
21853             }
21854             // outer..
21855             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21856                 continue;
21857             }
21858             
21859             
21860             has_other_nodes = true;
21861         }
21862         if (!nodes.length && other_nodes.length) {
21863             nodes= other_nodes;
21864         }
21865         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21866             return false;
21867         }
21868         
21869         return nodes[0];
21870     },
21871     createRange: function(sel)
21872     {
21873         // this has strange effects when using with 
21874         // top toolbar - not sure if it's a great idea.
21875         //this.editor.contentWindow.focus();
21876         if (typeof sel != "undefined") {
21877             try {
21878                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21879             } catch(e) {
21880                 return this.doc.createRange();
21881             }
21882         } else {
21883             return this.doc.createRange();
21884         }
21885     },
21886     getParentElement: function()
21887     {
21888         
21889         this.assignDocWin();
21890         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21891         
21892         var range = this.createRange(sel);
21893          
21894         try {
21895             var p = range.commonAncestorContainer;
21896             while (p.nodeType == 3) { // text node
21897                 p = p.parentNode;
21898             }
21899             return p;
21900         } catch (e) {
21901             return null;
21902         }
21903     
21904     },
21905     /***
21906      *
21907      * Range intersection.. the hard stuff...
21908      *  '-1' = before
21909      *  '0' = hits..
21910      *  '1' = after.
21911      *         [ -- selected range --- ]
21912      *   [fail]                        [fail]
21913      *
21914      *    basically..
21915      *      if end is before start or  hits it. fail.
21916      *      if start is after end or hits it fail.
21917      *
21918      *   if either hits (but other is outside. - then it's not 
21919      *   
21920      *    
21921      **/
21922     
21923     
21924     // @see http://www.thismuchiknow.co.uk/?p=64.
21925     rangeIntersectsNode : function(range, node)
21926     {
21927         var nodeRange = node.ownerDocument.createRange();
21928         try {
21929             nodeRange.selectNode(node);
21930         } catch (e) {
21931             nodeRange.selectNodeContents(node);
21932         }
21933     
21934         var rangeStartRange = range.cloneRange();
21935         rangeStartRange.collapse(true);
21936     
21937         var rangeEndRange = range.cloneRange();
21938         rangeEndRange.collapse(false);
21939     
21940         var nodeStartRange = nodeRange.cloneRange();
21941         nodeStartRange.collapse(true);
21942     
21943         var nodeEndRange = nodeRange.cloneRange();
21944         nodeEndRange.collapse(false);
21945     
21946         return rangeStartRange.compareBoundaryPoints(
21947                  Range.START_TO_START, nodeEndRange) == -1 &&
21948                rangeEndRange.compareBoundaryPoints(
21949                  Range.START_TO_START, nodeStartRange) == 1;
21950         
21951          
21952     },
21953     rangeCompareNode : function(range, node)
21954     {
21955         var nodeRange = node.ownerDocument.createRange();
21956         try {
21957             nodeRange.selectNode(node);
21958         } catch (e) {
21959             nodeRange.selectNodeContents(node);
21960         }
21961         
21962         
21963         range.collapse(true);
21964     
21965         nodeRange.collapse(true);
21966      
21967         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21968         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21969          
21970         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21971         
21972         var nodeIsBefore   =  ss == 1;
21973         var nodeIsAfter    = ee == -1;
21974         
21975         if (nodeIsBefore && nodeIsAfter) {
21976             return 0; // outer
21977         }
21978         if (!nodeIsBefore && nodeIsAfter) {
21979             return 1; //right trailed.
21980         }
21981         
21982         if (nodeIsBefore && !nodeIsAfter) {
21983             return 2;  // left trailed.
21984         }
21985         // fully contined.
21986         return 3;
21987     },
21988
21989     // private? - in a new class?
21990     cleanUpPaste :  function()
21991     {
21992         // cleans up the whole document..
21993         Roo.log('cleanuppaste');
21994         
21995         this.cleanUpChildren(this.doc.body);
21996         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21997         if (clean != this.doc.body.innerHTML) {
21998             this.doc.body.innerHTML = clean;
21999         }
22000         
22001     },
22002     
22003     cleanWordChars : function(input) {// change the chars to hex code
22004         var he = Roo.HtmlEditorCore;
22005         
22006         var output = input;
22007         Roo.each(he.swapCodes, function(sw) { 
22008             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22009             
22010             output = output.replace(swapper, sw[1]);
22011         });
22012         
22013         return output;
22014     },
22015     
22016     
22017     cleanUpChildren : function (n)
22018     {
22019         if (!n.childNodes.length) {
22020             return;
22021         }
22022         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22023            this.cleanUpChild(n.childNodes[i]);
22024         }
22025     },
22026     
22027     
22028         
22029     
22030     cleanUpChild : function (node)
22031     {
22032         var ed = this;
22033         //console.log(node);
22034         if (node.nodeName == "#text") {
22035             // clean up silly Windows -- stuff?
22036             return; 
22037         }
22038         if (node.nodeName == "#comment") {
22039             node.parentNode.removeChild(node);
22040             // clean up silly Windows -- stuff?
22041             return; 
22042         }
22043         var lcname = node.tagName.toLowerCase();
22044         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22045         // whitelist of tags..
22046         
22047         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22048             // remove node.
22049             node.parentNode.removeChild(node);
22050             return;
22051             
22052         }
22053         
22054         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22055         
22056         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22057         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22058         
22059         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22060         //    remove_keep_children = true;
22061         //}
22062         
22063         if (remove_keep_children) {
22064             this.cleanUpChildren(node);
22065             // inserts everything just before this node...
22066             while (node.childNodes.length) {
22067                 var cn = node.childNodes[0];
22068                 node.removeChild(cn);
22069                 node.parentNode.insertBefore(cn, node);
22070             }
22071             node.parentNode.removeChild(node);
22072             return;
22073         }
22074         
22075         if (!node.attributes || !node.attributes.length) {
22076             this.cleanUpChildren(node);
22077             return;
22078         }
22079         
22080         function cleanAttr(n,v)
22081         {
22082             
22083             if (v.match(/^\./) || v.match(/^\//)) {
22084                 return;
22085             }
22086             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22087                 return;
22088             }
22089             if (v.match(/^#/)) {
22090                 return;
22091             }
22092 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22093             node.removeAttribute(n);
22094             
22095         }
22096         
22097         var cwhite = this.cwhite;
22098         var cblack = this.cblack;
22099             
22100         function cleanStyle(n,v)
22101         {
22102             if (v.match(/expression/)) { //XSS?? should we even bother..
22103                 node.removeAttribute(n);
22104                 return;
22105             }
22106             
22107             var parts = v.split(/;/);
22108             var clean = [];
22109             
22110             Roo.each(parts, function(p) {
22111                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22112                 if (!p.length) {
22113                     return true;
22114                 }
22115                 var l = p.split(':').shift().replace(/\s+/g,'');
22116                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22117                 
22118                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22119 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22120                     //node.removeAttribute(n);
22121                     return true;
22122                 }
22123                 //Roo.log()
22124                 // only allow 'c whitelisted system attributes'
22125                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22126 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22127                     //node.removeAttribute(n);
22128                     return true;
22129                 }
22130                 
22131                 
22132                  
22133                 
22134                 clean.push(p);
22135                 return true;
22136             });
22137             if (clean.length) { 
22138                 node.setAttribute(n, clean.join(';'));
22139             } else {
22140                 node.removeAttribute(n);
22141             }
22142             
22143         }
22144         
22145         
22146         for (var i = node.attributes.length-1; i > -1 ; i--) {
22147             var a = node.attributes[i];
22148             //console.log(a);
22149             
22150             if (a.name.toLowerCase().substr(0,2)=='on')  {
22151                 node.removeAttribute(a.name);
22152                 continue;
22153             }
22154             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22155                 node.removeAttribute(a.name);
22156                 continue;
22157             }
22158             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22159                 cleanAttr(a.name,a.value); // fixme..
22160                 continue;
22161             }
22162             if (a.name == 'style') {
22163                 cleanStyle(a.name,a.value);
22164                 continue;
22165             }
22166             /// clean up MS crap..
22167             // tecnically this should be a list of valid class'es..
22168             
22169             
22170             if (a.name == 'class') {
22171                 if (a.value.match(/^Mso/)) {
22172                     node.className = '';
22173                 }
22174                 
22175                 if (a.value.match(/body/)) {
22176                     node.className = '';
22177                 }
22178                 continue;
22179             }
22180             
22181             // style cleanup!?
22182             // class cleanup?
22183             
22184         }
22185         
22186         
22187         this.cleanUpChildren(node);
22188         
22189         
22190     },
22191     
22192     /**
22193      * Clean up MS wordisms...
22194      */
22195     cleanWord : function(node)
22196     {
22197         
22198         
22199         if (!node) {
22200             this.cleanWord(this.doc.body);
22201             return;
22202         }
22203         if (node.nodeName == "#text") {
22204             // clean up silly Windows -- stuff?
22205             return; 
22206         }
22207         if (node.nodeName == "#comment") {
22208             node.parentNode.removeChild(node);
22209             // clean up silly Windows -- stuff?
22210             return; 
22211         }
22212         
22213         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22214             node.parentNode.removeChild(node);
22215             return;
22216         }
22217         
22218         // remove - but keep children..
22219         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22220             while (node.childNodes.length) {
22221                 var cn = node.childNodes[0];
22222                 node.removeChild(cn);
22223                 node.parentNode.insertBefore(cn, node);
22224             }
22225             node.parentNode.removeChild(node);
22226             this.iterateChildren(node, this.cleanWord);
22227             return;
22228         }
22229         // clean styles
22230         if (node.className.length) {
22231             
22232             var cn = node.className.split(/\W+/);
22233             var cna = [];
22234             Roo.each(cn, function(cls) {
22235                 if (cls.match(/Mso[a-zA-Z]+/)) {
22236                     return;
22237                 }
22238                 cna.push(cls);
22239             });
22240             node.className = cna.length ? cna.join(' ') : '';
22241             if (!cna.length) {
22242                 node.removeAttribute("class");
22243             }
22244         }
22245         
22246         if (node.hasAttribute("lang")) {
22247             node.removeAttribute("lang");
22248         }
22249         
22250         if (node.hasAttribute("style")) {
22251             
22252             var styles = node.getAttribute("style").split(";");
22253             var nstyle = [];
22254             Roo.each(styles, function(s) {
22255                 if (!s.match(/:/)) {
22256                     return;
22257                 }
22258                 var kv = s.split(":");
22259                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22260                     return;
22261                 }
22262                 // what ever is left... we allow.
22263                 nstyle.push(s);
22264             });
22265             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22266             if (!nstyle.length) {
22267                 node.removeAttribute('style');
22268             }
22269         }
22270         this.iterateChildren(node, this.cleanWord);
22271         
22272         
22273         
22274     },
22275     /**
22276      * iterateChildren of a Node, calling fn each time, using this as the scole..
22277      * @param {DomNode} node node to iterate children of.
22278      * @param {Function} fn method of this class to call on each item.
22279      */
22280     iterateChildren : function(node, fn)
22281     {
22282         if (!node.childNodes.length) {
22283                 return;
22284         }
22285         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22286            fn.call(this, node.childNodes[i])
22287         }
22288     },
22289     
22290     
22291     /**
22292      * cleanTableWidths.
22293      *
22294      * Quite often pasting from word etc.. results in tables with column and widths.
22295      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22296      *
22297      */
22298     cleanTableWidths : function(node)
22299     {
22300          
22301          
22302         if (!node) {
22303             this.cleanTableWidths(this.doc.body);
22304             return;
22305         }
22306         
22307         // ignore list...
22308         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22309             return; 
22310         }
22311         Roo.log(node.tagName);
22312         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22313             this.iterateChildren(node, this.cleanTableWidths);
22314             return;
22315         }
22316         if (node.hasAttribute('width')) {
22317             node.removeAttribute('width');
22318         }
22319         
22320          
22321         if (node.hasAttribute("style")) {
22322             // pretty basic...
22323             
22324             var styles = node.getAttribute("style").split(";");
22325             var nstyle = [];
22326             Roo.each(styles, function(s) {
22327                 if (!s.match(/:/)) {
22328                     return;
22329                 }
22330                 var kv = s.split(":");
22331                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22332                     return;
22333                 }
22334                 // what ever is left... we allow.
22335                 nstyle.push(s);
22336             });
22337             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22338             if (!nstyle.length) {
22339                 node.removeAttribute('style');
22340             }
22341         }
22342         
22343         this.iterateChildren(node, this.cleanTableWidths);
22344         
22345         
22346     },
22347     
22348     
22349     
22350     
22351     domToHTML : function(currentElement, depth, nopadtext) {
22352         
22353         depth = depth || 0;
22354         nopadtext = nopadtext || false;
22355     
22356         if (!currentElement) {
22357             return this.domToHTML(this.doc.body);
22358         }
22359         
22360         //Roo.log(currentElement);
22361         var j;
22362         var allText = false;
22363         var nodeName = currentElement.nodeName;
22364         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22365         
22366         if  (nodeName == '#text') {
22367             
22368             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22369         }
22370         
22371         
22372         var ret = '';
22373         if (nodeName != 'BODY') {
22374              
22375             var i = 0;
22376             // Prints the node tagName, such as <A>, <IMG>, etc
22377             if (tagName) {
22378                 var attr = [];
22379                 for(i = 0; i < currentElement.attributes.length;i++) {
22380                     // quoting?
22381                     var aname = currentElement.attributes.item(i).name;
22382                     if (!currentElement.attributes.item(i).value.length) {
22383                         continue;
22384                     }
22385                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22386                 }
22387                 
22388                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22389             } 
22390             else {
22391                 
22392                 // eack
22393             }
22394         } else {
22395             tagName = false;
22396         }
22397         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22398             return ret;
22399         }
22400         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22401             nopadtext = true;
22402         }
22403         
22404         
22405         // Traverse the tree
22406         i = 0;
22407         var currentElementChild = currentElement.childNodes.item(i);
22408         var allText = true;
22409         var innerHTML  = '';
22410         lastnode = '';
22411         while (currentElementChild) {
22412             // Formatting code (indent the tree so it looks nice on the screen)
22413             var nopad = nopadtext;
22414             if (lastnode == 'SPAN') {
22415                 nopad  = true;
22416             }
22417             // text
22418             if  (currentElementChild.nodeName == '#text') {
22419                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22420                 toadd = nopadtext ? toadd : toadd.trim();
22421                 if (!nopad && toadd.length > 80) {
22422                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22423                 }
22424                 innerHTML  += toadd;
22425                 
22426                 i++;
22427                 currentElementChild = currentElement.childNodes.item(i);
22428                 lastNode = '';
22429                 continue;
22430             }
22431             allText = false;
22432             
22433             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22434                 
22435             // Recursively traverse the tree structure of the child node
22436             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22437             lastnode = currentElementChild.nodeName;
22438             i++;
22439             currentElementChild=currentElement.childNodes.item(i);
22440         }
22441         
22442         ret += innerHTML;
22443         
22444         if (!allText) {
22445                 // The remaining code is mostly for formatting the tree
22446             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22447         }
22448         
22449         
22450         if (tagName) {
22451             ret+= "</"+tagName+">";
22452         }
22453         return ret;
22454         
22455     },
22456         
22457     applyBlacklists : function()
22458     {
22459         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22460         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22461         
22462         this.white = [];
22463         this.black = [];
22464         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22465             if (b.indexOf(tag) > -1) {
22466                 return;
22467             }
22468             this.white.push(tag);
22469             
22470         }, this);
22471         
22472         Roo.each(w, function(tag) {
22473             if (b.indexOf(tag) > -1) {
22474                 return;
22475             }
22476             if (this.white.indexOf(tag) > -1) {
22477                 return;
22478             }
22479             this.white.push(tag);
22480             
22481         }, this);
22482         
22483         
22484         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22485             if (w.indexOf(tag) > -1) {
22486                 return;
22487             }
22488             this.black.push(tag);
22489             
22490         }, this);
22491         
22492         Roo.each(b, function(tag) {
22493             if (w.indexOf(tag) > -1) {
22494                 return;
22495             }
22496             if (this.black.indexOf(tag) > -1) {
22497                 return;
22498             }
22499             this.black.push(tag);
22500             
22501         }, this);
22502         
22503         
22504         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22505         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22506         
22507         this.cwhite = [];
22508         this.cblack = [];
22509         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22510             if (b.indexOf(tag) > -1) {
22511                 return;
22512             }
22513             this.cwhite.push(tag);
22514             
22515         }, this);
22516         
22517         Roo.each(w, function(tag) {
22518             if (b.indexOf(tag) > -1) {
22519                 return;
22520             }
22521             if (this.cwhite.indexOf(tag) > -1) {
22522                 return;
22523             }
22524             this.cwhite.push(tag);
22525             
22526         }, this);
22527         
22528         
22529         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22530             if (w.indexOf(tag) > -1) {
22531                 return;
22532             }
22533             this.cblack.push(tag);
22534             
22535         }, this);
22536         
22537         Roo.each(b, function(tag) {
22538             if (w.indexOf(tag) > -1) {
22539                 return;
22540             }
22541             if (this.cblack.indexOf(tag) > -1) {
22542                 return;
22543             }
22544             this.cblack.push(tag);
22545             
22546         }, this);
22547     },
22548     
22549     setStylesheets : function(stylesheets)
22550     {
22551         if(typeof(stylesheets) == 'string'){
22552             Roo.get(this.iframe.contentDocument.head).createChild({
22553                 tag : 'link',
22554                 rel : 'stylesheet',
22555                 type : 'text/css',
22556                 href : stylesheets
22557             });
22558             
22559             return;
22560         }
22561         var _this = this;
22562      
22563         Roo.each(stylesheets, function(s) {
22564             if(!s.length){
22565                 return;
22566             }
22567             
22568             Roo.get(_this.iframe.contentDocument.head).createChild({
22569                 tag : 'link',
22570                 rel : 'stylesheet',
22571                 type : 'text/css',
22572                 href : s
22573             });
22574         });
22575
22576         
22577     },
22578     
22579     removeStylesheets : function()
22580     {
22581         var _this = this;
22582         
22583         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22584             s.remove();
22585         });
22586     }
22587     
22588     // hide stuff that is not compatible
22589     /**
22590      * @event blur
22591      * @hide
22592      */
22593     /**
22594      * @event change
22595      * @hide
22596      */
22597     /**
22598      * @event focus
22599      * @hide
22600      */
22601     /**
22602      * @event specialkey
22603      * @hide
22604      */
22605     /**
22606      * @cfg {String} fieldClass @hide
22607      */
22608     /**
22609      * @cfg {String} focusClass @hide
22610      */
22611     /**
22612      * @cfg {String} autoCreate @hide
22613      */
22614     /**
22615      * @cfg {String} inputType @hide
22616      */
22617     /**
22618      * @cfg {String} invalidClass @hide
22619      */
22620     /**
22621      * @cfg {String} invalidText @hide
22622      */
22623     /**
22624      * @cfg {String} msgFx @hide
22625      */
22626     /**
22627      * @cfg {String} validateOnBlur @hide
22628      */
22629 });
22630
22631 Roo.HtmlEditorCore.white = [
22632         'area', 'br', 'img', 'input', 'hr', 'wbr',
22633         
22634        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22635        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22636        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22637        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22638        'table',   'ul',         'xmp', 
22639        
22640        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22641       'thead',   'tr', 
22642      
22643       'dir', 'menu', 'ol', 'ul', 'dl',
22644        
22645       'embed',  'object'
22646 ];
22647
22648
22649 Roo.HtmlEditorCore.black = [
22650     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22651         'applet', // 
22652         'base',   'basefont', 'bgsound', 'blink',  'body', 
22653         'frame',  'frameset', 'head',    'html',   'ilayer', 
22654         'iframe', 'layer',  'link',     'meta',    'object',   
22655         'script', 'style' ,'title',  'xml' // clean later..
22656 ];
22657 Roo.HtmlEditorCore.clean = [
22658     'script', 'style', 'title', 'xml'
22659 ];
22660 Roo.HtmlEditorCore.remove = [
22661     'font'
22662 ];
22663 // attributes..
22664
22665 Roo.HtmlEditorCore.ablack = [
22666     'on'
22667 ];
22668     
22669 Roo.HtmlEditorCore.aclean = [ 
22670     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22671 ];
22672
22673 // protocols..
22674 Roo.HtmlEditorCore.pwhite= [
22675         'http',  'https',  'mailto'
22676 ];
22677
22678 // white listed style attributes.
22679 Roo.HtmlEditorCore.cwhite= [
22680       //  'text-align', /// default is to allow most things..
22681       
22682          
22683 //        'font-size'//??
22684 ];
22685
22686 // black listed style attributes.
22687 Roo.HtmlEditorCore.cblack= [
22688       //  'font-size' -- this can be set by the project 
22689 ];
22690
22691
22692 Roo.HtmlEditorCore.swapCodes   =[ 
22693     [    8211, "--" ], 
22694     [    8212, "--" ], 
22695     [    8216,  "'" ],  
22696     [    8217, "'" ],  
22697     [    8220, '"' ],  
22698     [    8221, '"' ],  
22699     [    8226, "*" ],  
22700     [    8230, "..." ]
22701 ]; 
22702
22703     /*
22704  * - LGPL
22705  *
22706  * HtmlEditor
22707  * 
22708  */
22709
22710 /**
22711  * @class Roo.bootstrap.HtmlEditor
22712  * @extends Roo.bootstrap.TextArea
22713  * Bootstrap HtmlEditor class
22714
22715  * @constructor
22716  * Create a new HtmlEditor
22717  * @param {Object} config The config object
22718  */
22719
22720 Roo.bootstrap.HtmlEditor = function(config){
22721     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22722     if (!this.toolbars) {
22723         this.toolbars = [];
22724     }
22725     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22726     this.addEvents({
22727             /**
22728              * @event initialize
22729              * Fires when the editor is fully initialized (including the iframe)
22730              * @param {HtmlEditor} this
22731              */
22732             initialize: true,
22733             /**
22734              * @event activate
22735              * Fires when the editor is first receives the focus. Any insertion must wait
22736              * until after this event.
22737              * @param {HtmlEditor} this
22738              */
22739             activate: true,
22740              /**
22741              * @event beforesync
22742              * Fires before the textarea is updated with content from the editor iframe. Return false
22743              * to cancel the sync.
22744              * @param {HtmlEditor} this
22745              * @param {String} html
22746              */
22747             beforesync: true,
22748              /**
22749              * @event beforepush
22750              * Fires before the iframe editor is updated with content from the textarea. Return false
22751              * to cancel the push.
22752              * @param {HtmlEditor} this
22753              * @param {String} html
22754              */
22755             beforepush: true,
22756              /**
22757              * @event sync
22758              * Fires when the textarea is updated with content from the editor iframe.
22759              * @param {HtmlEditor} this
22760              * @param {String} html
22761              */
22762             sync: true,
22763              /**
22764              * @event push
22765              * Fires when the iframe editor is updated with content from the textarea.
22766              * @param {HtmlEditor} this
22767              * @param {String} html
22768              */
22769             push: true,
22770              /**
22771              * @event editmodechange
22772              * Fires when the editor switches edit modes
22773              * @param {HtmlEditor} this
22774              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22775              */
22776             editmodechange: true,
22777             /**
22778              * @event editorevent
22779              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22780              * @param {HtmlEditor} this
22781              */
22782             editorevent: true,
22783             /**
22784              * @event firstfocus
22785              * Fires when on first focus - needed by toolbars..
22786              * @param {HtmlEditor} this
22787              */
22788             firstfocus: true,
22789             /**
22790              * @event autosave
22791              * Auto save the htmlEditor value as a file into Events
22792              * @param {HtmlEditor} this
22793              */
22794             autosave: true,
22795             /**
22796              * @event savedpreview
22797              * preview the saved version of htmlEditor
22798              * @param {HtmlEditor} this
22799              */
22800             savedpreview: true
22801         });
22802 };
22803
22804
22805 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
22806     
22807     
22808       /**
22809      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22810      */
22811     toolbars : false,
22812    
22813      /**
22814      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22815      *                        Roo.resizable.
22816      */
22817     resizable : false,
22818      /**
22819      * @cfg {Number} height (in pixels)
22820      */   
22821     height: 300,
22822    /**
22823      * @cfg {Number} width (in pixels)
22824      */   
22825     width: false,
22826     
22827     /**
22828      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22829      * 
22830      */
22831     stylesheets: false,
22832     
22833     // id of frame..
22834     frameId: false,
22835     
22836     // private properties
22837     validationEvent : false,
22838     deferHeight: true,
22839     initialized : false,
22840     activated : false,
22841     
22842     onFocus : Roo.emptyFn,
22843     iframePad:3,
22844     hideMode:'offsets',
22845     
22846     
22847     tbContainer : false,
22848     
22849     toolbarContainer :function() {
22850         return this.wrap.select('.x-html-editor-tb',true).first();
22851     },
22852
22853     /**
22854      * Protected method that will not generally be called directly. It
22855      * is called when the editor creates its toolbar. Override this method if you need to
22856      * add custom toolbar buttons.
22857      * @param {HtmlEditor} editor
22858      */
22859     createToolbar : function(){
22860         
22861         Roo.log("create toolbars");
22862         
22863         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
22864         this.toolbars[0].render(this.toolbarContainer());
22865         
22866         return;
22867         
22868 //        if (!editor.toolbars || !editor.toolbars.length) {
22869 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
22870 //        }
22871 //        
22872 //        for (var i =0 ; i < editor.toolbars.length;i++) {
22873 //            editor.toolbars[i] = Roo.factory(
22874 //                    typeof(editor.toolbars[i]) == 'string' ?
22875 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
22876 //                Roo.bootstrap.HtmlEditor);
22877 //            editor.toolbars[i].init(editor);
22878 //        }
22879     },
22880
22881      
22882     // private
22883     onRender : function(ct, position)
22884     {
22885        // Roo.log("Call onRender: " + this.xtype);
22886         var _t = this;
22887         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
22888       
22889         this.wrap = this.inputEl().wrap({
22890             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22891         });
22892         
22893         this.editorcore.onRender(ct, position);
22894          
22895         if (this.resizable) {
22896             this.resizeEl = new Roo.Resizable(this.wrap, {
22897                 pinned : true,
22898                 wrap: true,
22899                 dynamic : true,
22900                 minHeight : this.height,
22901                 height: this.height,
22902                 handles : this.resizable,
22903                 width: this.width,
22904                 listeners : {
22905                     resize : function(r, w, h) {
22906                         _t.onResize(w,h); // -something
22907                     }
22908                 }
22909             });
22910             
22911         }
22912         this.createToolbar(this);
22913        
22914         
22915         if(!this.width && this.resizable){
22916             this.setSize(this.wrap.getSize());
22917         }
22918         if (this.resizeEl) {
22919             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22920             // should trigger onReize..
22921         }
22922         
22923     },
22924
22925     // private
22926     onResize : function(w, h)
22927     {
22928         Roo.log('resize: ' +w + ',' + h );
22929         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
22930         var ew = false;
22931         var eh = false;
22932         
22933         if(this.inputEl() ){
22934             if(typeof w == 'number'){
22935                 var aw = w - this.wrap.getFrameWidth('lr');
22936                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
22937                 ew = aw;
22938             }
22939             if(typeof h == 'number'){
22940                  var tbh = -11;  // fixme it needs to tool bar size!
22941                 for (var i =0; i < this.toolbars.length;i++) {
22942                     // fixme - ask toolbars for heights?
22943                     tbh += this.toolbars[i].el.getHeight();
22944                     //if (this.toolbars[i].footer) {
22945                     //    tbh += this.toolbars[i].footer.el.getHeight();
22946                     //}
22947                 }
22948               
22949                 
22950                 
22951                 
22952                 
22953                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22954                 ah -= 5; // knock a few pixes off for look..
22955                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
22956                 var eh = ah;
22957             }
22958         }
22959         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22960         this.editorcore.onResize(ew,eh);
22961         
22962     },
22963
22964     /**
22965      * Toggles the editor between standard and source edit mode.
22966      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22967      */
22968     toggleSourceEdit : function(sourceEditMode)
22969     {
22970         this.editorcore.toggleSourceEdit(sourceEditMode);
22971         
22972         if(this.editorcore.sourceEditMode){
22973             Roo.log('editor - showing textarea');
22974             
22975 //            Roo.log('in');
22976 //            Roo.log(this.syncValue());
22977             this.syncValue();
22978             this.inputEl().removeClass(['hide', 'x-hidden']);
22979             this.inputEl().dom.removeAttribute('tabIndex');
22980             this.inputEl().focus();
22981         }else{
22982             Roo.log('editor - hiding textarea');
22983 //            Roo.log('out')
22984 //            Roo.log(this.pushValue()); 
22985             this.pushValue();
22986             
22987             this.inputEl().addClass(['hide', 'x-hidden']);
22988             this.inputEl().dom.setAttribute('tabIndex', -1);
22989             //this.deferFocus();
22990         }
22991          
22992         if(this.resizable){
22993             this.setSize(this.wrap.getSize());
22994         }
22995         
22996         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22997     },
22998  
22999     // private (for BoxComponent)
23000     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23001
23002     // private (for BoxComponent)
23003     getResizeEl : function(){
23004         return this.wrap;
23005     },
23006
23007     // private (for BoxComponent)
23008     getPositionEl : function(){
23009         return this.wrap;
23010     },
23011
23012     // private
23013     initEvents : function(){
23014         this.originalValue = this.getValue();
23015     },
23016
23017 //    /**
23018 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23019 //     * @method
23020 //     */
23021 //    markInvalid : Roo.emptyFn,
23022 //    /**
23023 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23024 //     * @method
23025 //     */
23026 //    clearInvalid : Roo.emptyFn,
23027
23028     setValue : function(v){
23029         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23030         this.editorcore.pushValue();
23031     },
23032
23033      
23034     // private
23035     deferFocus : function(){
23036         this.focus.defer(10, this);
23037     },
23038
23039     // doc'ed in Field
23040     focus : function(){
23041         this.editorcore.focus();
23042         
23043     },
23044       
23045
23046     // private
23047     onDestroy : function(){
23048         
23049         
23050         
23051         if(this.rendered){
23052             
23053             for (var i =0; i < this.toolbars.length;i++) {
23054                 // fixme - ask toolbars for heights?
23055                 this.toolbars[i].onDestroy();
23056             }
23057             
23058             this.wrap.dom.innerHTML = '';
23059             this.wrap.remove();
23060         }
23061     },
23062
23063     // private
23064     onFirstFocus : function(){
23065         //Roo.log("onFirstFocus");
23066         this.editorcore.onFirstFocus();
23067          for (var i =0; i < this.toolbars.length;i++) {
23068             this.toolbars[i].onFirstFocus();
23069         }
23070         
23071     },
23072     
23073     // private
23074     syncValue : function()
23075     {   
23076         this.editorcore.syncValue();
23077     },
23078     
23079     pushValue : function()
23080     {   
23081         this.editorcore.pushValue();
23082     }
23083      
23084     
23085     // hide stuff that is not compatible
23086     /**
23087      * @event blur
23088      * @hide
23089      */
23090     /**
23091      * @event change
23092      * @hide
23093      */
23094     /**
23095      * @event focus
23096      * @hide
23097      */
23098     /**
23099      * @event specialkey
23100      * @hide
23101      */
23102     /**
23103      * @cfg {String} fieldClass @hide
23104      */
23105     /**
23106      * @cfg {String} focusClass @hide
23107      */
23108     /**
23109      * @cfg {String} autoCreate @hide
23110      */
23111     /**
23112      * @cfg {String} inputType @hide
23113      */
23114     /**
23115      * @cfg {String} invalidClass @hide
23116      */
23117     /**
23118      * @cfg {String} invalidText @hide
23119      */
23120     /**
23121      * @cfg {String} msgFx @hide
23122      */
23123     /**
23124      * @cfg {String} validateOnBlur @hide
23125      */
23126 });
23127  
23128     
23129    
23130    
23131    
23132       
23133 Roo.namespace('Roo.bootstrap.htmleditor');
23134 /**
23135  * @class Roo.bootstrap.HtmlEditorToolbar1
23136  * Basic Toolbar
23137  * 
23138  * Usage:
23139  *
23140  new Roo.bootstrap.HtmlEditor({
23141     ....
23142     toolbars : [
23143         new Roo.bootstrap.HtmlEditorToolbar1({
23144             disable : { fonts: 1 , format: 1, ..., ... , ...],
23145             btns : [ .... ]
23146         })
23147     }
23148      
23149  * 
23150  * @cfg {Object} disable List of elements to disable..
23151  * @cfg {Array} btns List of additional buttons.
23152  * 
23153  * 
23154  * NEEDS Extra CSS? 
23155  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23156  */
23157  
23158 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23159 {
23160     
23161     Roo.apply(this, config);
23162     
23163     // default disabled, based on 'good practice'..
23164     this.disable = this.disable || {};
23165     Roo.applyIf(this.disable, {
23166         fontSize : true,
23167         colors : true,
23168         specialElements : true
23169     });
23170     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23171     
23172     this.editor = config.editor;
23173     this.editorcore = config.editor.editorcore;
23174     
23175     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23176     
23177     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23178     // dont call parent... till later.
23179 }
23180 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23181      
23182     bar : true,
23183     
23184     editor : false,
23185     editorcore : false,
23186     
23187     
23188     formats : [
23189         "p" ,  
23190         "h1","h2","h3","h4","h5","h6", 
23191         "pre", "code", 
23192         "abbr", "acronym", "address", "cite", "samp", "var",
23193         'div','span'
23194     ],
23195     
23196     onRender : function(ct, position)
23197     {
23198        // Roo.log("Call onRender: " + this.xtype);
23199         
23200        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23201        Roo.log(this.el);
23202        this.el.dom.style.marginBottom = '0';
23203        var _this = this;
23204        var editorcore = this.editorcore;
23205        var editor= this.editor;
23206        
23207        var children = [];
23208        var btn = function(id,cmd , toggle, handler){
23209        
23210             var  event = toggle ? 'toggle' : 'click';
23211        
23212             var a = {
23213                 size : 'sm',
23214                 xtype: 'Button',
23215                 xns: Roo.bootstrap,
23216                 glyphicon : id,
23217                 cmd : id || cmd,
23218                 enableToggle:toggle !== false,
23219                 //html : 'submit'
23220                 pressed : toggle ? false : null,
23221                 listeners : {}
23222             };
23223             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23224                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23225             };
23226             children.push(a);
23227             return a;
23228        }
23229         
23230         var style = {
23231                 xtype: 'Button',
23232                 size : 'sm',
23233                 xns: Roo.bootstrap,
23234                 glyphicon : 'font',
23235                 //html : 'submit'
23236                 menu : {
23237                     xtype: 'Menu',
23238                     xns: Roo.bootstrap,
23239                     items:  []
23240                 }
23241         };
23242         Roo.each(this.formats, function(f) {
23243             style.menu.items.push({
23244                 xtype :'MenuItem',
23245                 xns: Roo.bootstrap,
23246                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23247                 tagname : f,
23248                 listeners : {
23249                     click : function()
23250                     {
23251                         editorcore.insertTag(this.tagname);
23252                         editor.focus();
23253                     }
23254                 }
23255                 
23256             });
23257         });
23258          children.push(style);   
23259             
23260             
23261         btn('bold',false,true);
23262         btn('italic',false,true);
23263         btn('align-left', 'justifyleft',true);
23264         btn('align-center', 'justifycenter',true);
23265         btn('align-right' , 'justifyright',true);
23266         btn('link', false, false, function(btn) {
23267             //Roo.log("create link?");
23268             var url = prompt(this.createLinkText, this.defaultLinkValue);
23269             if(url && url != 'http:/'+'/'){
23270                 this.editorcore.relayCmd('createlink', url);
23271             }
23272         }),
23273         btn('list','insertunorderedlist',true);
23274         btn('pencil', false,true, function(btn){
23275                 Roo.log(this);
23276                 
23277                 this.toggleSourceEdit(btn.pressed);
23278         });
23279         /*
23280         var cog = {
23281                 xtype: 'Button',
23282                 size : 'sm',
23283                 xns: Roo.bootstrap,
23284                 glyphicon : 'cog',
23285                 //html : 'submit'
23286                 menu : {
23287                     xtype: 'Menu',
23288                     xns: Roo.bootstrap,
23289                     items:  []
23290                 }
23291         };
23292         
23293         cog.menu.items.push({
23294             xtype :'MenuItem',
23295             xns: Roo.bootstrap,
23296             html : Clean styles,
23297             tagname : f,
23298             listeners : {
23299                 click : function()
23300                 {
23301                     editorcore.insertTag(this.tagname);
23302                     editor.focus();
23303                 }
23304             }
23305             
23306         });
23307        */
23308         
23309          
23310        this.xtype = 'NavSimplebar';
23311         
23312         for(var i=0;i< children.length;i++) {
23313             
23314             this.buttons.add(this.addxtypeChild(children[i]));
23315             
23316         }
23317         
23318         editor.on('editorevent', this.updateToolbar, this);
23319     },
23320     onBtnClick : function(id)
23321     {
23322        this.editorcore.relayCmd(id);
23323        this.editorcore.focus();
23324     },
23325     
23326     /**
23327      * Protected method that will not generally be called directly. It triggers
23328      * a toolbar update by reading the markup state of the current selection in the editor.
23329      */
23330     updateToolbar: function(){
23331
23332         if(!this.editorcore.activated){
23333             this.editor.onFirstFocus(); // is this neeed?
23334             return;
23335         }
23336
23337         var btns = this.buttons; 
23338         var doc = this.editorcore.doc;
23339         btns.get('bold').setActive(doc.queryCommandState('bold'));
23340         btns.get('italic').setActive(doc.queryCommandState('italic'));
23341         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23342         
23343         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23344         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23345         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23346         
23347         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23348         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23349          /*
23350         
23351         var ans = this.editorcore.getAllAncestors();
23352         if (this.formatCombo) {
23353             
23354             
23355             var store = this.formatCombo.store;
23356             this.formatCombo.setValue("");
23357             for (var i =0; i < ans.length;i++) {
23358                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23359                     // select it..
23360                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23361                     break;
23362                 }
23363             }
23364         }
23365         
23366         
23367         
23368         // hides menus... - so this cant be on a menu...
23369         Roo.bootstrap.MenuMgr.hideAll();
23370         */
23371         Roo.bootstrap.MenuMgr.hideAll();
23372         //this.editorsyncValue();
23373     },
23374     onFirstFocus: function() {
23375         this.buttons.each(function(item){
23376            item.enable();
23377         });
23378     },
23379     toggleSourceEdit : function(sourceEditMode){
23380         
23381           
23382         if(sourceEditMode){
23383             Roo.log("disabling buttons");
23384            this.buttons.each( function(item){
23385                 if(item.cmd != 'pencil'){
23386                     item.disable();
23387                 }
23388             });
23389           
23390         }else{
23391             Roo.log("enabling buttons");
23392             if(this.editorcore.initialized){
23393                 this.buttons.each( function(item){
23394                     item.enable();
23395                 });
23396             }
23397             
23398         }
23399         Roo.log("calling toggole on editor");
23400         // tell the editor that it's been pressed..
23401         this.editor.toggleSourceEdit(sourceEditMode);
23402        
23403     }
23404 });
23405
23406
23407
23408
23409
23410 /**
23411  * @class Roo.bootstrap.Table.AbstractSelectionModel
23412  * @extends Roo.util.Observable
23413  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23414  * implemented by descendant classes.  This class should not be directly instantiated.
23415  * @constructor
23416  */
23417 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23418     this.locked = false;
23419     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23420 };
23421
23422
23423 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23424     /** @ignore Called by the grid automatically. Do not call directly. */
23425     init : function(grid){
23426         this.grid = grid;
23427         this.initEvents();
23428     },
23429
23430     /**
23431      * Locks the selections.
23432      */
23433     lock : function(){
23434         this.locked = true;
23435     },
23436
23437     /**
23438      * Unlocks the selections.
23439      */
23440     unlock : function(){
23441         this.locked = false;
23442     },
23443
23444     /**
23445      * Returns true if the selections are locked.
23446      * @return {Boolean}
23447      */
23448     isLocked : function(){
23449         return this.locked;
23450     }
23451 });
23452 /**
23453  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23454  * @class Roo.bootstrap.Table.RowSelectionModel
23455  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23456  * It supports multiple selections and keyboard selection/navigation. 
23457  * @constructor
23458  * @param {Object} config
23459  */
23460
23461 Roo.bootstrap.Table.RowSelectionModel = function(config){
23462     Roo.apply(this, config);
23463     this.selections = new Roo.util.MixedCollection(false, function(o){
23464         return o.id;
23465     });
23466
23467     this.last = false;
23468     this.lastActive = false;
23469
23470     this.addEvents({
23471         /**
23472              * @event selectionchange
23473              * Fires when the selection changes
23474              * @param {SelectionModel} this
23475              */
23476             "selectionchange" : true,
23477         /**
23478              * @event afterselectionchange
23479              * Fires after the selection changes (eg. by key press or clicking)
23480              * @param {SelectionModel} this
23481              */
23482             "afterselectionchange" : true,
23483         /**
23484              * @event beforerowselect
23485              * Fires when a row is selected being selected, return false to cancel.
23486              * @param {SelectionModel} this
23487              * @param {Number} rowIndex The selected index
23488              * @param {Boolean} keepExisting False if other selections will be cleared
23489              */
23490             "beforerowselect" : true,
23491         /**
23492              * @event rowselect
23493              * Fires when a row is selected.
23494              * @param {SelectionModel} this
23495              * @param {Number} rowIndex The selected index
23496              * @param {Roo.data.Record} r The record
23497              */
23498             "rowselect" : true,
23499         /**
23500              * @event rowdeselect
23501              * Fires when a row is deselected.
23502              * @param {SelectionModel} this
23503              * @param {Number} rowIndex The selected index
23504              */
23505         "rowdeselect" : true
23506     });
23507     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23508     this.locked = false;
23509  };
23510
23511 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23512     /**
23513      * @cfg {Boolean} singleSelect
23514      * True to allow selection of only one row at a time (defaults to false)
23515      */
23516     singleSelect : false,
23517
23518     // private
23519     initEvents : function()
23520     {
23521
23522         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23523         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23524         //}else{ // allow click to work like normal
23525          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23526         //}
23527         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23528         this.grid.on("rowclick", this.handleMouseDown, this);
23529         
23530         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23531             "up" : function(e){
23532                 if(!e.shiftKey){
23533                     this.selectPrevious(e.shiftKey);
23534                 }else if(this.last !== false && this.lastActive !== false){
23535                     var last = this.last;
23536                     this.selectRange(this.last,  this.lastActive-1);
23537                     this.grid.getView().focusRow(this.lastActive);
23538                     if(last !== false){
23539                         this.last = last;
23540                     }
23541                 }else{
23542                     this.selectFirstRow();
23543                 }
23544                 this.fireEvent("afterselectionchange", this);
23545             },
23546             "down" : function(e){
23547                 if(!e.shiftKey){
23548                     this.selectNext(e.shiftKey);
23549                 }else if(this.last !== false && this.lastActive !== false){
23550                     var last = this.last;
23551                     this.selectRange(this.last,  this.lastActive+1);
23552                     this.grid.getView().focusRow(this.lastActive);
23553                     if(last !== false){
23554                         this.last = last;
23555                     }
23556                 }else{
23557                     this.selectFirstRow();
23558                 }
23559                 this.fireEvent("afterselectionchange", this);
23560             },
23561             scope: this
23562         });
23563         this.grid.store.on('load', function(){
23564             this.selections.clear();
23565         },this);
23566         /*
23567         var view = this.grid.view;
23568         view.on("refresh", this.onRefresh, this);
23569         view.on("rowupdated", this.onRowUpdated, this);
23570         view.on("rowremoved", this.onRemove, this);
23571         */
23572     },
23573
23574     // private
23575     onRefresh : function()
23576     {
23577         var ds = this.grid.store, i, v = this.grid.view;
23578         var s = this.selections;
23579         s.each(function(r){
23580             if((i = ds.indexOfId(r.id)) != -1){
23581                 v.onRowSelect(i);
23582             }else{
23583                 s.remove(r);
23584             }
23585         });
23586     },
23587
23588     // private
23589     onRemove : function(v, index, r){
23590         this.selections.remove(r);
23591     },
23592
23593     // private
23594     onRowUpdated : function(v, index, r){
23595         if(this.isSelected(r)){
23596             v.onRowSelect(index);
23597         }
23598     },
23599
23600     /**
23601      * Select records.
23602      * @param {Array} records The records to select
23603      * @param {Boolean} keepExisting (optional) True to keep existing selections
23604      */
23605     selectRecords : function(records, keepExisting)
23606     {
23607         if(!keepExisting){
23608             this.clearSelections();
23609         }
23610             var ds = this.grid.store;
23611         for(var i = 0, len = records.length; i < len; i++){
23612             this.selectRow(ds.indexOf(records[i]), true);
23613         }
23614     },
23615
23616     /**
23617      * Gets the number of selected rows.
23618      * @return {Number}
23619      */
23620     getCount : function(){
23621         return this.selections.length;
23622     },
23623
23624     /**
23625      * Selects the first row in the grid.
23626      */
23627     selectFirstRow : function(){
23628         this.selectRow(0);
23629     },
23630
23631     /**
23632      * Select the last row.
23633      * @param {Boolean} keepExisting (optional) True to keep existing selections
23634      */
23635     selectLastRow : function(keepExisting){
23636         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
23637         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
23638     },
23639
23640     /**
23641      * Selects the row immediately following the last selected row.
23642      * @param {Boolean} keepExisting (optional) True to keep existing selections
23643      */
23644     selectNext : function(keepExisting)
23645     {
23646             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
23647             this.selectRow(this.last+1, keepExisting);
23648             this.grid.getView().focusRow(this.last);
23649         }
23650     },
23651
23652     /**
23653      * Selects the row that precedes the last selected row.
23654      * @param {Boolean} keepExisting (optional) True to keep existing selections
23655      */
23656     selectPrevious : function(keepExisting){
23657         if(this.last){
23658             this.selectRow(this.last-1, keepExisting);
23659             this.grid.getView().focusRow(this.last);
23660         }
23661     },
23662
23663     /**
23664      * Returns the selected records
23665      * @return {Array} Array of selected records
23666      */
23667     getSelections : function(){
23668         return [].concat(this.selections.items);
23669     },
23670
23671     /**
23672      * Returns the first selected record.
23673      * @return {Record}
23674      */
23675     getSelected : function(){
23676         return this.selections.itemAt(0);
23677     },
23678
23679
23680     /**
23681      * Clears all selections.
23682      */
23683     clearSelections : function(fast)
23684     {
23685         if(this.locked) {
23686             return;
23687         }
23688         if(fast !== true){
23689                 var ds = this.grid.store;
23690             var s = this.selections;
23691             s.each(function(r){
23692                 this.deselectRow(ds.indexOfId(r.id));
23693             }, this);
23694             s.clear();
23695         }else{
23696             this.selections.clear();
23697         }
23698         this.last = false;
23699     },
23700
23701
23702     /**
23703      * Selects all rows.
23704      */
23705     selectAll : function(){
23706         if(this.locked) {
23707             return;
23708         }
23709         this.selections.clear();
23710         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23711             this.selectRow(i, true);
23712         }
23713     },
23714
23715     /**
23716      * Returns True if there is a selection.
23717      * @return {Boolean}
23718      */
23719     hasSelection : function(){
23720         return this.selections.length > 0;
23721     },
23722
23723     /**
23724      * Returns True if the specified row is selected.
23725      * @param {Number/Record} record The record or index of the record to check
23726      * @return {Boolean}
23727      */
23728     isSelected : function(index){
23729             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23730         return (r && this.selections.key(r.id) ? true : false);
23731     },
23732
23733     /**
23734      * Returns True if the specified record id is selected.
23735      * @param {String} id The id of record to check
23736      * @return {Boolean}
23737      */
23738     isIdSelected : function(id){
23739         return (this.selections.key(id) ? true : false);
23740     },
23741
23742
23743     // private
23744     handleMouseDBClick : function(e, t){
23745         
23746     },
23747     // private
23748     handleMouseDown : function(e, t)
23749     {
23750             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23751         if(this.isLocked() || rowIndex < 0 ){
23752             return;
23753         };
23754         if(e.shiftKey && this.last !== false){
23755             var last = this.last;
23756             this.selectRange(last, rowIndex, e.ctrlKey);
23757             this.last = last; // reset the last
23758             t.focus();
23759     
23760         }else{
23761             var isSelected = this.isSelected(rowIndex);
23762             //Roo.log("select row:" + rowIndex);
23763             if(isSelected){
23764                 this.deselectRow(rowIndex);
23765             } else {
23766                         this.selectRow(rowIndex, true);
23767             }
23768     
23769             /*
23770                 if(e.button !== 0 && isSelected){
23771                 alert('rowIndex 2: ' + rowIndex);
23772                     view.focusRow(rowIndex);
23773                 }else if(e.ctrlKey && isSelected){
23774                     this.deselectRow(rowIndex);
23775                 }else if(!isSelected){
23776                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23777                     view.focusRow(rowIndex);
23778                 }
23779             */
23780         }
23781         this.fireEvent("afterselectionchange", this);
23782     },
23783     // private
23784     handleDragableRowClick :  function(grid, rowIndex, e) 
23785     {
23786         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23787             this.selectRow(rowIndex, false);
23788             grid.view.focusRow(rowIndex);
23789              this.fireEvent("afterselectionchange", this);
23790         }
23791     },
23792     
23793     /**
23794      * Selects multiple rows.
23795      * @param {Array} rows Array of the indexes of the row to select
23796      * @param {Boolean} keepExisting (optional) True to keep existing selections
23797      */
23798     selectRows : function(rows, keepExisting){
23799         if(!keepExisting){
23800             this.clearSelections();
23801         }
23802         for(var i = 0, len = rows.length; i < len; i++){
23803             this.selectRow(rows[i], true);
23804         }
23805     },
23806
23807     /**
23808      * Selects a range of rows. All rows in between startRow and endRow are also selected.
23809      * @param {Number} startRow The index of the first row in the range
23810      * @param {Number} endRow The index of the last row in the range
23811      * @param {Boolean} keepExisting (optional) True to retain existing selections
23812      */
23813     selectRange : function(startRow, endRow, keepExisting){
23814         if(this.locked) {
23815             return;
23816         }
23817         if(!keepExisting){
23818             this.clearSelections();
23819         }
23820         if(startRow <= endRow){
23821             for(var i = startRow; i <= endRow; i++){
23822                 this.selectRow(i, true);
23823             }
23824         }else{
23825             for(var i = startRow; i >= endRow; i--){
23826                 this.selectRow(i, true);
23827             }
23828         }
23829     },
23830
23831     /**
23832      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
23833      * @param {Number} startRow The index of the first row in the range
23834      * @param {Number} endRow The index of the last row in the range
23835      */
23836     deselectRange : function(startRow, endRow, preventViewNotify){
23837         if(this.locked) {
23838             return;
23839         }
23840         for(var i = startRow; i <= endRow; i++){
23841             this.deselectRow(i, preventViewNotify);
23842         }
23843     },
23844
23845     /**
23846      * Selects a row.
23847      * @param {Number} row The index of the row to select
23848      * @param {Boolean} keepExisting (optional) True to keep existing selections
23849      */
23850     selectRow : function(index, keepExisting, preventViewNotify)
23851     {
23852             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
23853             return;
23854         }
23855         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
23856             if(!keepExisting || this.singleSelect){
23857                 this.clearSelections();
23858             }
23859             
23860             var r = this.grid.store.getAt(index);
23861             //console.log('selectRow - record id :' + r.id);
23862             
23863             this.selections.add(r);
23864             this.last = this.lastActive = index;
23865             if(!preventViewNotify){
23866                 var proxy = new Roo.Element(
23867                                 this.grid.getRowDom(index)
23868                 );
23869                 proxy.addClass('bg-info info');
23870             }
23871             this.fireEvent("rowselect", this, index, r);
23872             this.fireEvent("selectionchange", this);
23873         }
23874     },
23875
23876     /**
23877      * Deselects a row.
23878      * @param {Number} row The index of the row to deselect
23879      */
23880     deselectRow : function(index, preventViewNotify)
23881     {
23882         if(this.locked) {
23883             return;
23884         }
23885         if(this.last == index){
23886             this.last = false;
23887         }
23888         if(this.lastActive == index){
23889             this.lastActive = false;
23890         }
23891         
23892         var r = this.grid.store.getAt(index);
23893         if (!r) {
23894             return;
23895         }
23896         
23897         this.selections.remove(r);
23898         //.console.log('deselectRow - record id :' + r.id);
23899         if(!preventViewNotify){
23900         
23901             var proxy = new Roo.Element(
23902                 this.grid.getRowDom(index)
23903             );
23904             proxy.removeClass('bg-info info');
23905         }
23906         this.fireEvent("rowdeselect", this, index);
23907         this.fireEvent("selectionchange", this);
23908     },
23909
23910     // private
23911     restoreLast : function(){
23912         if(this._last){
23913             this.last = this._last;
23914         }
23915     },
23916
23917     // private
23918     acceptsNav : function(row, col, cm){
23919         return !cm.isHidden(col) && cm.isCellEditable(col, row);
23920     },
23921
23922     // private
23923     onEditorKey : function(field, e){
23924         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
23925         if(k == e.TAB){
23926             e.stopEvent();
23927             ed.completeEdit();
23928             if(e.shiftKey){
23929                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
23930             }else{
23931                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
23932             }
23933         }else if(k == e.ENTER && !e.ctrlKey){
23934             e.stopEvent();
23935             ed.completeEdit();
23936             if(e.shiftKey){
23937                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
23938             }else{
23939                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
23940             }
23941         }else if(k == e.ESC){
23942             ed.cancelEdit();
23943         }
23944         if(newCell){
23945             g.startEditing(newCell[0], newCell[1]);
23946         }
23947     }
23948 });
23949 /*
23950  * Based on:
23951  * Ext JS Library 1.1.1
23952  * Copyright(c) 2006-2007, Ext JS, LLC.
23953  *
23954  * Originally Released Under LGPL - original licence link has changed is not relivant.
23955  *
23956  * Fork - LGPL
23957  * <script type="text/javascript">
23958  */
23959  
23960 /**
23961  * @class Roo.bootstrap.PagingToolbar
23962  * @extends Roo.bootstrap.NavSimplebar
23963  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
23964  * @constructor
23965  * Create a new PagingToolbar
23966  * @param {Object} config The config object
23967  * @param {Roo.data.Store} store
23968  */
23969 Roo.bootstrap.PagingToolbar = function(config)
23970 {
23971     // old args format still supported... - xtype is prefered..
23972         // created from xtype...
23973     
23974     this.ds = config.dataSource;
23975     
23976     if (config.store && !this.ds) {
23977         this.store= Roo.factory(config.store, Roo.data);
23978         this.ds = this.store;
23979         this.ds.xmodule = this.xmodule || false;
23980     }
23981     
23982     this.toolbarItems = [];
23983     if (config.items) {
23984         this.toolbarItems = config.items;
23985     }
23986     
23987     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
23988     
23989     this.cursor = 0;
23990     
23991     if (this.ds) { 
23992         this.bind(this.ds);
23993     }
23994     
23995     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
23996     
23997 };
23998
23999 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24000     /**
24001      * @cfg {Roo.data.Store} dataSource
24002      * The underlying data store providing the paged data
24003      */
24004     /**
24005      * @cfg {String/HTMLElement/Element} container
24006      * container The id or element that will contain the toolbar
24007      */
24008     /**
24009      * @cfg {Boolean} displayInfo
24010      * True to display the displayMsg (defaults to false)
24011      */
24012     /**
24013      * @cfg {Number} pageSize
24014      * The number of records to display per page (defaults to 20)
24015      */
24016     pageSize: 20,
24017     /**
24018      * @cfg {String} displayMsg
24019      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24020      */
24021     displayMsg : 'Displaying {0} - {1} of {2}',
24022     /**
24023      * @cfg {String} emptyMsg
24024      * The message to display when no records are found (defaults to "No data to display")
24025      */
24026     emptyMsg : 'No data to display',
24027     /**
24028      * Customizable piece of the default paging text (defaults to "Page")
24029      * @type String
24030      */
24031     beforePageText : "Page",
24032     /**
24033      * Customizable piece of the default paging text (defaults to "of %0")
24034      * @type String
24035      */
24036     afterPageText : "of {0}",
24037     /**
24038      * Customizable piece of the default paging text (defaults to "First Page")
24039      * @type String
24040      */
24041     firstText : "First Page",
24042     /**
24043      * Customizable piece of the default paging text (defaults to "Previous Page")
24044      * @type String
24045      */
24046     prevText : "Previous Page",
24047     /**
24048      * Customizable piece of the default paging text (defaults to "Next Page")
24049      * @type String
24050      */
24051     nextText : "Next Page",
24052     /**
24053      * Customizable piece of the default paging text (defaults to "Last Page")
24054      * @type String
24055      */
24056     lastText : "Last Page",
24057     /**
24058      * Customizable piece of the default paging text (defaults to "Refresh")
24059      * @type String
24060      */
24061     refreshText : "Refresh",
24062
24063     buttons : false,
24064     // private
24065     onRender : function(ct, position) 
24066     {
24067         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24068         this.navgroup.parentId = this.id;
24069         this.navgroup.onRender(this.el, null);
24070         // add the buttons to the navgroup
24071         
24072         if(this.displayInfo){
24073             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24074             this.displayEl = this.el.select('.x-paging-info', true).first();
24075 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24076 //            this.displayEl = navel.el.select('span',true).first();
24077         }
24078         
24079         var _this = this;
24080         
24081         if(this.buttons){
24082             Roo.each(_this.buttons, function(e){ // this might need to use render????
24083                Roo.factory(e).onRender(_this.el, null);
24084             });
24085         }
24086             
24087         Roo.each(_this.toolbarItems, function(e) {
24088             _this.navgroup.addItem(e);
24089         });
24090         
24091         
24092         this.first = this.navgroup.addItem({
24093             tooltip: this.firstText,
24094             cls: "prev",
24095             icon : 'fa fa-backward',
24096             disabled: true,
24097             preventDefault: true,
24098             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24099         });
24100         
24101         this.prev =  this.navgroup.addItem({
24102             tooltip: this.prevText,
24103             cls: "prev",
24104             icon : 'fa fa-step-backward',
24105             disabled: true,
24106             preventDefault: true,
24107             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24108         });
24109     //this.addSeparator();
24110         
24111         
24112         var field = this.navgroup.addItem( {
24113             tagtype : 'span',
24114             cls : 'x-paging-position',
24115             
24116             html : this.beforePageText  +
24117                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24118                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24119          } ); //?? escaped?
24120         
24121         this.field = field.el.select('input', true).first();
24122         this.field.on("keydown", this.onPagingKeydown, this);
24123         this.field.on("focus", function(){this.dom.select();});
24124     
24125     
24126         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24127         //this.field.setHeight(18);
24128         //this.addSeparator();
24129         this.next = this.navgroup.addItem({
24130             tooltip: this.nextText,
24131             cls: "next",
24132             html : ' <i class="fa fa-step-forward">',
24133             disabled: true,
24134             preventDefault: true,
24135             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24136         });
24137         this.last = this.navgroup.addItem({
24138             tooltip: this.lastText,
24139             icon : 'fa fa-forward',
24140             cls: "next",
24141             disabled: true,
24142             preventDefault: true,
24143             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24144         });
24145     //this.addSeparator();
24146         this.loading = this.navgroup.addItem({
24147             tooltip: this.refreshText,
24148             icon: 'fa fa-refresh',
24149             preventDefault: true,
24150             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24151         });
24152         
24153     },
24154
24155     // private
24156     updateInfo : function(){
24157         if(this.displayEl){
24158             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24159             var msg = count == 0 ?
24160                 this.emptyMsg :
24161                 String.format(
24162                     this.displayMsg,
24163                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24164                 );
24165             this.displayEl.update(msg);
24166         }
24167     },
24168
24169     // private
24170     onLoad : function(ds, r, o){
24171        this.cursor = o.params ? o.params.start : 0;
24172        var d = this.getPageData(),
24173             ap = d.activePage,
24174             ps = d.pages;
24175         
24176        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24177        this.field.dom.value = ap;
24178        this.first.setDisabled(ap == 1);
24179        this.prev.setDisabled(ap == 1);
24180        this.next.setDisabled(ap == ps);
24181        this.last.setDisabled(ap == ps);
24182        this.loading.enable();
24183        this.updateInfo();
24184     },
24185
24186     // private
24187     getPageData : function(){
24188         var total = this.ds.getTotalCount();
24189         return {
24190             total : total,
24191             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24192             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24193         };
24194     },
24195
24196     // private
24197     onLoadError : function(){
24198         this.loading.enable();
24199     },
24200
24201     // private
24202     onPagingKeydown : function(e){
24203         var k = e.getKey();
24204         var d = this.getPageData();
24205         if(k == e.RETURN){
24206             var v = this.field.dom.value, pageNum;
24207             if(!v || isNaN(pageNum = parseInt(v, 10))){
24208                 this.field.dom.value = d.activePage;
24209                 return;
24210             }
24211             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24212             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24213             e.stopEvent();
24214         }
24215         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))
24216         {
24217           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24218           this.field.dom.value = pageNum;
24219           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24220           e.stopEvent();
24221         }
24222         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24223         {
24224           var v = this.field.dom.value, pageNum; 
24225           var increment = (e.shiftKey) ? 10 : 1;
24226           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24227                 increment *= -1;
24228           }
24229           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24230             this.field.dom.value = d.activePage;
24231             return;
24232           }
24233           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24234           {
24235             this.field.dom.value = parseInt(v, 10) + increment;
24236             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24237             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24238           }
24239           e.stopEvent();
24240         }
24241     },
24242
24243     // private
24244     beforeLoad : function(){
24245         if(this.loading){
24246             this.loading.disable();
24247         }
24248     },
24249
24250     // private
24251     onClick : function(which){
24252         
24253         var ds = this.ds;
24254         if (!ds) {
24255             return;
24256         }
24257         
24258         switch(which){
24259             case "first":
24260                 ds.load({params:{start: 0, limit: this.pageSize}});
24261             break;
24262             case "prev":
24263                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24264             break;
24265             case "next":
24266                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24267             break;
24268             case "last":
24269                 var total = ds.getTotalCount();
24270                 var extra = total % this.pageSize;
24271                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24272                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24273             break;
24274             case "refresh":
24275                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24276             break;
24277         }
24278     },
24279
24280     /**
24281      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24282      * @param {Roo.data.Store} store The data store to unbind
24283      */
24284     unbind : function(ds){
24285         ds.un("beforeload", this.beforeLoad, this);
24286         ds.un("load", this.onLoad, this);
24287         ds.un("loadexception", this.onLoadError, this);
24288         ds.un("remove", this.updateInfo, this);
24289         ds.un("add", this.updateInfo, this);
24290         this.ds = undefined;
24291     },
24292
24293     /**
24294      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24295      * @param {Roo.data.Store} store The data store to bind
24296      */
24297     bind : function(ds){
24298         ds.on("beforeload", this.beforeLoad, this);
24299         ds.on("load", this.onLoad, this);
24300         ds.on("loadexception", this.onLoadError, this);
24301         ds.on("remove", this.updateInfo, this);
24302         ds.on("add", this.updateInfo, this);
24303         this.ds = ds;
24304     }
24305 });/*
24306  * - LGPL
24307  *
24308  * element
24309  * 
24310  */
24311
24312 /**
24313  * @class Roo.bootstrap.MessageBar
24314  * @extends Roo.bootstrap.Component
24315  * Bootstrap MessageBar class
24316  * @cfg {String} html contents of the MessageBar
24317  * @cfg {String} weight (info | success | warning | danger) default info
24318  * @cfg {String} beforeClass insert the bar before the given class
24319  * @cfg {Boolean} closable (true | false) default false
24320  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24321  * 
24322  * @constructor
24323  * Create a new Element
24324  * @param {Object} config The config object
24325  */
24326
24327 Roo.bootstrap.MessageBar = function(config){
24328     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24329 };
24330
24331 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24332     
24333     html: '',
24334     weight: 'info',
24335     closable: false,
24336     fixed: false,
24337     beforeClass: 'bootstrap-sticky-wrap',
24338     
24339     getAutoCreate : function(){
24340         
24341         var cfg = {
24342             tag: 'div',
24343             cls: 'alert alert-dismissable alert-' + this.weight,
24344             cn: [
24345                 {
24346                     tag: 'span',
24347                     cls: 'message',
24348                     html: this.html || ''
24349                 }
24350             ]
24351         };
24352         
24353         if(this.fixed){
24354             cfg.cls += ' alert-messages-fixed';
24355         }
24356         
24357         if(this.closable){
24358             cfg.cn.push({
24359                 tag: 'button',
24360                 cls: 'close',
24361                 html: 'x'
24362             });
24363         }
24364         
24365         return cfg;
24366     },
24367     
24368     onRender : function(ct, position)
24369     {
24370         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24371         
24372         if(!this.el){
24373             var cfg = Roo.apply({},  this.getAutoCreate());
24374             cfg.id = Roo.id();
24375             
24376             if (this.cls) {
24377                 cfg.cls += ' ' + this.cls;
24378             }
24379             if (this.style) {
24380                 cfg.style = this.style;
24381             }
24382             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24383             
24384             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24385         }
24386         
24387         this.el.select('>button.close').on('click', this.hide, this);
24388         
24389     },
24390     
24391     show : function()
24392     {
24393         if (!this.rendered) {
24394             this.render();
24395         }
24396         
24397         this.el.show();
24398         
24399         this.fireEvent('show', this);
24400         
24401     },
24402     
24403     hide : function()
24404     {
24405         if (!this.rendered) {
24406             this.render();
24407         }
24408         
24409         this.el.hide();
24410         
24411         this.fireEvent('hide', this);
24412     },
24413     
24414     update : function()
24415     {
24416 //        var e = this.el.dom.firstChild;
24417 //        
24418 //        if(this.closable){
24419 //            e = e.nextSibling;
24420 //        }
24421 //        
24422 //        e.data = this.html || '';
24423
24424         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24425     }
24426    
24427 });
24428
24429  
24430
24431      /*
24432  * - LGPL
24433  *
24434  * Graph
24435  * 
24436  */
24437
24438
24439 /**
24440  * @class Roo.bootstrap.Graph
24441  * @extends Roo.bootstrap.Component
24442  * Bootstrap Graph class
24443 > Prameters
24444  -sm {number} sm 4
24445  -md {number} md 5
24446  @cfg {String} graphtype  bar | vbar | pie
24447  @cfg {number} g_x coodinator | centre x (pie)
24448  @cfg {number} g_y coodinator | centre y (pie)
24449  @cfg {number} g_r radius (pie)
24450  @cfg {number} g_height height of the chart (respected by all elements in the set)
24451  @cfg {number} g_width width of the chart (respected by all elements in the set)
24452  @cfg {Object} title The title of the chart
24453     
24454  -{Array}  values
24455  -opts (object) options for the chart 
24456      o {
24457      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24458      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24459      o vgutter (number)
24460      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.
24461      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24462      o to
24463      o stretch (boolean)
24464      o }
24465  -opts (object) options for the pie
24466      o{
24467      o cut
24468      o startAngle (number)
24469      o endAngle (number)
24470      } 
24471  *
24472  * @constructor
24473  * Create a new Input
24474  * @param {Object} config The config object
24475  */
24476
24477 Roo.bootstrap.Graph = function(config){
24478     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24479     
24480     this.addEvents({
24481         // img events
24482         /**
24483          * @event click
24484          * The img click event for the img.
24485          * @param {Roo.EventObject} e
24486          */
24487         "click" : true
24488     });
24489 };
24490
24491 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24492     
24493     sm: 4,
24494     md: 5,
24495     graphtype: 'bar',
24496     g_height: 250,
24497     g_width: 400,
24498     g_x: 50,
24499     g_y: 50,
24500     g_r: 30,
24501     opts:{
24502         //g_colors: this.colors,
24503         g_type: 'soft',
24504         g_gutter: '20%'
24505
24506     },
24507     title : false,
24508
24509     getAutoCreate : function(){
24510         
24511         var cfg = {
24512             tag: 'div',
24513             html : null
24514         };
24515         
24516         
24517         return  cfg;
24518     },
24519
24520     onRender : function(ct,position){
24521         
24522         
24523         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24524         
24525         if (typeof(Raphael) == 'undefined') {
24526             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24527             return;
24528         }
24529         
24530         this.raphael = Raphael(this.el.dom);
24531         
24532                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24533                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24534                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24535                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24536                 /*
24537                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24538                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24539                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24540                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24541                 
24542                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24543                 r.barchart(330, 10, 300, 220, data1);
24544                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24545                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24546                 */
24547                 
24548                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24549                 // r.barchart(30, 30, 560, 250,  xdata, {
24550                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24551                 //     axis : "0 0 1 1",
24552                 //     axisxlabels :  xdata
24553                 //     //yvalues : cols,
24554                    
24555                 // });
24556 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24557 //        
24558 //        this.load(null,xdata,{
24559 //                axis : "0 0 1 1",
24560 //                axisxlabels :  xdata
24561 //                });
24562
24563     },
24564
24565     load : function(graphtype,xdata,opts)
24566     {
24567         this.raphael.clear();
24568         if(!graphtype) {
24569             graphtype = this.graphtype;
24570         }
24571         if(!opts){
24572             opts = this.opts;
24573         }
24574         var r = this.raphael,
24575             fin = function () {
24576                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24577             },
24578             fout = function () {
24579                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24580             },
24581             pfin = function() {
24582                 this.sector.stop();
24583                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24584
24585                 if (this.label) {
24586                     this.label[0].stop();
24587                     this.label[0].attr({ r: 7.5 });
24588                     this.label[1].attr({ "font-weight": 800 });
24589                 }
24590             },
24591             pfout = function() {
24592                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
24593
24594                 if (this.label) {
24595                     this.label[0].animate({ r: 5 }, 500, "bounce");
24596                     this.label[1].attr({ "font-weight": 400 });
24597                 }
24598             };
24599
24600         switch(graphtype){
24601             case 'bar':
24602                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24603                 break;
24604             case 'hbar':
24605                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24606                 break;
24607             case 'pie':
24608 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
24609 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
24610 //            
24611                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
24612                 
24613                 break;
24614
24615         }
24616         
24617         if(this.title){
24618             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
24619         }
24620         
24621     },
24622     
24623     setTitle: function(o)
24624     {
24625         this.title = o;
24626     },
24627     
24628     initEvents: function() {
24629         
24630         if(!this.href){
24631             this.el.on('click', this.onClick, this);
24632         }
24633     },
24634     
24635     onClick : function(e)
24636     {
24637         Roo.log('img onclick');
24638         this.fireEvent('click', this, e);
24639     }
24640    
24641 });
24642
24643  
24644 /*
24645  * - LGPL
24646  *
24647  * numberBox
24648  * 
24649  */
24650 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24651
24652 /**
24653  * @class Roo.bootstrap.dash.NumberBox
24654  * @extends Roo.bootstrap.Component
24655  * Bootstrap NumberBox class
24656  * @cfg {String} headline Box headline
24657  * @cfg {String} content Box content
24658  * @cfg {String} icon Box icon
24659  * @cfg {String} footer Footer text
24660  * @cfg {String} fhref Footer href
24661  * 
24662  * @constructor
24663  * Create a new NumberBox
24664  * @param {Object} config The config object
24665  */
24666
24667
24668 Roo.bootstrap.dash.NumberBox = function(config){
24669     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24670     
24671 };
24672
24673 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
24674     
24675     headline : '',
24676     content : '',
24677     icon : '',
24678     footer : '',
24679     fhref : '',
24680     ficon : '',
24681     
24682     getAutoCreate : function(){
24683         
24684         var cfg = {
24685             tag : 'div',
24686             cls : 'small-box ',
24687             cn : [
24688                 {
24689                     tag : 'div',
24690                     cls : 'inner',
24691                     cn :[
24692                         {
24693                             tag : 'h3',
24694                             cls : 'roo-headline',
24695                             html : this.headline
24696                         },
24697                         {
24698                             tag : 'p',
24699                             cls : 'roo-content',
24700                             html : this.content
24701                         }
24702                     ]
24703                 }
24704             ]
24705         };
24706         
24707         if(this.icon){
24708             cfg.cn.push({
24709                 tag : 'div',
24710                 cls : 'icon',
24711                 cn :[
24712                     {
24713                         tag : 'i',
24714                         cls : 'ion ' + this.icon
24715                     }
24716                 ]
24717             });
24718         }
24719         
24720         if(this.footer){
24721             var footer = {
24722                 tag : 'a',
24723                 cls : 'small-box-footer',
24724                 href : this.fhref || '#',
24725                 html : this.footer
24726             };
24727             
24728             cfg.cn.push(footer);
24729             
24730         }
24731         
24732         return  cfg;
24733     },
24734
24735     onRender : function(ct,position){
24736         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24737
24738
24739        
24740                 
24741     },
24742
24743     setHeadline: function (value)
24744     {
24745         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24746     },
24747     
24748     setFooter: function (value, href)
24749     {
24750         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24751         
24752         if(href){
24753             this.el.select('a.small-box-footer',true).first().attr('href', href);
24754         }
24755         
24756     },
24757
24758     setContent: function (value)
24759     {
24760         this.el.select('.roo-content',true).first().dom.innerHTML = value;
24761     },
24762
24763     initEvents: function() 
24764     {   
24765         
24766     }
24767     
24768 });
24769
24770  
24771 /*
24772  * - LGPL
24773  *
24774  * TabBox
24775  * 
24776  */
24777 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24778
24779 /**
24780  * @class Roo.bootstrap.dash.TabBox
24781  * @extends Roo.bootstrap.Component
24782  * Bootstrap TabBox class
24783  * @cfg {String} title Title of the TabBox
24784  * @cfg {String} icon Icon of the TabBox
24785  * @cfg {Boolean} showtabs (true|false) show the tabs default true
24786  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24787  * 
24788  * @constructor
24789  * Create a new TabBox
24790  * @param {Object} config The config object
24791  */
24792
24793
24794 Roo.bootstrap.dash.TabBox = function(config){
24795     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24796     this.addEvents({
24797         // raw events
24798         /**
24799          * @event addpane
24800          * When a pane is added
24801          * @param {Roo.bootstrap.dash.TabPane} pane
24802          */
24803         "addpane" : true,
24804         /**
24805          * @event activatepane
24806          * When a pane is activated
24807          * @param {Roo.bootstrap.dash.TabPane} pane
24808          */
24809         "activatepane" : true
24810         
24811          
24812     });
24813     
24814     this.panes = [];
24815 };
24816
24817 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
24818
24819     title : '',
24820     icon : false,
24821     showtabs : true,
24822     tabScrollable : false,
24823     
24824     getChildContainer : function()
24825     {
24826         return this.el.select('.tab-content', true).first();
24827     },
24828     
24829     getAutoCreate : function(){
24830         
24831         var header = {
24832             tag: 'li',
24833             cls: 'pull-left header',
24834             html: this.title,
24835             cn : []
24836         };
24837         
24838         if(this.icon){
24839             header.cn.push({
24840                 tag: 'i',
24841                 cls: 'fa ' + this.icon
24842             });
24843         }
24844         
24845         var h = {
24846             tag: 'ul',
24847             cls: 'nav nav-tabs pull-right',
24848             cn: [
24849                 header
24850             ]
24851         };
24852         
24853         if(this.tabScrollable){
24854             h = {
24855                 tag: 'div',
24856                 cls: 'tab-header',
24857                 cn: [
24858                     {
24859                         tag: 'ul',
24860                         cls: 'nav nav-tabs pull-right',
24861                         cn: [
24862                             header
24863                         ]
24864                     }
24865                 ]
24866             };
24867         }
24868         
24869         var cfg = {
24870             tag: 'div',
24871             cls: 'nav-tabs-custom',
24872             cn: [
24873                 h,
24874                 {
24875                     tag: 'div',
24876                     cls: 'tab-content no-padding',
24877                     cn: []
24878                 }
24879             ]
24880         };
24881
24882         return  cfg;
24883     },
24884     initEvents : function()
24885     {
24886         //Roo.log('add add pane handler');
24887         this.on('addpane', this.onAddPane, this);
24888     },
24889      /**
24890      * Updates the box title
24891      * @param {String} html to set the title to.
24892      */
24893     setTitle : function(value)
24894     {
24895         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
24896     },
24897     onAddPane : function(pane)
24898     {
24899         this.panes.push(pane);
24900         //Roo.log('addpane');
24901         //Roo.log(pane);
24902         // tabs are rendere left to right..
24903         if(!this.showtabs){
24904             return;
24905         }
24906         
24907         var ctr = this.el.select('.nav-tabs', true).first();
24908          
24909          
24910         var existing = ctr.select('.nav-tab',true);
24911         var qty = existing.getCount();;
24912         
24913         
24914         var tab = ctr.createChild({
24915             tag : 'li',
24916             cls : 'nav-tab' + (qty ? '' : ' active'),
24917             cn : [
24918                 {
24919                     tag : 'a',
24920                     href:'#',
24921                     html : pane.title
24922                 }
24923             ]
24924         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
24925         pane.tab = tab;
24926         
24927         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
24928         if (!qty) {
24929             pane.el.addClass('active');
24930         }
24931         
24932                 
24933     },
24934     onTabClick : function(ev,un,ob,pane)
24935     {
24936         //Roo.log('tab - prev default');
24937         ev.preventDefault();
24938         
24939         
24940         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
24941         pane.tab.addClass('active');
24942         //Roo.log(pane.title);
24943         this.getChildContainer().select('.tab-pane',true).removeClass('active');
24944         // technically we should have a deactivate event.. but maybe add later.
24945         // and it should not de-activate the selected tab...
24946         this.fireEvent('activatepane', pane);
24947         pane.el.addClass('active');
24948         pane.fireEvent('activate');
24949         
24950         
24951     },
24952     
24953     getActivePane : function()
24954     {
24955         var r = false;
24956         Roo.each(this.panes, function(p) {
24957             if(p.el.hasClass('active')){
24958                 r = p;
24959                 return false;
24960             }
24961             
24962             return;
24963         });
24964         
24965         return r;
24966     }
24967     
24968     
24969 });
24970
24971  
24972 /*
24973  * - LGPL
24974  *
24975  * Tab pane
24976  * 
24977  */
24978 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24979 /**
24980  * @class Roo.bootstrap.TabPane
24981  * @extends Roo.bootstrap.Component
24982  * Bootstrap TabPane class
24983  * @cfg {Boolean} active (false | true) Default false
24984  * @cfg {String} title title of panel
24985
24986  * 
24987  * @constructor
24988  * Create a new TabPane
24989  * @param {Object} config The config object
24990  */
24991
24992 Roo.bootstrap.dash.TabPane = function(config){
24993     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
24994     
24995     this.addEvents({
24996         // raw events
24997         /**
24998          * @event activate
24999          * When a pane is activated
25000          * @param {Roo.bootstrap.dash.TabPane} pane
25001          */
25002         "activate" : true
25003          
25004     });
25005 };
25006
25007 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25008     
25009     active : false,
25010     title : '',
25011     
25012     // the tabBox that this is attached to.
25013     tab : false,
25014      
25015     getAutoCreate : function() 
25016     {
25017         var cfg = {
25018             tag: 'div',
25019             cls: 'tab-pane'
25020         };
25021         
25022         if(this.active){
25023             cfg.cls += ' active';
25024         }
25025         
25026         return cfg;
25027     },
25028     initEvents  : function()
25029     {
25030         //Roo.log('trigger add pane handler');
25031         this.parent().fireEvent('addpane', this)
25032     },
25033     
25034      /**
25035      * Updates the tab title 
25036      * @param {String} html to set the title to.
25037      */
25038     setTitle: function(str)
25039     {
25040         if (!this.tab) {
25041             return;
25042         }
25043         this.title = str;
25044         this.tab.select('a', true).first().dom.innerHTML = str;
25045         
25046     }
25047     
25048     
25049     
25050 });
25051
25052  
25053
25054
25055  /*
25056  * - LGPL
25057  *
25058  * menu
25059  * 
25060  */
25061 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25062
25063 /**
25064  * @class Roo.bootstrap.menu.Menu
25065  * @extends Roo.bootstrap.Component
25066  * Bootstrap Menu class - container for Menu
25067  * @cfg {String} html Text of the menu
25068  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25069  * @cfg {String} icon Font awesome icon
25070  * @cfg {String} pos Menu align to (top | bottom) default bottom
25071  * 
25072  * 
25073  * @constructor
25074  * Create a new Menu
25075  * @param {Object} config The config object
25076  */
25077
25078
25079 Roo.bootstrap.menu.Menu = function(config){
25080     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25081     
25082     this.addEvents({
25083         /**
25084          * @event beforeshow
25085          * Fires before this menu is displayed
25086          * @param {Roo.bootstrap.menu.Menu} this
25087          */
25088         beforeshow : true,
25089         /**
25090          * @event beforehide
25091          * Fires before this menu is hidden
25092          * @param {Roo.bootstrap.menu.Menu} this
25093          */
25094         beforehide : true,
25095         /**
25096          * @event show
25097          * Fires after this menu is displayed
25098          * @param {Roo.bootstrap.menu.Menu} this
25099          */
25100         show : true,
25101         /**
25102          * @event hide
25103          * Fires after this menu is hidden
25104          * @param {Roo.bootstrap.menu.Menu} this
25105          */
25106         hide : true,
25107         /**
25108          * @event click
25109          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25110          * @param {Roo.bootstrap.menu.Menu} this
25111          * @param {Roo.EventObject} e
25112          */
25113         click : true
25114     });
25115     
25116 };
25117
25118 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25119     
25120     submenu : false,
25121     html : '',
25122     weight : 'default',
25123     icon : false,
25124     pos : 'bottom',
25125     
25126     
25127     getChildContainer : function() {
25128         if(this.isSubMenu){
25129             return this.el;
25130         }
25131         
25132         return this.el.select('ul.dropdown-menu', true).first();  
25133     },
25134     
25135     getAutoCreate : function()
25136     {
25137         var text = [
25138             {
25139                 tag : 'span',
25140                 cls : 'roo-menu-text',
25141                 html : this.html
25142             }
25143         ];
25144         
25145         if(this.icon){
25146             text.unshift({
25147                 tag : 'i',
25148                 cls : 'fa ' + this.icon
25149             })
25150         }
25151         
25152         
25153         var cfg = {
25154             tag : 'div',
25155             cls : 'btn-group',
25156             cn : [
25157                 {
25158                     tag : 'button',
25159                     cls : 'dropdown-button btn btn-' + this.weight,
25160                     cn : text
25161                 },
25162                 {
25163                     tag : 'button',
25164                     cls : 'dropdown-toggle btn btn-' + this.weight,
25165                     cn : [
25166                         {
25167                             tag : 'span',
25168                             cls : 'caret'
25169                         }
25170                     ]
25171                 },
25172                 {
25173                     tag : 'ul',
25174                     cls : 'dropdown-menu'
25175                 }
25176             ]
25177             
25178         };
25179         
25180         if(this.pos == 'top'){
25181             cfg.cls += ' dropup';
25182         }
25183         
25184         if(this.isSubMenu){
25185             cfg = {
25186                 tag : 'ul',
25187                 cls : 'dropdown-menu'
25188             }
25189         }
25190         
25191         return cfg;
25192     },
25193     
25194     onRender : function(ct, position)
25195     {
25196         this.isSubMenu = ct.hasClass('dropdown-submenu');
25197         
25198         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25199     },
25200     
25201     initEvents : function() 
25202     {
25203         if(this.isSubMenu){
25204             return;
25205         }
25206         
25207         this.hidden = true;
25208         
25209         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25210         this.triggerEl.on('click', this.onTriggerPress, this);
25211         
25212         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25213         this.buttonEl.on('click', this.onClick, this);
25214         
25215     },
25216     
25217     list : function()
25218     {
25219         if(this.isSubMenu){
25220             return this.el;
25221         }
25222         
25223         return this.el.select('ul.dropdown-menu', true).first();
25224     },
25225     
25226     onClick : function(e)
25227     {
25228         this.fireEvent("click", this, e);
25229     },
25230     
25231     onTriggerPress  : function(e)
25232     {   
25233         if (this.isVisible()) {
25234             this.hide();
25235         } else {
25236             this.show();
25237         }
25238     },
25239     
25240     isVisible : function(){
25241         return !this.hidden;
25242     },
25243     
25244     show : function()
25245     {
25246         this.fireEvent("beforeshow", this);
25247         
25248         this.hidden = false;
25249         this.el.addClass('open');
25250         
25251         Roo.get(document).on("mouseup", this.onMouseUp, this);
25252         
25253         this.fireEvent("show", this);
25254         
25255         
25256     },
25257     
25258     hide : function()
25259     {
25260         this.fireEvent("beforehide", this);
25261         
25262         this.hidden = true;
25263         this.el.removeClass('open');
25264         
25265         Roo.get(document).un("mouseup", this.onMouseUp);
25266         
25267         this.fireEvent("hide", this);
25268     },
25269     
25270     onMouseUp : function()
25271     {
25272         this.hide();
25273     }
25274     
25275 });
25276
25277  
25278  /*
25279  * - LGPL
25280  *
25281  * menu item
25282  * 
25283  */
25284 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25285
25286 /**
25287  * @class Roo.bootstrap.menu.Item
25288  * @extends Roo.bootstrap.Component
25289  * Bootstrap MenuItem class
25290  * @cfg {Boolean} submenu (true | false) default false
25291  * @cfg {String} html text of the item
25292  * @cfg {String} href the link
25293  * @cfg {Boolean} disable (true | false) default false
25294  * @cfg {Boolean} preventDefault (true | false) default true
25295  * @cfg {String} icon Font awesome icon
25296  * @cfg {String} pos Submenu align to (left | right) default right 
25297  * 
25298  * 
25299  * @constructor
25300  * Create a new Item
25301  * @param {Object} config The config object
25302  */
25303
25304
25305 Roo.bootstrap.menu.Item = function(config){
25306     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25307     this.addEvents({
25308         /**
25309          * @event mouseover
25310          * Fires when the mouse is hovering over this menu
25311          * @param {Roo.bootstrap.menu.Item} this
25312          * @param {Roo.EventObject} e
25313          */
25314         mouseover : true,
25315         /**
25316          * @event mouseout
25317          * Fires when the mouse exits this menu
25318          * @param {Roo.bootstrap.menu.Item} this
25319          * @param {Roo.EventObject} e
25320          */
25321         mouseout : true,
25322         // raw events
25323         /**
25324          * @event click
25325          * The raw click event for the entire grid.
25326          * @param {Roo.EventObject} e
25327          */
25328         click : true
25329     });
25330 };
25331
25332 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25333     
25334     submenu : false,
25335     href : '',
25336     html : '',
25337     preventDefault: true,
25338     disable : false,
25339     icon : false,
25340     pos : 'right',
25341     
25342     getAutoCreate : function()
25343     {
25344         var text = [
25345             {
25346                 tag : 'span',
25347                 cls : 'roo-menu-item-text',
25348                 html : this.html
25349             }
25350         ];
25351         
25352         if(this.icon){
25353             text.unshift({
25354                 tag : 'i',
25355                 cls : 'fa ' + this.icon
25356             })
25357         }
25358         
25359         var cfg = {
25360             tag : 'li',
25361             cn : [
25362                 {
25363                     tag : 'a',
25364                     href : this.href || '#',
25365                     cn : text
25366                 }
25367             ]
25368         };
25369         
25370         if(this.disable){
25371             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25372         }
25373         
25374         if(this.submenu){
25375             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25376             
25377             if(this.pos == 'left'){
25378                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25379             }
25380         }
25381         
25382         return cfg;
25383     },
25384     
25385     initEvents : function() 
25386     {
25387         this.el.on('mouseover', this.onMouseOver, this);
25388         this.el.on('mouseout', this.onMouseOut, this);
25389         
25390         this.el.select('a', true).first().on('click', this.onClick, this);
25391         
25392     },
25393     
25394     onClick : function(e)
25395     {
25396         if(this.preventDefault){
25397             e.preventDefault();
25398         }
25399         
25400         this.fireEvent("click", this, e);
25401     },
25402     
25403     onMouseOver : function(e)
25404     {
25405         if(this.submenu && this.pos == 'left'){
25406             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25407         }
25408         
25409         this.fireEvent("mouseover", this, e);
25410     },
25411     
25412     onMouseOut : function(e)
25413     {
25414         this.fireEvent("mouseout", this, e);
25415     }
25416 });
25417
25418  
25419
25420  /*
25421  * - LGPL
25422  *
25423  * menu separator
25424  * 
25425  */
25426 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25427
25428 /**
25429  * @class Roo.bootstrap.menu.Separator
25430  * @extends Roo.bootstrap.Component
25431  * Bootstrap Separator class
25432  * 
25433  * @constructor
25434  * Create a new Separator
25435  * @param {Object} config The config object
25436  */
25437
25438
25439 Roo.bootstrap.menu.Separator = function(config){
25440     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25441 };
25442
25443 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25444     
25445     getAutoCreate : function(){
25446         var cfg = {
25447             tag : 'li',
25448             cls: 'divider'
25449         };
25450         
25451         return cfg;
25452     }
25453    
25454 });
25455
25456  
25457
25458  /*
25459  * - LGPL
25460  *
25461  * Tooltip
25462  * 
25463  */
25464
25465 /**
25466  * @class Roo.bootstrap.Tooltip
25467  * Bootstrap Tooltip class
25468  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25469  * to determine which dom element triggers the tooltip.
25470  * 
25471  * It needs to add support for additional attributes like tooltip-position
25472  * 
25473  * @constructor
25474  * Create a new Toolti
25475  * @param {Object} config The config object
25476  */
25477
25478 Roo.bootstrap.Tooltip = function(config){
25479     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25480     
25481     this.alignment = Roo.bootstrap.Tooltip.alignment;
25482     
25483     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25484         this.alignment = config.alignment;
25485     }
25486     
25487 };
25488
25489 Roo.apply(Roo.bootstrap.Tooltip, {
25490     /**
25491      * @function init initialize tooltip monitoring.
25492      * @static
25493      */
25494     currentEl : false,
25495     currentTip : false,
25496     currentRegion : false,
25497     
25498     //  init : delay?
25499     
25500     init : function()
25501     {
25502         Roo.get(document).on('mouseover', this.enter ,this);
25503         Roo.get(document).on('mouseout', this.leave, this);
25504          
25505         
25506         this.currentTip = new Roo.bootstrap.Tooltip();
25507     },
25508     
25509     enter : function(ev)
25510     {
25511         var dom = ev.getTarget();
25512         
25513         //Roo.log(['enter',dom]);
25514         var el = Roo.fly(dom);
25515         if (this.currentEl) {
25516             //Roo.log(dom);
25517             //Roo.log(this.currentEl);
25518             //Roo.log(this.currentEl.contains(dom));
25519             if (this.currentEl == el) {
25520                 return;
25521             }
25522             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25523                 return;
25524             }
25525
25526         }
25527         
25528         if (this.currentTip.el) {
25529             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25530         }    
25531         //Roo.log(ev);
25532         
25533         if(!el || el.dom == document){
25534             return;
25535         }
25536         
25537         var bindEl = el;
25538         
25539         // you can not look for children, as if el is the body.. then everythign is the child..
25540         if (!el.attr('tooltip')) { //
25541             if (!el.select("[tooltip]").elements.length) {
25542                 return;
25543             }
25544             // is the mouse over this child...?
25545             bindEl = el.select("[tooltip]").first();
25546             var xy = ev.getXY();
25547             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25548                 //Roo.log("not in region.");
25549                 return;
25550             }
25551             //Roo.log("child element over..");
25552             
25553         }
25554         this.currentEl = bindEl;
25555         this.currentTip.bind(bindEl);
25556         this.currentRegion = Roo.lib.Region.getRegion(dom);
25557         this.currentTip.enter();
25558         
25559     },
25560     leave : function(ev)
25561     {
25562         var dom = ev.getTarget();
25563         //Roo.log(['leave',dom]);
25564         if (!this.currentEl) {
25565             return;
25566         }
25567         
25568         
25569         if (dom != this.currentEl.dom) {
25570             return;
25571         }
25572         var xy = ev.getXY();
25573         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
25574             return;
25575         }
25576         // only activate leave if mouse cursor is outside... bounding box..
25577         
25578         
25579         
25580         
25581         if (this.currentTip) {
25582             this.currentTip.leave();
25583         }
25584         //Roo.log('clear currentEl');
25585         this.currentEl = false;
25586         
25587         
25588     },
25589     alignment : {
25590         'left' : ['r-l', [-2,0], 'right'],
25591         'right' : ['l-r', [2,0], 'left'],
25592         'bottom' : ['t-b', [0,2], 'top'],
25593         'top' : [ 'b-t', [0,-2], 'bottom']
25594     }
25595     
25596 });
25597
25598
25599 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
25600     
25601     
25602     bindEl : false,
25603     
25604     delay : null, // can be { show : 300 , hide: 500}
25605     
25606     timeout : null,
25607     
25608     hoverState : null, //???
25609     
25610     placement : 'bottom', 
25611     
25612     alignment : false,
25613     
25614     getAutoCreate : function(){
25615     
25616         var cfg = {
25617            cls : 'tooltip',
25618            role : 'tooltip',
25619            cn : [
25620                 {
25621                     cls : 'tooltip-arrow'
25622                 },
25623                 {
25624                     cls : 'tooltip-inner'
25625                 }
25626            ]
25627         };
25628         
25629         return cfg;
25630     },
25631     bind : function(el)
25632     {
25633         this.bindEl = el;
25634     },
25635       
25636     
25637     enter : function () {
25638        
25639         if (this.timeout != null) {
25640             clearTimeout(this.timeout);
25641         }
25642         
25643         this.hoverState = 'in';
25644          //Roo.log("enter - show");
25645         if (!this.delay || !this.delay.show) {
25646             this.show();
25647             return;
25648         }
25649         var _t = this;
25650         this.timeout = setTimeout(function () {
25651             if (_t.hoverState == 'in') {
25652                 _t.show();
25653             }
25654         }, this.delay.show);
25655     },
25656     leave : function()
25657     {
25658         clearTimeout(this.timeout);
25659     
25660         this.hoverState = 'out';
25661          if (!this.delay || !this.delay.hide) {
25662             this.hide();
25663             return;
25664         }
25665        
25666         var _t = this;
25667         this.timeout = setTimeout(function () {
25668             //Roo.log("leave - timeout");
25669             
25670             if (_t.hoverState == 'out') {
25671                 _t.hide();
25672                 Roo.bootstrap.Tooltip.currentEl = false;
25673             }
25674         }, delay);
25675     },
25676     
25677     show : function (msg)
25678     {
25679         if (!this.el) {
25680             this.render(document.body);
25681         }
25682         // set content.
25683         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25684         
25685         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25686         
25687         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25688         
25689         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25690         
25691         var placement = typeof this.placement == 'function' ?
25692             this.placement.call(this, this.el, on_el) :
25693             this.placement;
25694             
25695         var autoToken = /\s?auto?\s?/i;
25696         var autoPlace = autoToken.test(placement);
25697         if (autoPlace) {
25698             placement = placement.replace(autoToken, '') || 'top';
25699         }
25700         
25701         //this.el.detach()
25702         //this.el.setXY([0,0]);
25703         this.el.show();
25704         //this.el.dom.style.display='block';
25705         
25706         //this.el.appendTo(on_el);
25707         
25708         var p = this.getPosition();
25709         var box = this.el.getBox();
25710         
25711         if (autoPlace) {
25712             // fixme..
25713         }
25714         
25715         var align = this.alignment[placement];
25716         
25717         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25718         
25719         if(placement == 'top' || placement == 'bottom'){
25720             if(xy[0] < 0){
25721                 placement = 'right';
25722             }
25723             
25724             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25725                 placement = 'left';
25726             }
25727             
25728             var scroll = Roo.select('body', true).first().getScroll();
25729             
25730             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25731                 placement = 'top';
25732             }
25733             
25734         }
25735         
25736         this.el.alignTo(this.bindEl, align[0],align[1]);
25737         //var arrow = this.el.select('.arrow',true).first();
25738         //arrow.set(align[2], 
25739         
25740         this.el.addClass(placement);
25741         
25742         this.el.addClass('in fade');
25743         
25744         this.hoverState = null;
25745         
25746         if (this.el.hasClass('fade')) {
25747             // fade it?
25748         }
25749         
25750     },
25751     hide : function()
25752     {
25753          
25754         if (!this.el) {
25755             return;
25756         }
25757         //this.el.setXY([0,0]);
25758         this.el.removeClass('in');
25759         //this.el.hide();
25760         
25761     }
25762     
25763 });
25764  
25765
25766  /*
25767  * - LGPL
25768  *
25769  * Location Picker
25770  * 
25771  */
25772
25773 /**
25774  * @class Roo.bootstrap.LocationPicker
25775  * @extends Roo.bootstrap.Component
25776  * Bootstrap LocationPicker class
25777  * @cfg {Number} latitude Position when init default 0
25778  * @cfg {Number} longitude Position when init default 0
25779  * @cfg {Number} zoom default 15
25780  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25781  * @cfg {Boolean} mapTypeControl default false
25782  * @cfg {Boolean} disableDoubleClickZoom default false
25783  * @cfg {Boolean} scrollwheel default true
25784  * @cfg {Boolean} streetViewControl default false
25785  * @cfg {Number} radius default 0
25786  * @cfg {String} locationName
25787  * @cfg {Boolean} draggable default true
25788  * @cfg {Boolean} enableAutocomplete default false
25789  * @cfg {Boolean} enableReverseGeocode default true
25790  * @cfg {String} markerTitle
25791  * 
25792  * @constructor
25793  * Create a new LocationPicker
25794  * @param {Object} config The config object
25795  */
25796
25797
25798 Roo.bootstrap.LocationPicker = function(config){
25799     
25800     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25801     
25802     this.addEvents({
25803         /**
25804          * @event initial
25805          * Fires when the picker initialized.
25806          * @param {Roo.bootstrap.LocationPicker} this
25807          * @param {Google Location} location
25808          */
25809         initial : true,
25810         /**
25811          * @event positionchanged
25812          * Fires when the picker position changed.
25813          * @param {Roo.bootstrap.LocationPicker} this
25814          * @param {Google Location} location
25815          */
25816         positionchanged : true,
25817         /**
25818          * @event resize
25819          * Fires when the map resize.
25820          * @param {Roo.bootstrap.LocationPicker} this
25821          */
25822         resize : true,
25823         /**
25824          * @event show
25825          * Fires when the map show.
25826          * @param {Roo.bootstrap.LocationPicker} this
25827          */
25828         show : true,
25829         /**
25830          * @event hide
25831          * Fires when the map hide.
25832          * @param {Roo.bootstrap.LocationPicker} this
25833          */
25834         hide : true,
25835         /**
25836          * @event mapClick
25837          * Fires when click the map.
25838          * @param {Roo.bootstrap.LocationPicker} this
25839          * @param {Map event} e
25840          */
25841         mapClick : true,
25842         /**
25843          * @event mapRightClick
25844          * Fires when right click the map.
25845          * @param {Roo.bootstrap.LocationPicker} this
25846          * @param {Map event} e
25847          */
25848         mapRightClick : true,
25849         /**
25850          * @event markerClick
25851          * Fires when click the marker.
25852          * @param {Roo.bootstrap.LocationPicker} this
25853          * @param {Map event} e
25854          */
25855         markerClick : true,
25856         /**
25857          * @event markerRightClick
25858          * Fires when right click the marker.
25859          * @param {Roo.bootstrap.LocationPicker} this
25860          * @param {Map event} e
25861          */
25862         markerRightClick : true,
25863         /**
25864          * @event OverlayViewDraw
25865          * Fires when OverlayView Draw
25866          * @param {Roo.bootstrap.LocationPicker} this
25867          */
25868         OverlayViewDraw : true,
25869         /**
25870          * @event OverlayViewOnAdd
25871          * Fires when OverlayView Draw
25872          * @param {Roo.bootstrap.LocationPicker} this
25873          */
25874         OverlayViewOnAdd : true,
25875         /**
25876          * @event OverlayViewOnRemove
25877          * Fires when OverlayView Draw
25878          * @param {Roo.bootstrap.LocationPicker} this
25879          */
25880         OverlayViewOnRemove : true,
25881         /**
25882          * @event OverlayViewShow
25883          * Fires when OverlayView Draw
25884          * @param {Roo.bootstrap.LocationPicker} this
25885          * @param {Pixel} cpx
25886          */
25887         OverlayViewShow : true,
25888         /**
25889          * @event OverlayViewHide
25890          * Fires when OverlayView Draw
25891          * @param {Roo.bootstrap.LocationPicker} this
25892          */
25893         OverlayViewHide : true,
25894         /**
25895          * @event loadexception
25896          * Fires when load google lib failed.
25897          * @param {Roo.bootstrap.LocationPicker} this
25898          */
25899         loadexception : true
25900     });
25901         
25902 };
25903
25904 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
25905     
25906     gMapContext: false,
25907     
25908     latitude: 0,
25909     longitude: 0,
25910     zoom: 15,
25911     mapTypeId: false,
25912     mapTypeControl: false,
25913     disableDoubleClickZoom: false,
25914     scrollwheel: true,
25915     streetViewControl: false,
25916     radius: 0,
25917     locationName: '',
25918     draggable: true,
25919     enableAutocomplete: false,
25920     enableReverseGeocode: true,
25921     markerTitle: '',
25922     
25923     getAutoCreate: function()
25924     {
25925
25926         var cfg = {
25927             tag: 'div',
25928             cls: 'roo-location-picker'
25929         };
25930         
25931         return cfg
25932     },
25933     
25934     initEvents: function(ct, position)
25935     {       
25936         if(!this.el.getWidth() || this.isApplied()){
25937             return;
25938         }
25939         
25940         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25941         
25942         this.initial();
25943     },
25944     
25945     initial: function()
25946     {
25947         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
25948             this.fireEvent('loadexception', this);
25949             return;
25950         }
25951         
25952         if(!this.mapTypeId){
25953             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
25954         }
25955         
25956         this.gMapContext = this.GMapContext();
25957         
25958         this.initOverlayView();
25959         
25960         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
25961         
25962         var _this = this;
25963                 
25964         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
25965             _this.setPosition(_this.gMapContext.marker.position);
25966         });
25967         
25968         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
25969             _this.fireEvent('mapClick', this, event);
25970             
25971         });
25972
25973         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
25974             _this.fireEvent('mapRightClick', this, event);
25975             
25976         });
25977         
25978         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
25979             _this.fireEvent('markerClick', this, event);
25980             
25981         });
25982
25983         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
25984             _this.fireEvent('markerRightClick', this, event);
25985             
25986         });
25987         
25988         this.setPosition(this.gMapContext.location);
25989         
25990         this.fireEvent('initial', this, this.gMapContext.location);
25991     },
25992     
25993     initOverlayView: function()
25994     {
25995         var _this = this;
25996         
25997         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
25998             
25999             draw: function()
26000             {
26001                 _this.fireEvent('OverlayViewDraw', _this);
26002             },
26003             
26004             onAdd: function()
26005             {
26006                 _this.fireEvent('OverlayViewOnAdd', _this);
26007             },
26008             
26009             onRemove: function()
26010             {
26011                 _this.fireEvent('OverlayViewOnRemove', _this);
26012             },
26013             
26014             show: function(cpx)
26015             {
26016                 _this.fireEvent('OverlayViewShow', _this, cpx);
26017             },
26018             
26019             hide: function()
26020             {
26021                 _this.fireEvent('OverlayViewHide', _this);
26022             }
26023             
26024         });
26025     },
26026     
26027     fromLatLngToContainerPixel: function(event)
26028     {
26029         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26030     },
26031     
26032     isApplied: function() 
26033     {
26034         return this.getGmapContext() == false ? false : true;
26035     },
26036     
26037     getGmapContext: function() 
26038     {
26039         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26040     },
26041     
26042     GMapContext: function() 
26043     {
26044         var position = new google.maps.LatLng(this.latitude, this.longitude);
26045         
26046         var _map = new google.maps.Map(this.el.dom, {
26047             center: position,
26048             zoom: this.zoom,
26049             mapTypeId: this.mapTypeId,
26050             mapTypeControl: this.mapTypeControl,
26051             disableDoubleClickZoom: this.disableDoubleClickZoom,
26052             scrollwheel: this.scrollwheel,
26053             streetViewControl: this.streetViewControl,
26054             locationName: this.locationName,
26055             draggable: this.draggable,
26056             enableAutocomplete: this.enableAutocomplete,
26057             enableReverseGeocode: this.enableReverseGeocode
26058         });
26059         
26060         var _marker = new google.maps.Marker({
26061             position: position,
26062             map: _map,
26063             title: this.markerTitle,
26064             draggable: this.draggable
26065         });
26066         
26067         return {
26068             map: _map,
26069             marker: _marker,
26070             circle: null,
26071             location: position,
26072             radius: this.radius,
26073             locationName: this.locationName,
26074             addressComponents: {
26075                 formatted_address: null,
26076                 addressLine1: null,
26077                 addressLine2: null,
26078                 streetName: null,
26079                 streetNumber: null,
26080                 city: null,
26081                 district: null,
26082                 state: null,
26083                 stateOrProvince: null
26084             },
26085             settings: this,
26086             domContainer: this.el.dom,
26087             geodecoder: new google.maps.Geocoder()
26088         };
26089     },
26090     
26091     drawCircle: function(center, radius, options) 
26092     {
26093         if (this.gMapContext.circle != null) {
26094             this.gMapContext.circle.setMap(null);
26095         }
26096         if (radius > 0) {
26097             radius *= 1;
26098             options = Roo.apply({}, options, {
26099                 strokeColor: "#0000FF",
26100                 strokeOpacity: .35,
26101                 strokeWeight: 2,
26102                 fillColor: "#0000FF",
26103                 fillOpacity: .2
26104             });
26105             
26106             options.map = this.gMapContext.map;
26107             options.radius = radius;
26108             options.center = center;
26109             this.gMapContext.circle = new google.maps.Circle(options);
26110             return this.gMapContext.circle;
26111         }
26112         
26113         return null;
26114     },
26115     
26116     setPosition: function(location) 
26117     {
26118         this.gMapContext.location = location;
26119         this.gMapContext.marker.setPosition(location);
26120         this.gMapContext.map.panTo(location);
26121         this.drawCircle(location, this.gMapContext.radius, {});
26122         
26123         var _this = this;
26124         
26125         if (this.gMapContext.settings.enableReverseGeocode) {
26126             this.gMapContext.geodecoder.geocode({
26127                 latLng: this.gMapContext.location
26128             }, function(results, status) {
26129                 
26130                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26131                     _this.gMapContext.locationName = results[0].formatted_address;
26132                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26133                     
26134                     _this.fireEvent('positionchanged', this, location);
26135                 }
26136             });
26137             
26138             return;
26139         }
26140         
26141         this.fireEvent('positionchanged', this, location);
26142     },
26143     
26144     resize: function()
26145     {
26146         google.maps.event.trigger(this.gMapContext.map, "resize");
26147         
26148         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26149         
26150         this.fireEvent('resize', this);
26151     },
26152     
26153     setPositionByLatLng: function(latitude, longitude)
26154     {
26155         this.setPosition(new google.maps.LatLng(latitude, longitude));
26156     },
26157     
26158     getCurrentPosition: function() 
26159     {
26160         return {
26161             latitude: this.gMapContext.location.lat(),
26162             longitude: this.gMapContext.location.lng()
26163         };
26164     },
26165     
26166     getAddressName: function() 
26167     {
26168         return this.gMapContext.locationName;
26169     },
26170     
26171     getAddressComponents: function() 
26172     {
26173         return this.gMapContext.addressComponents;
26174     },
26175     
26176     address_component_from_google_geocode: function(address_components) 
26177     {
26178         var result = {};
26179         
26180         for (var i = 0; i < address_components.length; i++) {
26181             var component = address_components[i];
26182             if (component.types.indexOf("postal_code") >= 0) {
26183                 result.postalCode = component.short_name;
26184             } else if (component.types.indexOf("street_number") >= 0) {
26185                 result.streetNumber = component.short_name;
26186             } else if (component.types.indexOf("route") >= 0) {
26187                 result.streetName = component.short_name;
26188             } else if (component.types.indexOf("neighborhood") >= 0) {
26189                 result.city = component.short_name;
26190             } else if (component.types.indexOf("locality") >= 0) {
26191                 result.city = component.short_name;
26192             } else if (component.types.indexOf("sublocality") >= 0) {
26193                 result.district = component.short_name;
26194             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26195                 result.stateOrProvince = component.short_name;
26196             } else if (component.types.indexOf("country") >= 0) {
26197                 result.country = component.short_name;
26198             }
26199         }
26200         
26201         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26202         result.addressLine2 = "";
26203         return result;
26204     },
26205     
26206     setZoomLevel: function(zoom)
26207     {
26208         this.gMapContext.map.setZoom(zoom);
26209     },
26210     
26211     show: function()
26212     {
26213         if(!this.el){
26214             return;
26215         }
26216         
26217         this.el.show();
26218         
26219         this.resize();
26220         
26221         this.fireEvent('show', this);
26222     },
26223     
26224     hide: function()
26225     {
26226         if(!this.el){
26227             return;
26228         }
26229         
26230         this.el.hide();
26231         
26232         this.fireEvent('hide', this);
26233     }
26234     
26235 });
26236
26237 Roo.apply(Roo.bootstrap.LocationPicker, {
26238     
26239     OverlayView : function(map, options)
26240     {
26241         options = options || {};
26242         
26243         this.setMap(map);
26244     }
26245     
26246     
26247 });/*
26248  * - LGPL
26249  *
26250  * Alert
26251  * 
26252  */
26253
26254 /**
26255  * @class Roo.bootstrap.Alert
26256  * @extends Roo.bootstrap.Component
26257  * Bootstrap Alert class
26258  * @cfg {String} title The title of alert
26259  * @cfg {String} html The content of alert
26260  * @cfg {String} weight (  success | info | warning | danger )
26261  * @cfg {String} faicon font-awesomeicon
26262  * 
26263  * @constructor
26264  * Create a new alert
26265  * @param {Object} config The config object
26266  */
26267
26268
26269 Roo.bootstrap.Alert = function(config){
26270     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26271     
26272 };
26273
26274 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26275     
26276     title: '',
26277     html: '',
26278     weight: false,
26279     faicon: false,
26280     
26281     getAutoCreate : function()
26282     {
26283         
26284         var cfg = {
26285             tag : 'div',
26286             cls : 'alert',
26287             cn : [
26288                 {
26289                     tag : 'i',
26290                     cls : 'roo-alert-icon'
26291                     
26292                 },
26293                 {
26294                     tag : 'b',
26295                     cls : 'roo-alert-title',
26296                     html : this.title
26297                 },
26298                 {
26299                     tag : 'span',
26300                     cls : 'roo-alert-text',
26301                     html : this.html
26302                 }
26303             ]
26304         };
26305         
26306         if(this.faicon){
26307             cfg.cn[0].cls += ' fa ' + this.faicon;
26308         }
26309         
26310         if(this.weight){
26311             cfg.cls += ' alert-' + this.weight;
26312         }
26313         
26314         return cfg;
26315     },
26316     
26317     initEvents: function() 
26318     {
26319         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26320     },
26321     
26322     setTitle : function(str)
26323     {
26324         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26325     },
26326     
26327     setText : function(str)
26328     {
26329         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26330     },
26331     
26332     setWeight : function(weight)
26333     {
26334         if(this.weight){
26335             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26336         }
26337         
26338         this.weight = weight;
26339         
26340         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26341     },
26342     
26343     setIcon : function(icon)
26344     {
26345         if(this.faicon){
26346             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26347         }
26348         
26349         this.faicon = icon;
26350         
26351         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26352     },
26353     
26354     hide: function() 
26355     {
26356         this.el.hide();   
26357     },
26358     
26359     show: function() 
26360     {  
26361         this.el.show();   
26362     }
26363     
26364 });
26365
26366  
26367 /*
26368 * Licence: LGPL
26369 */
26370
26371 /**
26372  * @class Roo.bootstrap.UploadCropbox
26373  * @extends Roo.bootstrap.Component
26374  * Bootstrap UploadCropbox class
26375  * @cfg {String} emptyText show when image has been loaded
26376  * @cfg {String} rotateNotify show when image too small to rotate
26377  * @cfg {Number} errorTimeout default 3000
26378  * @cfg {Number} minWidth default 300
26379  * @cfg {Number} minHeight default 300
26380  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26381  * @cfg {Boolean} isDocument (true|false) default false
26382  * @cfg {String} url action url
26383  * @cfg {String} paramName default 'imageUpload'
26384  * @cfg {String} method default POST
26385  * @cfg {Boolean} loadMask (true|false) default true
26386  * @cfg {Boolean} loadingText default 'Loading...'
26387  * 
26388  * @constructor
26389  * Create a new UploadCropbox
26390  * @param {Object} config The config object
26391  */
26392
26393 Roo.bootstrap.UploadCropbox = function(config){
26394     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26395     
26396     this.addEvents({
26397         /**
26398          * @event beforeselectfile
26399          * Fire before select file
26400          * @param {Roo.bootstrap.UploadCropbox} this
26401          */
26402         "beforeselectfile" : true,
26403         /**
26404          * @event initial
26405          * Fire after initEvent
26406          * @param {Roo.bootstrap.UploadCropbox} this
26407          */
26408         "initial" : true,
26409         /**
26410          * @event crop
26411          * Fire after initEvent
26412          * @param {Roo.bootstrap.UploadCropbox} this
26413          * @param {String} data
26414          */
26415         "crop" : true,
26416         /**
26417          * @event prepare
26418          * Fire when preparing the file data
26419          * @param {Roo.bootstrap.UploadCropbox} this
26420          * @param {Object} file
26421          */
26422         "prepare" : true,
26423         /**
26424          * @event exception
26425          * Fire when get exception
26426          * @param {Roo.bootstrap.UploadCropbox} this
26427          * @param {XMLHttpRequest} xhr
26428          */
26429         "exception" : true,
26430         /**
26431          * @event beforeloadcanvas
26432          * Fire before load the canvas
26433          * @param {Roo.bootstrap.UploadCropbox} this
26434          * @param {String} src
26435          */
26436         "beforeloadcanvas" : true,
26437         /**
26438          * @event trash
26439          * Fire when trash image
26440          * @param {Roo.bootstrap.UploadCropbox} this
26441          */
26442         "trash" : true,
26443         /**
26444          * @event download
26445          * Fire when download the image
26446          * @param {Roo.bootstrap.UploadCropbox} this
26447          */
26448         "download" : true,
26449         /**
26450          * @event footerbuttonclick
26451          * Fire when footerbuttonclick
26452          * @param {Roo.bootstrap.UploadCropbox} this
26453          * @param {String} type
26454          */
26455         "footerbuttonclick" : true,
26456         /**
26457          * @event resize
26458          * Fire when resize
26459          * @param {Roo.bootstrap.UploadCropbox} this
26460          */
26461         "resize" : true,
26462         /**
26463          * @event rotate
26464          * Fire when rotate the image
26465          * @param {Roo.bootstrap.UploadCropbox} this
26466          * @param {String} pos
26467          */
26468         "rotate" : true,
26469         /**
26470          * @event inspect
26471          * Fire when inspect the file
26472          * @param {Roo.bootstrap.UploadCropbox} this
26473          * @param {Object} file
26474          */
26475         "inspect" : true,
26476         /**
26477          * @event upload
26478          * Fire when xhr upload the file
26479          * @param {Roo.bootstrap.UploadCropbox} this
26480          * @param {Object} data
26481          */
26482         "upload" : true,
26483         /**
26484          * @event arrange
26485          * Fire when arrange the file data
26486          * @param {Roo.bootstrap.UploadCropbox} this
26487          * @param {Object} formData
26488          */
26489         "arrange" : true
26490     });
26491     
26492     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26493 };
26494
26495 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26496     
26497     emptyText : 'Click to upload image',
26498     rotateNotify : 'Image is too small to rotate',
26499     errorTimeout : 3000,
26500     scale : 0,
26501     baseScale : 1,
26502     rotate : 0,
26503     dragable : false,
26504     pinching : false,
26505     mouseX : 0,
26506     mouseY : 0,
26507     cropData : false,
26508     minWidth : 300,
26509     minHeight : 300,
26510     file : false,
26511     exif : {},
26512     baseRotate : 1,
26513     cropType : 'image/jpeg',
26514     buttons : false,
26515     canvasLoaded : false,
26516     isDocument : false,
26517     method : 'POST',
26518     paramName : 'imageUpload',
26519     loadMask : true,
26520     loadingText : 'Loading...',
26521     maskEl : false,
26522     
26523     getAutoCreate : function()
26524     {
26525         var cfg = {
26526             tag : 'div',
26527             cls : 'roo-upload-cropbox',
26528             cn : [
26529                 {
26530                     tag : 'input',
26531                     cls : 'roo-upload-cropbox-selector',
26532                     type : 'file'
26533                 },
26534                 {
26535                     tag : 'div',
26536                     cls : 'roo-upload-cropbox-body',
26537                     style : 'cursor:pointer',
26538                     cn : [
26539                         {
26540                             tag : 'div',
26541                             cls : 'roo-upload-cropbox-preview'
26542                         },
26543                         {
26544                             tag : 'div',
26545                             cls : 'roo-upload-cropbox-thumb'
26546                         },
26547                         {
26548                             tag : 'div',
26549                             cls : 'roo-upload-cropbox-empty-notify',
26550                             html : this.emptyText
26551                         },
26552                         {
26553                             tag : 'div',
26554                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26555                             html : this.rotateNotify
26556                         }
26557                     ]
26558                 },
26559                 {
26560                     tag : 'div',
26561                     cls : 'roo-upload-cropbox-footer',
26562                     cn : {
26563                         tag : 'div',
26564                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26565                         cn : []
26566                     }
26567                 }
26568             ]
26569         };
26570         
26571         return cfg;
26572     },
26573     
26574     onRender : function(ct, position)
26575     {
26576         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26577         
26578         if (this.buttons.length) {
26579             
26580             Roo.each(this.buttons, function(bb) {
26581                 
26582                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26583                 
26584                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26585                 
26586             }, this);
26587         }
26588         
26589         if(this.loadMask){
26590             this.maskEl = this.el;
26591         }
26592     },
26593     
26594     initEvents : function()
26595     {
26596         this.urlAPI = (window.createObjectURL && window) || 
26597                                 (window.URL && URL.revokeObjectURL && URL) || 
26598                                 (window.webkitURL && webkitURL);
26599                         
26600         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
26601         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26602         
26603         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
26604         this.selectorEl.hide();
26605         
26606         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
26607         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26608         
26609         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
26610         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26611         this.thumbEl.hide();
26612         
26613         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
26614         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26615         
26616         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
26617         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26618         this.errorEl.hide();
26619         
26620         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
26621         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26622         this.footerEl.hide();
26623         
26624         this.setThumbBoxSize();
26625         
26626         this.bind();
26627         
26628         this.resize();
26629         
26630         this.fireEvent('initial', this);
26631     },
26632
26633     bind : function()
26634     {
26635         var _this = this;
26636         
26637         window.addEventListener("resize", function() { _this.resize(); } );
26638         
26639         this.bodyEl.on('click', this.beforeSelectFile, this);
26640         
26641         if(Roo.isTouch){
26642             this.bodyEl.on('touchstart', this.onTouchStart, this);
26643             this.bodyEl.on('touchmove', this.onTouchMove, this);
26644             this.bodyEl.on('touchend', this.onTouchEnd, this);
26645         }
26646         
26647         if(!Roo.isTouch){
26648             this.bodyEl.on('mousedown', this.onMouseDown, this);
26649             this.bodyEl.on('mousemove', this.onMouseMove, this);
26650             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26651             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26652             Roo.get(document).on('mouseup', this.onMouseUp, this);
26653         }
26654         
26655         this.selectorEl.on('change', this.onFileSelected, this);
26656     },
26657     
26658     reset : function()
26659     {    
26660         this.scale = 0;
26661         this.baseScale = 1;
26662         this.rotate = 0;
26663         this.baseRotate = 1;
26664         this.dragable = false;
26665         this.pinching = false;
26666         this.mouseX = 0;
26667         this.mouseY = 0;
26668         this.cropData = false;
26669         this.notifyEl.dom.innerHTML = this.emptyText;
26670         
26671         this.selectorEl.dom.value = '';
26672         
26673     },
26674     
26675     resize : function()
26676     {
26677         if(this.fireEvent('resize', this) != false){
26678             this.setThumbBoxPosition();
26679             this.setCanvasPosition();
26680         }
26681     },
26682     
26683     onFooterButtonClick : function(e, el, o, type)
26684     {
26685         switch (type) {
26686             case 'rotate-left' :
26687                 this.onRotateLeft(e);
26688                 break;
26689             case 'rotate-right' :
26690                 this.onRotateRight(e);
26691                 break;
26692             case 'picture' :
26693                 this.beforeSelectFile(e);
26694                 break;
26695             case 'trash' :
26696                 this.trash(e);
26697                 break;
26698             case 'crop' :
26699                 this.crop(e);
26700                 break;
26701             case 'download' :
26702                 this.download(e);
26703                 break;
26704             default :
26705                 break;
26706         }
26707         
26708         this.fireEvent('footerbuttonclick', this, type);
26709     },
26710     
26711     beforeSelectFile : function(e)
26712     {
26713         e.preventDefault();
26714         
26715         if(this.fireEvent('beforeselectfile', this) != false){
26716             this.selectorEl.dom.click();
26717         }
26718     },
26719     
26720     onFileSelected : function(e)
26721     {
26722         e.preventDefault();
26723         
26724         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26725             return;
26726         }
26727         
26728         var file = this.selectorEl.dom.files[0];
26729         
26730         if(this.fireEvent('inspect', this, file) != false){
26731             this.prepare(file);
26732         }
26733         
26734     },
26735     
26736     trash : function(e)
26737     {
26738         this.fireEvent('trash', this);
26739     },
26740     
26741     download : function(e)
26742     {
26743         this.fireEvent('download', this);
26744     },
26745     
26746     loadCanvas : function(src)
26747     {   
26748         if(this.fireEvent('beforeloadcanvas', this, src) != false){
26749             
26750             this.reset();
26751             
26752             this.imageEl = document.createElement('img');
26753             
26754             var _this = this;
26755             
26756             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26757             
26758             this.imageEl.src = src;
26759         }
26760     },
26761     
26762     onLoadCanvas : function()
26763     {   
26764         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26765         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26766         
26767         this.bodyEl.un('click', this.beforeSelectFile, this);
26768         
26769         this.notifyEl.hide();
26770         this.thumbEl.show();
26771         this.footerEl.show();
26772         
26773         this.baseRotateLevel();
26774         
26775         if(this.isDocument){
26776             this.setThumbBoxSize();
26777         }
26778         
26779         this.setThumbBoxPosition();
26780         
26781         this.baseScaleLevel();
26782         
26783         this.draw();
26784         
26785         this.resize();
26786         
26787         this.canvasLoaded = true;
26788         
26789         if(this.loadMask){
26790             this.maskEl.unmask();
26791         }
26792         
26793     },
26794     
26795     setCanvasPosition : function()
26796     {   
26797         if(!this.canvasEl){
26798             return;
26799         }
26800         
26801         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26802         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26803         
26804         this.previewEl.setLeft(pw);
26805         this.previewEl.setTop(ph);
26806         
26807     },
26808     
26809     onMouseDown : function(e)
26810     {   
26811         e.stopEvent();
26812         
26813         this.dragable = true;
26814         this.pinching = false;
26815         
26816         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26817             this.dragable = false;
26818             return;
26819         }
26820         
26821         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26822         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26823         
26824     },
26825     
26826     onMouseMove : function(e)
26827     {   
26828         e.stopEvent();
26829         
26830         if(!this.canvasLoaded){
26831             return;
26832         }
26833         
26834         if (!this.dragable){
26835             return;
26836         }
26837         
26838         var minX = Math.ceil(this.thumbEl.getLeft(true));
26839         var minY = Math.ceil(this.thumbEl.getTop(true));
26840         
26841         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
26842         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
26843         
26844         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26845         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26846         
26847         x = x - this.mouseX;
26848         y = y - this.mouseY;
26849         
26850         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
26851         var bgY = Math.ceil(y + this.previewEl.getTop(true));
26852         
26853         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
26854         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
26855         
26856         this.previewEl.setLeft(bgX);
26857         this.previewEl.setTop(bgY);
26858         
26859         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26860         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26861     },
26862     
26863     onMouseUp : function(e)
26864     {   
26865         e.stopEvent();
26866         
26867         this.dragable = false;
26868     },
26869     
26870     onMouseWheel : function(e)
26871     {   
26872         e.stopEvent();
26873         
26874         this.startScale = this.scale;
26875         
26876         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
26877         
26878         if(!this.zoomable()){
26879             this.scale = this.startScale;
26880             return;
26881         }
26882         
26883         this.draw();
26884         
26885         return;
26886     },
26887     
26888     zoomable : function()
26889     {
26890         var minScale = this.thumbEl.getWidth() / this.minWidth;
26891         
26892         if(this.minWidth < this.minHeight){
26893             minScale = this.thumbEl.getHeight() / this.minHeight;
26894         }
26895         
26896         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
26897         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
26898         
26899         if(
26900                 this.isDocument &&
26901                 (this.rotate == 0 || this.rotate == 180) && 
26902                 (
26903                     width > this.imageEl.OriginWidth || 
26904                     height > this.imageEl.OriginHeight ||
26905                     (width < this.minWidth && height < this.minHeight)
26906                 )
26907         ){
26908             return false;
26909         }
26910         
26911         if(
26912                 this.isDocument &&
26913                 (this.rotate == 90 || this.rotate == 270) && 
26914                 (
26915                     width > this.imageEl.OriginWidth || 
26916                     height > this.imageEl.OriginHeight ||
26917                     (width < this.minHeight && height < this.minWidth)
26918                 )
26919         ){
26920             return false;
26921         }
26922         
26923         if(
26924                 !this.isDocument &&
26925                 (this.rotate == 0 || this.rotate == 180) && 
26926                 (
26927                     width < this.minWidth || 
26928                     width > this.imageEl.OriginWidth || 
26929                     height < this.minHeight || 
26930                     height > this.imageEl.OriginHeight
26931                 )
26932         ){
26933             return false;
26934         }
26935         
26936         if(
26937                 !this.isDocument &&
26938                 (this.rotate == 90 || this.rotate == 270) && 
26939                 (
26940                     width < this.minHeight || 
26941                     width > this.imageEl.OriginWidth || 
26942                     height < this.minWidth || 
26943                     height > this.imageEl.OriginHeight
26944                 )
26945         ){
26946             return false;
26947         }
26948         
26949         return true;
26950         
26951     },
26952     
26953     onRotateLeft : function(e)
26954     {   
26955         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26956             
26957             var minScale = this.thumbEl.getWidth() / this.minWidth;
26958             
26959             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26960             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26961             
26962             this.startScale = this.scale;
26963             
26964             while (this.getScaleLevel() < minScale){
26965             
26966                 this.scale = this.scale + 1;
26967                 
26968                 if(!this.zoomable()){
26969                     break;
26970                 }
26971                 
26972                 if(
26973                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26974                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26975                 ){
26976                     continue;
26977                 }
26978                 
26979                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26980
26981                 this.draw();
26982                 
26983                 return;
26984             }
26985             
26986             this.scale = this.startScale;
26987             
26988             this.onRotateFail();
26989             
26990             return false;
26991         }
26992         
26993         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26994
26995         if(this.isDocument){
26996             this.setThumbBoxSize();
26997             this.setThumbBoxPosition();
26998             this.setCanvasPosition();
26999         }
27000         
27001         this.draw();
27002         
27003         this.fireEvent('rotate', this, 'left');
27004         
27005     },
27006     
27007     onRotateRight : function(e)
27008     {
27009         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27010             
27011             var minScale = this.thumbEl.getWidth() / this.minWidth;
27012         
27013             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27014             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27015             
27016             this.startScale = this.scale;
27017             
27018             while (this.getScaleLevel() < minScale){
27019             
27020                 this.scale = this.scale + 1;
27021                 
27022                 if(!this.zoomable()){
27023                     break;
27024                 }
27025                 
27026                 if(
27027                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27028                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27029                 ){
27030                     continue;
27031                 }
27032                 
27033                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27034
27035                 this.draw();
27036                 
27037                 return;
27038             }
27039             
27040             this.scale = this.startScale;
27041             
27042             this.onRotateFail();
27043             
27044             return false;
27045         }
27046         
27047         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27048
27049         if(this.isDocument){
27050             this.setThumbBoxSize();
27051             this.setThumbBoxPosition();
27052             this.setCanvasPosition();
27053         }
27054         
27055         this.draw();
27056         
27057         this.fireEvent('rotate', this, 'right');
27058     },
27059     
27060     onRotateFail : function()
27061     {
27062         this.errorEl.show(true);
27063         
27064         var _this = this;
27065         
27066         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27067     },
27068     
27069     draw : function()
27070     {
27071         this.previewEl.dom.innerHTML = '';
27072         
27073         var canvasEl = document.createElement("canvas");
27074         
27075         var contextEl = canvasEl.getContext("2d");
27076         
27077         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27078         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27079         var center = this.imageEl.OriginWidth / 2;
27080         
27081         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27082             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27083             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27084             center = this.imageEl.OriginHeight / 2;
27085         }
27086         
27087         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27088         
27089         contextEl.translate(center, center);
27090         contextEl.rotate(this.rotate * Math.PI / 180);
27091
27092         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27093         
27094         this.canvasEl = document.createElement("canvas");
27095         
27096         this.contextEl = this.canvasEl.getContext("2d");
27097         
27098         switch (this.rotate) {
27099             case 0 :
27100                 
27101                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27102                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27103                 
27104                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27105                 
27106                 break;
27107             case 90 : 
27108                 
27109                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27110                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27111                 
27112                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27113                     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);
27114                     break;
27115                 }
27116                 
27117                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27118                 
27119                 break;
27120             case 180 :
27121                 
27122                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27123                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27124                 
27125                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27126                     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);
27127                     break;
27128                 }
27129                 
27130                 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);
27131                 
27132                 break;
27133             case 270 :
27134                 
27135                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27136                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27137         
27138                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27139                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27140                     break;
27141                 }
27142                 
27143                 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);
27144                 
27145                 break;
27146             default : 
27147                 break;
27148         }
27149         
27150         this.previewEl.appendChild(this.canvasEl);
27151         
27152         this.setCanvasPosition();
27153     },
27154     
27155     crop : function()
27156     {
27157         if(!this.canvasLoaded){
27158             return;
27159         }
27160         
27161         var imageCanvas = document.createElement("canvas");
27162         
27163         var imageContext = imageCanvas.getContext("2d");
27164         
27165         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27166         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27167         
27168         var center = imageCanvas.width / 2;
27169         
27170         imageContext.translate(center, center);
27171         
27172         imageContext.rotate(this.rotate * Math.PI / 180);
27173         
27174         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27175         
27176         var canvas = document.createElement("canvas");
27177         
27178         var context = canvas.getContext("2d");
27179                 
27180         canvas.width = this.minWidth;
27181         canvas.height = this.minHeight;
27182
27183         switch (this.rotate) {
27184             case 0 :
27185                 
27186                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27187                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27188                 
27189                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27190                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27191                 
27192                 var targetWidth = this.minWidth - 2 * x;
27193                 var targetHeight = this.minHeight - 2 * y;
27194                 
27195                 var scale = 1;
27196                 
27197                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27198                     scale = targetWidth / width;
27199                 }
27200                 
27201                 if(x > 0 && y == 0){
27202                     scale = targetHeight / height;
27203                 }
27204                 
27205                 if(x > 0 && y > 0){
27206                     scale = targetWidth / width;
27207                     
27208                     if(width < height){
27209                         scale = targetHeight / height;
27210                     }
27211                 }
27212                 
27213                 context.scale(scale, scale);
27214                 
27215                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27216                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27217
27218                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27219                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27220
27221                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27222                 
27223                 break;
27224             case 90 : 
27225                 
27226                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27227                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27228                 
27229                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27230                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27231                 
27232                 var targetWidth = this.minWidth - 2 * x;
27233                 var targetHeight = this.minHeight - 2 * y;
27234                 
27235                 var scale = 1;
27236                 
27237                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27238                     scale = targetWidth / width;
27239                 }
27240                 
27241                 if(x > 0 && y == 0){
27242                     scale = targetHeight / height;
27243                 }
27244                 
27245                 if(x > 0 && y > 0){
27246                     scale = targetWidth / width;
27247                     
27248                     if(width < height){
27249                         scale = targetHeight / height;
27250                     }
27251                 }
27252                 
27253                 context.scale(scale, scale);
27254                 
27255                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27256                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27257
27258                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27259                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27260                 
27261                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27262                 
27263                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27264                 
27265                 break;
27266             case 180 :
27267                 
27268                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27269                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27270                 
27271                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27272                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27273                 
27274                 var targetWidth = this.minWidth - 2 * x;
27275                 var targetHeight = this.minHeight - 2 * y;
27276                 
27277                 var scale = 1;
27278                 
27279                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27280                     scale = targetWidth / width;
27281                 }
27282                 
27283                 if(x > 0 && y == 0){
27284                     scale = targetHeight / height;
27285                 }
27286                 
27287                 if(x > 0 && y > 0){
27288                     scale = targetWidth / width;
27289                     
27290                     if(width < height){
27291                         scale = targetHeight / height;
27292                     }
27293                 }
27294                 
27295                 context.scale(scale, scale);
27296                 
27297                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27298                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27299
27300                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27301                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27302
27303                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27304                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27305                 
27306                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27307                 
27308                 break;
27309             case 270 :
27310                 
27311                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27312                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27313                 
27314                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27315                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27316                 
27317                 var targetWidth = this.minWidth - 2 * x;
27318                 var targetHeight = this.minHeight - 2 * y;
27319                 
27320                 var scale = 1;
27321                 
27322                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27323                     scale = targetWidth / width;
27324                 }
27325                 
27326                 if(x > 0 && y == 0){
27327                     scale = targetHeight / height;
27328                 }
27329                 
27330                 if(x > 0 && y > 0){
27331                     scale = targetWidth / width;
27332                     
27333                     if(width < height){
27334                         scale = targetHeight / height;
27335                     }
27336                 }
27337                 
27338                 context.scale(scale, scale);
27339                 
27340                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27341                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27342
27343                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27344                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27345                 
27346                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27347                 
27348                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27349                 
27350                 break;
27351             default : 
27352                 break;
27353         }
27354         
27355         this.cropData = canvas.toDataURL(this.cropType);
27356         
27357         if(this.fireEvent('crop', this, this.cropData) !== false){
27358             this.process(this.file, this.cropData);
27359         }
27360         
27361         return;
27362         
27363     },
27364     
27365     setThumbBoxSize : function()
27366     {
27367         var width, height;
27368         
27369         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27370             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27371             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27372             
27373             this.minWidth = width;
27374             this.minHeight = height;
27375             
27376             if(this.rotate == 90 || this.rotate == 270){
27377                 this.minWidth = height;
27378                 this.minHeight = width;
27379             }
27380         }
27381         
27382         height = 300;
27383         width = Math.ceil(this.minWidth * height / this.minHeight);
27384         
27385         if(this.minWidth > this.minHeight){
27386             width = 300;
27387             height = Math.ceil(this.minHeight * width / this.minWidth);
27388         }
27389         
27390         this.thumbEl.setStyle({
27391             width : width + 'px',
27392             height : height + 'px'
27393         });
27394
27395         return;
27396             
27397     },
27398     
27399     setThumbBoxPosition : function()
27400     {
27401         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27402         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27403         
27404         this.thumbEl.setLeft(x);
27405         this.thumbEl.setTop(y);
27406         
27407     },
27408     
27409     baseRotateLevel : function()
27410     {
27411         this.baseRotate = 1;
27412         
27413         if(
27414                 typeof(this.exif) != 'undefined' &&
27415                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27416                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27417         ){
27418             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27419         }
27420         
27421         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27422         
27423     },
27424     
27425     baseScaleLevel : function()
27426     {
27427         var width, height;
27428         
27429         if(this.isDocument){
27430             
27431             if(this.baseRotate == 6 || this.baseRotate == 8){
27432             
27433                 height = this.thumbEl.getHeight();
27434                 this.baseScale = height / this.imageEl.OriginWidth;
27435
27436                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27437                     width = this.thumbEl.getWidth();
27438                     this.baseScale = width / this.imageEl.OriginHeight;
27439                 }
27440
27441                 return;
27442             }
27443
27444             height = this.thumbEl.getHeight();
27445             this.baseScale = height / this.imageEl.OriginHeight;
27446
27447             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27448                 width = this.thumbEl.getWidth();
27449                 this.baseScale = width / this.imageEl.OriginWidth;
27450             }
27451
27452             return;
27453         }
27454         
27455         if(this.baseRotate == 6 || this.baseRotate == 8){
27456             
27457             width = this.thumbEl.getHeight();
27458             this.baseScale = width / this.imageEl.OriginHeight;
27459             
27460             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27461                 height = this.thumbEl.getWidth();
27462                 this.baseScale = height / this.imageEl.OriginHeight;
27463             }
27464             
27465             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27466                 height = this.thumbEl.getWidth();
27467                 this.baseScale = height / this.imageEl.OriginHeight;
27468                 
27469                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27470                     width = this.thumbEl.getHeight();
27471                     this.baseScale = width / this.imageEl.OriginWidth;
27472                 }
27473             }
27474             
27475             return;
27476         }
27477         
27478         width = this.thumbEl.getWidth();
27479         this.baseScale = width / this.imageEl.OriginWidth;
27480         
27481         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27482             height = this.thumbEl.getHeight();
27483             this.baseScale = height / this.imageEl.OriginHeight;
27484         }
27485         
27486         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27487             
27488             height = this.thumbEl.getHeight();
27489             this.baseScale = height / this.imageEl.OriginHeight;
27490             
27491             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27492                 width = this.thumbEl.getWidth();
27493                 this.baseScale = width / this.imageEl.OriginWidth;
27494             }
27495             
27496         }
27497         
27498         return;
27499     },
27500     
27501     getScaleLevel : function()
27502     {
27503         return this.baseScale * Math.pow(1.1, this.scale);
27504     },
27505     
27506     onTouchStart : function(e)
27507     {
27508         if(!this.canvasLoaded){
27509             this.beforeSelectFile(e);
27510             return;
27511         }
27512         
27513         var touches = e.browserEvent.touches;
27514         
27515         if(!touches){
27516             return;
27517         }
27518         
27519         if(touches.length == 1){
27520             this.onMouseDown(e);
27521             return;
27522         }
27523         
27524         if(touches.length != 2){
27525             return;
27526         }
27527         
27528         var coords = [];
27529         
27530         for(var i = 0, finger; finger = touches[i]; i++){
27531             coords.push(finger.pageX, finger.pageY);
27532         }
27533         
27534         var x = Math.pow(coords[0] - coords[2], 2);
27535         var y = Math.pow(coords[1] - coords[3], 2);
27536         
27537         this.startDistance = Math.sqrt(x + y);
27538         
27539         this.startScale = this.scale;
27540         
27541         this.pinching = true;
27542         this.dragable = false;
27543         
27544     },
27545     
27546     onTouchMove : function(e)
27547     {
27548         if(!this.pinching && !this.dragable){
27549             return;
27550         }
27551         
27552         var touches = e.browserEvent.touches;
27553         
27554         if(!touches){
27555             return;
27556         }
27557         
27558         if(this.dragable){
27559             this.onMouseMove(e);
27560             return;
27561         }
27562         
27563         var coords = [];
27564         
27565         for(var i = 0, finger; finger = touches[i]; i++){
27566             coords.push(finger.pageX, finger.pageY);
27567         }
27568         
27569         var x = Math.pow(coords[0] - coords[2], 2);
27570         var y = Math.pow(coords[1] - coords[3], 2);
27571         
27572         this.endDistance = Math.sqrt(x + y);
27573         
27574         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27575         
27576         if(!this.zoomable()){
27577             this.scale = this.startScale;
27578             return;
27579         }
27580         
27581         this.draw();
27582         
27583     },
27584     
27585     onTouchEnd : function(e)
27586     {
27587         this.pinching = false;
27588         this.dragable = false;
27589         
27590     },
27591     
27592     process : function(file, crop)
27593     {
27594         if(this.loadMask){
27595             this.maskEl.mask(this.loadingText);
27596         }
27597         
27598         this.xhr = new XMLHttpRequest();
27599         
27600         file.xhr = this.xhr;
27601
27602         this.xhr.open(this.method, this.url, true);
27603         
27604         var headers = {
27605             "Accept": "application/json",
27606             "Cache-Control": "no-cache",
27607             "X-Requested-With": "XMLHttpRequest"
27608         };
27609         
27610         for (var headerName in headers) {
27611             var headerValue = headers[headerName];
27612             if (headerValue) {
27613                 this.xhr.setRequestHeader(headerName, headerValue);
27614             }
27615         }
27616         
27617         var _this = this;
27618         
27619         this.xhr.onload = function()
27620         {
27621             _this.xhrOnLoad(_this.xhr);
27622         }
27623         
27624         this.xhr.onerror = function()
27625         {
27626             _this.xhrOnError(_this.xhr);
27627         }
27628         
27629         var formData = new FormData();
27630
27631         formData.append('returnHTML', 'NO');
27632         
27633         if(crop){
27634             formData.append('crop', crop);
27635         }
27636         
27637         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
27638             formData.append(this.paramName, file, file.name);
27639         }
27640         
27641         if(typeof(file.filename) != 'undefined'){
27642             formData.append('filename', file.filename);
27643         }
27644         
27645         if(typeof(file.mimetype) != 'undefined'){
27646             formData.append('mimetype', file.mimetype);
27647         }
27648         
27649         if(this.fireEvent('arrange', this, formData) != false){
27650             this.xhr.send(formData);
27651         };
27652     },
27653     
27654     xhrOnLoad : function(xhr)
27655     {
27656         if(this.loadMask){
27657             this.maskEl.unmask();
27658         }
27659         
27660         if (xhr.readyState !== 4) {
27661             this.fireEvent('exception', this, xhr);
27662             return;
27663         }
27664
27665         var response = Roo.decode(xhr.responseText);
27666         
27667         if(!response.success){
27668             this.fireEvent('exception', this, xhr);
27669             return;
27670         }
27671         
27672         var response = Roo.decode(xhr.responseText);
27673         
27674         this.fireEvent('upload', this, response);
27675         
27676     },
27677     
27678     xhrOnError : function()
27679     {
27680         if(this.loadMask){
27681             this.maskEl.unmask();
27682         }
27683         
27684         Roo.log('xhr on error');
27685         
27686         var response = Roo.decode(xhr.responseText);
27687           
27688         Roo.log(response);
27689         
27690     },
27691     
27692     prepare : function(file)
27693     {   
27694         if(this.loadMask){
27695             this.maskEl.mask(this.loadingText);
27696         }
27697         
27698         this.file = false;
27699         this.exif = {};
27700         
27701         if(typeof(file) === 'string'){
27702             this.loadCanvas(file);
27703             return;
27704         }
27705         
27706         if(!file || !this.urlAPI){
27707             return;
27708         }
27709         
27710         this.file = file;
27711         this.cropType = file.type;
27712         
27713         var _this = this;
27714         
27715         if(this.fireEvent('prepare', this, this.file) != false){
27716             
27717             var reader = new FileReader();
27718             
27719             reader.onload = function (e) {
27720                 if (e.target.error) {
27721                     Roo.log(e.target.error);
27722                     return;
27723                 }
27724                 
27725                 var buffer = e.target.result,
27726                     dataView = new DataView(buffer),
27727                     offset = 2,
27728                     maxOffset = dataView.byteLength - 4,
27729                     markerBytes,
27730                     markerLength;
27731                 
27732                 if (dataView.getUint16(0) === 0xffd8) {
27733                     while (offset < maxOffset) {
27734                         markerBytes = dataView.getUint16(offset);
27735                         
27736                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27737                             markerLength = dataView.getUint16(offset + 2) + 2;
27738                             if (offset + markerLength > dataView.byteLength) {
27739                                 Roo.log('Invalid meta data: Invalid segment size.');
27740                                 break;
27741                             }
27742                             
27743                             if(markerBytes == 0xffe1){
27744                                 _this.parseExifData(
27745                                     dataView,
27746                                     offset,
27747                                     markerLength
27748                                 );
27749                             }
27750                             
27751                             offset += markerLength;
27752                             
27753                             continue;
27754                         }
27755                         
27756                         break;
27757                     }
27758                     
27759                 }
27760                 
27761                 var url = _this.urlAPI.createObjectURL(_this.file);
27762                 
27763                 _this.loadCanvas(url);
27764                 
27765                 return;
27766             }
27767             
27768             reader.readAsArrayBuffer(this.file);
27769             
27770         }
27771         
27772     },
27773     
27774     parseExifData : function(dataView, offset, length)
27775     {
27776         var tiffOffset = offset + 10,
27777             littleEndian,
27778             dirOffset;
27779     
27780         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27781             // No Exif data, might be XMP data instead
27782             return;
27783         }
27784         
27785         // Check for the ASCII code for "Exif" (0x45786966):
27786         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27787             // No Exif data, might be XMP data instead
27788             return;
27789         }
27790         if (tiffOffset + 8 > dataView.byteLength) {
27791             Roo.log('Invalid Exif data: Invalid segment size.');
27792             return;
27793         }
27794         // Check for the two null bytes:
27795         if (dataView.getUint16(offset + 8) !== 0x0000) {
27796             Roo.log('Invalid Exif data: Missing byte alignment offset.');
27797             return;
27798         }
27799         // Check the byte alignment:
27800         switch (dataView.getUint16(tiffOffset)) {
27801         case 0x4949:
27802             littleEndian = true;
27803             break;
27804         case 0x4D4D:
27805             littleEndian = false;
27806             break;
27807         default:
27808             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27809             return;
27810         }
27811         // Check for the TIFF tag marker (0x002A):
27812         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27813             Roo.log('Invalid Exif data: Missing TIFF marker.');
27814             return;
27815         }
27816         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27817         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27818         
27819         this.parseExifTags(
27820             dataView,
27821             tiffOffset,
27822             tiffOffset + dirOffset,
27823             littleEndian
27824         );
27825     },
27826     
27827     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27828     {
27829         var tagsNumber,
27830             dirEndOffset,
27831             i;
27832         if (dirOffset + 6 > dataView.byteLength) {
27833             Roo.log('Invalid Exif data: Invalid directory offset.');
27834             return;
27835         }
27836         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
27837         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
27838         if (dirEndOffset + 4 > dataView.byteLength) {
27839             Roo.log('Invalid Exif data: Invalid directory size.');
27840             return;
27841         }
27842         for (i = 0; i < tagsNumber; i += 1) {
27843             this.parseExifTag(
27844                 dataView,
27845                 tiffOffset,
27846                 dirOffset + 2 + 12 * i, // tag offset
27847                 littleEndian
27848             );
27849         }
27850         // Return the offset to the next directory:
27851         return dataView.getUint32(dirEndOffset, littleEndian);
27852     },
27853     
27854     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
27855     {
27856         var tag = dataView.getUint16(offset, littleEndian);
27857         
27858         this.exif[tag] = this.getExifValue(
27859             dataView,
27860             tiffOffset,
27861             offset,
27862             dataView.getUint16(offset + 2, littleEndian), // tag type
27863             dataView.getUint32(offset + 4, littleEndian), // tag length
27864             littleEndian
27865         );
27866     },
27867     
27868     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
27869     {
27870         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
27871             tagSize,
27872             dataOffset,
27873             values,
27874             i,
27875             str,
27876             c;
27877     
27878         if (!tagType) {
27879             Roo.log('Invalid Exif data: Invalid tag type.');
27880             return;
27881         }
27882         
27883         tagSize = tagType.size * length;
27884         // Determine if the value is contained in the dataOffset bytes,
27885         // or if the value at the dataOffset is a pointer to the actual data:
27886         dataOffset = tagSize > 4 ?
27887                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
27888         if (dataOffset + tagSize > dataView.byteLength) {
27889             Roo.log('Invalid Exif data: Invalid data offset.');
27890             return;
27891         }
27892         if (length === 1) {
27893             return tagType.getValue(dataView, dataOffset, littleEndian);
27894         }
27895         values = [];
27896         for (i = 0; i < length; i += 1) {
27897             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
27898         }
27899         
27900         if (tagType.ascii) {
27901             str = '';
27902             // Concatenate the chars:
27903             for (i = 0; i < values.length; i += 1) {
27904                 c = values[i];
27905                 // Ignore the terminating NULL byte(s):
27906                 if (c === '\u0000') {
27907                     break;
27908                 }
27909                 str += c;
27910             }
27911             return str;
27912         }
27913         return values;
27914     }
27915     
27916 });
27917
27918 Roo.apply(Roo.bootstrap.UploadCropbox, {
27919     tags : {
27920         'Orientation': 0x0112
27921     },
27922     
27923     Orientation: {
27924             1: 0, //'top-left',
27925 //            2: 'top-right',
27926             3: 180, //'bottom-right',
27927 //            4: 'bottom-left',
27928 //            5: 'left-top',
27929             6: 90, //'right-top',
27930 //            7: 'right-bottom',
27931             8: 270 //'left-bottom'
27932     },
27933     
27934     exifTagTypes : {
27935         // byte, 8-bit unsigned int:
27936         1: {
27937             getValue: function (dataView, dataOffset) {
27938                 return dataView.getUint8(dataOffset);
27939             },
27940             size: 1
27941         },
27942         // ascii, 8-bit byte:
27943         2: {
27944             getValue: function (dataView, dataOffset) {
27945                 return String.fromCharCode(dataView.getUint8(dataOffset));
27946             },
27947             size: 1,
27948             ascii: true
27949         },
27950         // short, 16 bit int:
27951         3: {
27952             getValue: function (dataView, dataOffset, littleEndian) {
27953                 return dataView.getUint16(dataOffset, littleEndian);
27954             },
27955             size: 2
27956         },
27957         // long, 32 bit int:
27958         4: {
27959             getValue: function (dataView, dataOffset, littleEndian) {
27960                 return dataView.getUint32(dataOffset, littleEndian);
27961             },
27962             size: 4
27963         },
27964         // rational = two long values, first is numerator, second is denominator:
27965         5: {
27966             getValue: function (dataView, dataOffset, littleEndian) {
27967                 return dataView.getUint32(dataOffset, littleEndian) /
27968                     dataView.getUint32(dataOffset + 4, littleEndian);
27969             },
27970             size: 8
27971         },
27972         // slong, 32 bit signed int:
27973         9: {
27974             getValue: function (dataView, dataOffset, littleEndian) {
27975                 return dataView.getInt32(dataOffset, littleEndian);
27976             },
27977             size: 4
27978         },
27979         // srational, two slongs, first is numerator, second is denominator:
27980         10: {
27981             getValue: function (dataView, dataOffset, littleEndian) {
27982                 return dataView.getInt32(dataOffset, littleEndian) /
27983                     dataView.getInt32(dataOffset + 4, littleEndian);
27984             },
27985             size: 8
27986         }
27987     },
27988     
27989     footer : {
27990         STANDARD : [
27991             {
27992                 tag : 'div',
27993                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27994                 action : 'rotate-left',
27995                 cn : [
27996                     {
27997                         tag : 'button',
27998                         cls : 'btn btn-default',
27999                         html : '<i class="fa fa-undo"></i>'
28000                     }
28001                 ]
28002             },
28003             {
28004                 tag : 'div',
28005                 cls : 'btn-group roo-upload-cropbox-picture',
28006                 action : 'picture',
28007                 cn : [
28008                     {
28009                         tag : 'button',
28010                         cls : 'btn btn-default',
28011                         html : '<i class="fa fa-picture-o"></i>'
28012                     }
28013                 ]
28014             },
28015             {
28016                 tag : 'div',
28017                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28018                 action : 'rotate-right',
28019                 cn : [
28020                     {
28021                         tag : 'button',
28022                         cls : 'btn btn-default',
28023                         html : '<i class="fa fa-repeat"></i>'
28024                     }
28025                 ]
28026             }
28027         ],
28028         DOCUMENT : [
28029             {
28030                 tag : 'div',
28031                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28032                 action : 'rotate-left',
28033                 cn : [
28034                     {
28035                         tag : 'button',
28036                         cls : 'btn btn-default',
28037                         html : '<i class="fa fa-undo"></i>'
28038                     }
28039                 ]
28040             },
28041             {
28042                 tag : 'div',
28043                 cls : 'btn-group roo-upload-cropbox-download',
28044                 action : 'download',
28045                 cn : [
28046                     {
28047                         tag : 'button',
28048                         cls : 'btn btn-default',
28049                         html : '<i class="fa fa-download"></i>'
28050                     }
28051                 ]
28052             },
28053             {
28054                 tag : 'div',
28055                 cls : 'btn-group roo-upload-cropbox-crop',
28056                 action : 'crop',
28057                 cn : [
28058                     {
28059                         tag : 'button',
28060                         cls : 'btn btn-default',
28061                         html : '<i class="fa fa-crop"></i>'
28062                     }
28063                 ]
28064             },
28065             {
28066                 tag : 'div',
28067                 cls : 'btn-group roo-upload-cropbox-trash',
28068                 action : 'trash',
28069                 cn : [
28070                     {
28071                         tag : 'button',
28072                         cls : 'btn btn-default',
28073                         html : '<i class="fa fa-trash"></i>'
28074                     }
28075                 ]
28076             },
28077             {
28078                 tag : 'div',
28079                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28080                 action : 'rotate-right',
28081                 cn : [
28082                     {
28083                         tag : 'button',
28084                         cls : 'btn btn-default',
28085                         html : '<i class="fa fa-repeat"></i>'
28086                     }
28087                 ]
28088             }
28089         ],
28090         ROTATOR : [
28091             {
28092                 tag : 'div',
28093                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28094                 action : 'rotate-left',
28095                 cn : [
28096                     {
28097                         tag : 'button',
28098                         cls : 'btn btn-default',
28099                         html : '<i class="fa fa-undo"></i>'
28100                     }
28101                 ]
28102             },
28103             {
28104                 tag : 'div',
28105                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28106                 action : 'rotate-right',
28107                 cn : [
28108                     {
28109                         tag : 'button',
28110                         cls : 'btn btn-default',
28111                         html : '<i class="fa fa-repeat"></i>'
28112                     }
28113                 ]
28114             }
28115         ]
28116     }
28117 });
28118
28119 /*
28120 * Licence: LGPL
28121 */
28122
28123 /**
28124  * @class Roo.bootstrap.DocumentManager
28125  * @extends Roo.bootstrap.Component
28126  * Bootstrap DocumentManager class
28127  * @cfg {String} paramName default 'imageUpload'
28128  * @cfg {String} toolTipName default 'filename'
28129  * @cfg {String} method default POST
28130  * @cfg {String} url action url
28131  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28132  * @cfg {Boolean} multiple multiple upload default true
28133  * @cfg {Number} thumbSize default 300
28134  * @cfg {String} fieldLabel
28135  * @cfg {Number} labelWidth default 4
28136  * @cfg {String} labelAlign (left|top) default left
28137  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28138 * @cfg {Number} labellg set the width of label (1-12)
28139  * @cfg {Number} labelmd set the width of label (1-12)
28140  * @cfg {Number} labelsm set the width of label (1-12)
28141  * @cfg {Number} labelxs set the width of label (1-12)
28142  * 
28143  * @constructor
28144  * Create a new DocumentManager
28145  * @param {Object} config The config object
28146  */
28147
28148 Roo.bootstrap.DocumentManager = function(config){
28149     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28150     
28151     this.files = [];
28152     this.delegates = [];
28153     
28154     this.addEvents({
28155         /**
28156          * @event initial
28157          * Fire when initial the DocumentManager
28158          * @param {Roo.bootstrap.DocumentManager} this
28159          */
28160         "initial" : true,
28161         /**
28162          * @event inspect
28163          * inspect selected file
28164          * @param {Roo.bootstrap.DocumentManager} this
28165          * @param {File} file
28166          */
28167         "inspect" : true,
28168         /**
28169          * @event exception
28170          * Fire when xhr load exception
28171          * @param {Roo.bootstrap.DocumentManager} this
28172          * @param {XMLHttpRequest} xhr
28173          */
28174         "exception" : true,
28175         /**
28176          * @event afterupload
28177          * Fire when xhr load exception
28178          * @param {Roo.bootstrap.DocumentManager} this
28179          * @param {XMLHttpRequest} xhr
28180          */
28181         "afterupload" : true,
28182         /**
28183          * @event prepare
28184          * prepare the form data
28185          * @param {Roo.bootstrap.DocumentManager} this
28186          * @param {Object} formData
28187          */
28188         "prepare" : true,
28189         /**
28190          * @event remove
28191          * Fire when remove the file
28192          * @param {Roo.bootstrap.DocumentManager} this
28193          * @param {Object} file
28194          */
28195         "remove" : true,
28196         /**
28197          * @event refresh
28198          * Fire after refresh the file
28199          * @param {Roo.bootstrap.DocumentManager} this
28200          */
28201         "refresh" : true,
28202         /**
28203          * @event click
28204          * Fire after click the image
28205          * @param {Roo.bootstrap.DocumentManager} this
28206          * @param {Object} file
28207          */
28208         "click" : true,
28209         /**
28210          * @event edit
28211          * Fire when upload a image and editable set to true
28212          * @param {Roo.bootstrap.DocumentManager} this
28213          * @param {Object} file
28214          */
28215         "edit" : true,
28216         /**
28217          * @event beforeselectfile
28218          * Fire before select file
28219          * @param {Roo.bootstrap.DocumentManager} this
28220          */
28221         "beforeselectfile" : true,
28222         /**
28223          * @event process
28224          * Fire before process file
28225          * @param {Roo.bootstrap.DocumentManager} this
28226          * @param {Object} file
28227          */
28228         "process" : true
28229         
28230     });
28231 };
28232
28233 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28234     
28235     boxes : 0,
28236     inputName : '',
28237     thumbSize : 300,
28238     multiple : true,
28239     files : false,
28240     method : 'POST',
28241     url : '',
28242     paramName : 'imageUpload',
28243     toolTipName : 'filename',
28244     fieldLabel : '',
28245     labelWidth : 4,
28246     labelAlign : 'left',
28247     editable : true,
28248     delegates : false,
28249     xhr : false, 
28250     
28251     labellg : 0,
28252     labelmd : 0,
28253     labelsm : 0,
28254     labelxs : 0,
28255     
28256     getAutoCreate : function()
28257     {   
28258         var managerWidget = {
28259             tag : 'div',
28260             cls : 'roo-document-manager',
28261             cn : [
28262                 {
28263                     tag : 'input',
28264                     cls : 'roo-document-manager-selector',
28265                     type : 'file'
28266                 },
28267                 {
28268                     tag : 'div',
28269                     cls : 'roo-document-manager-uploader',
28270                     cn : [
28271                         {
28272                             tag : 'div',
28273                             cls : 'roo-document-manager-upload-btn',
28274                             html : '<i class="fa fa-plus"></i>'
28275                         }
28276                     ]
28277                     
28278                 }
28279             ]
28280         };
28281         
28282         var content = [
28283             {
28284                 tag : 'div',
28285                 cls : 'column col-md-12',
28286                 cn : managerWidget
28287             }
28288         ];
28289         
28290         if(this.fieldLabel.length){
28291             
28292             content = [
28293                 {
28294                     tag : 'div',
28295                     cls : 'column col-md-12',
28296                     html : this.fieldLabel
28297                 },
28298                 {
28299                     tag : 'div',
28300                     cls : 'column col-md-12',
28301                     cn : managerWidget
28302                 }
28303             ];
28304
28305             if(this.labelAlign == 'left'){
28306                 content = [
28307                     {
28308                         tag : 'div',
28309                         cls : 'column',
28310                         html : this.fieldLabel
28311                     },
28312                     {
28313                         tag : 'div',
28314                         cls : 'column',
28315                         cn : managerWidget
28316                     }
28317                 ];
28318                 
28319                 if(this.labelWidth > 12){
28320                     content[0].style = "width: " + this.labelWidth + 'px';
28321                 }
28322
28323                 if(this.labelWidth < 13 && this.labelmd == 0){
28324                     this.labelmd = this.labelWidth;
28325                 }
28326
28327                 if(this.labellg > 0){
28328                     content[0].cls += ' col-lg-' + this.labellg;
28329                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28330                 }
28331
28332                 if(this.labelmd > 0){
28333                     content[0].cls += ' col-md-' + this.labelmd;
28334                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28335                 }
28336
28337                 if(this.labelsm > 0){
28338                     content[0].cls += ' col-sm-' + this.labelsm;
28339                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28340                 }
28341
28342                 if(this.labelxs > 0){
28343                     content[0].cls += ' col-xs-' + this.labelxs;
28344                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28345                 }
28346                 
28347             }
28348         }
28349         
28350         var cfg = {
28351             tag : 'div',
28352             cls : 'row clearfix',
28353             cn : content
28354         };
28355         
28356         return cfg;
28357         
28358     },
28359     
28360     initEvents : function()
28361     {
28362         this.managerEl = this.el.select('.roo-document-manager', true).first();
28363         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28364         
28365         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28366         this.selectorEl.hide();
28367         
28368         if(this.multiple){
28369             this.selectorEl.attr('multiple', 'multiple');
28370         }
28371         
28372         this.selectorEl.on('change', this.onFileSelected, this);
28373         
28374         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28375         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28376         
28377         this.uploader.on('click', this.onUploaderClick, this);
28378         
28379         this.renderProgressDialog();
28380         
28381         var _this = this;
28382         
28383         window.addEventListener("resize", function() { _this.refresh(); } );
28384         
28385         this.fireEvent('initial', this);
28386     },
28387     
28388     renderProgressDialog : function()
28389     {
28390         var _this = this;
28391         
28392         this.progressDialog = new Roo.bootstrap.Modal({
28393             cls : 'roo-document-manager-progress-dialog',
28394             allow_close : false,
28395             title : '',
28396             buttons : [
28397                 {
28398                     name  :'cancel',
28399                     weight : 'danger',
28400                     html : 'Cancel'
28401                 }
28402             ], 
28403             listeners : { 
28404                 btnclick : function() {
28405                     _this.uploadCancel();
28406                     this.hide();
28407                 }
28408             }
28409         });
28410          
28411         this.progressDialog.render(Roo.get(document.body));
28412          
28413         this.progress = new Roo.bootstrap.Progress({
28414             cls : 'roo-document-manager-progress',
28415             active : true,
28416             striped : true
28417         });
28418         
28419         this.progress.render(this.progressDialog.getChildContainer());
28420         
28421         this.progressBar = new Roo.bootstrap.ProgressBar({
28422             cls : 'roo-document-manager-progress-bar',
28423             aria_valuenow : 0,
28424             aria_valuemin : 0,
28425             aria_valuemax : 12,
28426             panel : 'success'
28427         });
28428         
28429         this.progressBar.render(this.progress.getChildContainer());
28430     },
28431     
28432     onUploaderClick : function(e)
28433     {
28434         e.preventDefault();
28435      
28436         if(this.fireEvent('beforeselectfile', this) != false){
28437             this.selectorEl.dom.click();
28438         }
28439         
28440     },
28441     
28442     onFileSelected : function(e)
28443     {
28444         e.preventDefault();
28445         
28446         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28447             return;
28448         }
28449         
28450         Roo.each(this.selectorEl.dom.files, function(file){
28451             if(this.fireEvent('inspect', this, file) != false){
28452                 this.files.push(file);
28453             }
28454         }, this);
28455         
28456         this.queue();
28457         
28458     },
28459     
28460     queue : function()
28461     {
28462         this.selectorEl.dom.value = '';
28463         
28464         if(!this.files.length){
28465             return;
28466         }
28467         
28468         if(this.boxes > 0 && this.files.length > this.boxes){
28469             this.files = this.files.slice(0, this.boxes);
28470         }
28471         
28472         this.uploader.show();
28473         
28474         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28475             this.uploader.hide();
28476         }
28477         
28478         var _this = this;
28479         
28480         var files = [];
28481         
28482         var docs = [];
28483         
28484         Roo.each(this.files, function(file){
28485             
28486             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28487                 var f = this.renderPreview(file);
28488                 files.push(f);
28489                 return;
28490             }
28491             
28492             if(file.type.indexOf('image') != -1){
28493                 this.delegates.push(
28494                     (function(){
28495                         _this.process(file);
28496                     }).createDelegate(this)
28497                 );
28498         
28499                 return;
28500             }
28501             
28502             docs.push(
28503                 (function(){
28504                     _this.process(file);
28505                 }).createDelegate(this)
28506             );
28507             
28508         }, this);
28509         
28510         this.files = files;
28511         
28512         this.delegates = this.delegates.concat(docs);
28513         
28514         if(!this.delegates.length){
28515             this.refresh();
28516             return;
28517         }
28518         
28519         this.progressBar.aria_valuemax = this.delegates.length;
28520         
28521         this.arrange();
28522         
28523         return;
28524     },
28525     
28526     arrange : function()
28527     {
28528         if(!this.delegates.length){
28529             this.progressDialog.hide();
28530             this.refresh();
28531             return;
28532         }
28533         
28534         var delegate = this.delegates.shift();
28535         
28536         this.progressDialog.show();
28537         
28538         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28539         
28540         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28541         
28542         delegate();
28543     },
28544     
28545     refresh : function()
28546     {
28547         this.uploader.show();
28548         
28549         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28550             this.uploader.hide();
28551         }
28552         
28553         Roo.isTouch ? this.closable(false) : this.closable(true);
28554         
28555         this.fireEvent('refresh', this);
28556     },
28557     
28558     onRemove : function(e, el, o)
28559     {
28560         e.preventDefault();
28561         
28562         this.fireEvent('remove', this, o);
28563         
28564     },
28565     
28566     remove : function(o)
28567     {
28568         var files = [];
28569         
28570         Roo.each(this.files, function(file){
28571             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28572                 files.push(file);
28573                 return;
28574             }
28575
28576             o.target.remove();
28577
28578         }, this);
28579         
28580         this.files = files;
28581         
28582         this.refresh();
28583     },
28584     
28585     clear : function()
28586     {
28587         Roo.each(this.files, function(file){
28588             if(!file.target){
28589                 return;
28590             }
28591             
28592             file.target.remove();
28593
28594         }, this);
28595         
28596         this.files = [];
28597         
28598         this.refresh();
28599     },
28600     
28601     onClick : function(e, el, o)
28602     {
28603         e.preventDefault();
28604         
28605         this.fireEvent('click', this, o);
28606         
28607     },
28608     
28609     closable : function(closable)
28610     {
28611         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
28612             
28613             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28614             
28615             if(closable){
28616                 el.show();
28617                 return;
28618             }
28619             
28620             el.hide();
28621             
28622         }, this);
28623     },
28624     
28625     xhrOnLoad : function(xhr)
28626     {
28627         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28628             el.remove();
28629         }, this);
28630         
28631         if (xhr.readyState !== 4) {
28632             this.arrange();
28633             this.fireEvent('exception', this, xhr);
28634             return;
28635         }
28636
28637         var response = Roo.decode(xhr.responseText);
28638         
28639         if(!response.success){
28640             this.arrange();
28641             this.fireEvent('exception', this, xhr);
28642             return;
28643         }
28644         
28645         var file = this.renderPreview(response.data);
28646         
28647         this.files.push(file);
28648         
28649         this.arrange();
28650         
28651         this.fireEvent('afterupload', this, xhr);
28652         
28653     },
28654     
28655     xhrOnError : function(xhr)
28656     {
28657         Roo.log('xhr on error');
28658         
28659         var response = Roo.decode(xhr.responseText);
28660           
28661         Roo.log(response);
28662         
28663         this.arrange();
28664     },
28665     
28666     process : function(file)
28667     {
28668         if(this.fireEvent('process', this, file) !== false){
28669             if(this.editable && file.type.indexOf('image') != -1){
28670                 this.fireEvent('edit', this, file);
28671                 return;
28672             }
28673
28674             this.uploadStart(file, false);
28675
28676             return;
28677         }
28678         
28679     },
28680     
28681     uploadStart : function(file, crop)
28682     {
28683         this.xhr = new XMLHttpRequest();
28684         
28685         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28686             this.arrange();
28687             return;
28688         }
28689         
28690         file.xhr = this.xhr;
28691             
28692         this.managerEl.createChild({
28693             tag : 'div',
28694             cls : 'roo-document-manager-loading',
28695             cn : [
28696                 {
28697                     tag : 'div',
28698                     tooltip : file.name,
28699                     cls : 'roo-document-manager-thumb',
28700                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28701                 }
28702             ]
28703
28704         });
28705
28706         this.xhr.open(this.method, this.url, true);
28707         
28708         var headers = {
28709             "Accept": "application/json",
28710             "Cache-Control": "no-cache",
28711             "X-Requested-With": "XMLHttpRequest"
28712         };
28713         
28714         for (var headerName in headers) {
28715             var headerValue = headers[headerName];
28716             if (headerValue) {
28717                 this.xhr.setRequestHeader(headerName, headerValue);
28718             }
28719         }
28720         
28721         var _this = this;
28722         
28723         this.xhr.onload = function()
28724         {
28725             _this.xhrOnLoad(_this.xhr);
28726         }
28727         
28728         this.xhr.onerror = function()
28729         {
28730             _this.xhrOnError(_this.xhr);
28731         }
28732         
28733         var formData = new FormData();
28734
28735         formData.append('returnHTML', 'NO');
28736         
28737         if(crop){
28738             formData.append('crop', crop);
28739         }
28740         
28741         formData.append(this.paramName, file, file.name);
28742         
28743         var options = {
28744             file : file, 
28745             manually : false
28746         };
28747         
28748         if(this.fireEvent('prepare', this, formData, options) != false){
28749             
28750             if(options.manually){
28751                 return;
28752             }
28753             
28754             this.xhr.send(formData);
28755             return;
28756         };
28757         
28758         this.uploadCancel();
28759     },
28760     
28761     uploadCancel : function()
28762     {
28763         if (this.xhr) {
28764             this.xhr.abort();
28765         }
28766         
28767         this.delegates = [];
28768         
28769         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28770             el.remove();
28771         }, this);
28772         
28773         this.arrange();
28774     },
28775     
28776     renderPreview : function(file)
28777     {
28778         if(typeof(file.target) != 'undefined' && file.target){
28779             return file;
28780         }
28781         
28782         var previewEl = this.managerEl.createChild({
28783             tag : 'div',
28784             cls : 'roo-document-manager-preview',
28785             cn : [
28786                 {
28787                     tag : 'div',
28788                     tooltip : file[this.toolTipName],
28789                     cls : 'roo-document-manager-thumb',
28790                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
28791                 },
28792                 {
28793                     tag : 'button',
28794                     cls : 'close',
28795                     html : '<i class="fa fa-times-circle"></i>'
28796                 }
28797             ]
28798         });
28799
28800         var close = previewEl.select('button.close', true).first();
28801
28802         close.on('click', this.onRemove, this, file);
28803
28804         file.target = previewEl;
28805
28806         var image = previewEl.select('img', true).first();
28807         
28808         var _this = this;
28809         
28810         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28811         
28812         image.on('click', this.onClick, this, file);
28813         
28814         return file;
28815         
28816     },
28817     
28818     onPreviewLoad : function(file, image)
28819     {
28820         if(typeof(file.target) == 'undefined' || !file.target){
28821             return;
28822         }
28823         
28824         var width = image.dom.naturalWidth || image.dom.width;
28825         var height = image.dom.naturalHeight || image.dom.height;
28826         
28827         if(width > height){
28828             file.target.addClass('wide');
28829             return;
28830         }
28831         
28832         file.target.addClass('tall');
28833         return;
28834         
28835     },
28836     
28837     uploadFromSource : function(file, crop)
28838     {
28839         this.xhr = new XMLHttpRequest();
28840         
28841         this.managerEl.createChild({
28842             tag : 'div',
28843             cls : 'roo-document-manager-loading',
28844             cn : [
28845                 {
28846                     tag : 'div',
28847                     tooltip : file.name,
28848                     cls : 'roo-document-manager-thumb',
28849                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28850                 }
28851             ]
28852
28853         });
28854
28855         this.xhr.open(this.method, this.url, true);
28856         
28857         var headers = {
28858             "Accept": "application/json",
28859             "Cache-Control": "no-cache",
28860             "X-Requested-With": "XMLHttpRequest"
28861         };
28862         
28863         for (var headerName in headers) {
28864             var headerValue = headers[headerName];
28865             if (headerValue) {
28866                 this.xhr.setRequestHeader(headerName, headerValue);
28867             }
28868         }
28869         
28870         var _this = this;
28871         
28872         this.xhr.onload = function()
28873         {
28874             _this.xhrOnLoad(_this.xhr);
28875         }
28876         
28877         this.xhr.onerror = function()
28878         {
28879             _this.xhrOnError(_this.xhr);
28880         }
28881         
28882         var formData = new FormData();
28883
28884         formData.append('returnHTML', 'NO');
28885         
28886         formData.append('crop', crop);
28887         
28888         if(typeof(file.filename) != 'undefined'){
28889             formData.append('filename', file.filename);
28890         }
28891         
28892         if(typeof(file.mimetype) != 'undefined'){
28893             formData.append('mimetype', file.mimetype);
28894         }
28895         
28896         Roo.log(formData);
28897         
28898         if(this.fireEvent('prepare', this, formData) != false){
28899             this.xhr.send(formData);
28900         };
28901     }
28902 });
28903
28904 /*
28905 * Licence: LGPL
28906 */
28907
28908 /**
28909  * @class Roo.bootstrap.DocumentViewer
28910  * @extends Roo.bootstrap.Component
28911  * Bootstrap DocumentViewer class
28912  * @cfg {Boolean} showDownload (true|false) show download button (default true)
28913  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
28914  * 
28915  * @constructor
28916  * Create a new DocumentViewer
28917  * @param {Object} config The config object
28918  */
28919
28920 Roo.bootstrap.DocumentViewer = function(config){
28921     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
28922     
28923     this.addEvents({
28924         /**
28925          * @event initial
28926          * Fire after initEvent
28927          * @param {Roo.bootstrap.DocumentViewer} this
28928          */
28929         "initial" : true,
28930         /**
28931          * @event click
28932          * Fire after click
28933          * @param {Roo.bootstrap.DocumentViewer} this
28934          */
28935         "click" : true,
28936         /**
28937          * @event download
28938          * Fire after download button
28939          * @param {Roo.bootstrap.DocumentViewer} this
28940          */
28941         "download" : true,
28942         /**
28943          * @event trash
28944          * Fire after trash button
28945          * @param {Roo.bootstrap.DocumentViewer} this
28946          */
28947         "trash" : true
28948         
28949     });
28950 };
28951
28952 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
28953     
28954     showDownload : true,
28955     
28956     showTrash : true,
28957     
28958     getAutoCreate : function()
28959     {
28960         var cfg = {
28961             tag : 'div',
28962             cls : 'roo-document-viewer',
28963             cn : [
28964                 {
28965                     tag : 'div',
28966                     cls : 'roo-document-viewer-body',
28967                     cn : [
28968                         {
28969                             tag : 'div',
28970                             cls : 'roo-document-viewer-thumb',
28971                             cn : [
28972                                 {
28973                                     tag : 'img',
28974                                     cls : 'roo-document-viewer-image'
28975                                 }
28976                             ]
28977                         }
28978                     ]
28979                 },
28980                 {
28981                     tag : 'div',
28982                     cls : 'roo-document-viewer-footer',
28983                     cn : {
28984                         tag : 'div',
28985                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
28986                         cn : [
28987                             {
28988                                 tag : 'div',
28989                                 cls : 'btn-group roo-document-viewer-download',
28990                                 cn : [
28991                                     {
28992                                         tag : 'button',
28993                                         cls : 'btn btn-default',
28994                                         html : '<i class="fa fa-download"></i>'
28995                                     }
28996                                 ]
28997                             },
28998                             {
28999                                 tag : 'div',
29000                                 cls : 'btn-group roo-document-viewer-trash',
29001                                 cn : [
29002                                     {
29003                                         tag : 'button',
29004                                         cls : 'btn btn-default',
29005                                         html : '<i class="fa fa-trash"></i>'
29006                                     }
29007                                 ]
29008                             }
29009                         ]
29010                     }
29011                 }
29012             ]
29013         };
29014         
29015         return cfg;
29016     },
29017     
29018     initEvents : function()
29019     {
29020         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29021         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29022         
29023         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29024         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29025         
29026         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29027         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29028         
29029         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29030         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29031         
29032         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29033         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29034         
29035         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29036         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29037         
29038         this.bodyEl.on('click', this.onClick, this);
29039         this.downloadBtn.on('click', this.onDownload, this);
29040         this.trashBtn.on('click', this.onTrash, this);
29041         
29042         this.downloadBtn.hide();
29043         this.trashBtn.hide();
29044         
29045         if(this.showDownload){
29046             this.downloadBtn.show();
29047         }
29048         
29049         if(this.showTrash){
29050             this.trashBtn.show();
29051         }
29052         
29053         if(!this.showDownload && !this.showTrash) {
29054             this.footerEl.hide();
29055         }
29056         
29057     },
29058     
29059     initial : function()
29060     {
29061         this.fireEvent('initial', this);
29062         
29063     },
29064     
29065     onClick : function(e)
29066     {
29067         e.preventDefault();
29068         
29069         this.fireEvent('click', this);
29070     },
29071     
29072     onDownload : function(e)
29073     {
29074         e.preventDefault();
29075         
29076         this.fireEvent('download', this);
29077     },
29078     
29079     onTrash : function(e)
29080     {
29081         e.preventDefault();
29082         
29083         this.fireEvent('trash', this);
29084     }
29085     
29086 });
29087 /*
29088  * - LGPL
29089  *
29090  * nav progress bar
29091  * 
29092  */
29093
29094 /**
29095  * @class Roo.bootstrap.NavProgressBar
29096  * @extends Roo.bootstrap.Component
29097  * Bootstrap NavProgressBar class
29098  * 
29099  * @constructor
29100  * Create a new nav progress bar
29101  * @param {Object} config The config object
29102  */
29103
29104 Roo.bootstrap.NavProgressBar = function(config){
29105     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29106
29107     this.bullets = this.bullets || [];
29108    
29109 //    Roo.bootstrap.NavProgressBar.register(this);
29110      this.addEvents({
29111         /**
29112              * @event changed
29113              * Fires when the active item changes
29114              * @param {Roo.bootstrap.NavProgressBar} this
29115              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29116              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29117          */
29118         'changed': true
29119      });
29120     
29121 };
29122
29123 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29124     
29125     bullets : [],
29126     barItems : [],
29127     
29128     getAutoCreate : function()
29129     {
29130         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29131         
29132         cfg = {
29133             tag : 'div',
29134             cls : 'roo-navigation-bar-group',
29135             cn : [
29136                 {
29137                     tag : 'div',
29138                     cls : 'roo-navigation-top-bar'
29139                 },
29140                 {
29141                     tag : 'div',
29142                     cls : 'roo-navigation-bullets-bar',
29143                     cn : [
29144                         {
29145                             tag : 'ul',
29146                             cls : 'roo-navigation-bar'
29147                         }
29148                     ]
29149                 },
29150                 
29151                 {
29152                     tag : 'div',
29153                     cls : 'roo-navigation-bottom-bar'
29154                 }
29155             ]
29156             
29157         };
29158         
29159         return cfg;
29160         
29161     },
29162     
29163     initEvents: function() 
29164     {
29165         
29166     },
29167     
29168     onRender : function(ct, position) 
29169     {
29170         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29171         
29172         if(this.bullets.length){
29173             Roo.each(this.bullets, function(b){
29174                this.addItem(b);
29175             }, this);
29176         }
29177         
29178         this.format();
29179         
29180     },
29181     
29182     addItem : function(cfg)
29183     {
29184         var item = new Roo.bootstrap.NavProgressItem(cfg);
29185         
29186         item.parentId = this.id;
29187         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29188         
29189         if(cfg.html){
29190             var top = new Roo.bootstrap.Element({
29191                 tag : 'div',
29192                 cls : 'roo-navigation-bar-text'
29193             });
29194             
29195             var bottom = new Roo.bootstrap.Element({
29196                 tag : 'div',
29197                 cls : 'roo-navigation-bar-text'
29198             });
29199             
29200             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29201             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29202             
29203             var topText = new Roo.bootstrap.Element({
29204                 tag : 'span',
29205                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29206             });
29207             
29208             var bottomText = new Roo.bootstrap.Element({
29209                 tag : 'span',
29210                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29211             });
29212             
29213             topText.onRender(top.el, null);
29214             bottomText.onRender(bottom.el, null);
29215             
29216             item.topEl = top;
29217             item.bottomEl = bottom;
29218         }
29219         
29220         this.barItems.push(item);
29221         
29222         return item;
29223     },
29224     
29225     getActive : function()
29226     {
29227         var active = false;
29228         
29229         Roo.each(this.barItems, function(v){
29230             
29231             if (!v.isActive()) {
29232                 return;
29233             }
29234             
29235             active = v;
29236             return false;
29237             
29238         });
29239         
29240         return active;
29241     },
29242     
29243     setActiveItem : function(item)
29244     {
29245         var prev = false;
29246         
29247         Roo.each(this.barItems, function(v){
29248             if (v.rid == item.rid) {
29249                 return ;
29250             }
29251             
29252             if (v.isActive()) {
29253                 v.setActive(false);
29254                 prev = v;
29255             }
29256         });
29257
29258         item.setActive(true);
29259         
29260         this.fireEvent('changed', this, item, prev);
29261     },
29262     
29263     getBarItem: function(rid)
29264     {
29265         var ret = false;
29266         
29267         Roo.each(this.barItems, function(e) {
29268             if (e.rid != rid) {
29269                 return;
29270             }
29271             
29272             ret =  e;
29273             return false;
29274         });
29275         
29276         return ret;
29277     },
29278     
29279     indexOfItem : function(item)
29280     {
29281         var index = false;
29282         
29283         Roo.each(this.barItems, function(v, i){
29284             
29285             if (v.rid != item.rid) {
29286                 return;
29287             }
29288             
29289             index = i;
29290             return false
29291         });
29292         
29293         return index;
29294     },
29295     
29296     setActiveNext : function()
29297     {
29298         var i = this.indexOfItem(this.getActive());
29299         
29300         if (i > this.barItems.length) {
29301             return;
29302         }
29303         
29304         this.setActiveItem(this.barItems[i+1]);
29305     },
29306     
29307     setActivePrev : function()
29308     {
29309         var i = this.indexOfItem(this.getActive());
29310         
29311         if (i  < 1) {
29312             return;
29313         }
29314         
29315         this.setActiveItem(this.barItems[i-1]);
29316     },
29317     
29318     format : function()
29319     {
29320         if(!this.barItems.length){
29321             return;
29322         }
29323      
29324         var width = 100 / this.barItems.length;
29325         
29326         Roo.each(this.barItems, function(i){
29327             i.el.setStyle('width', width + '%');
29328             i.topEl.el.setStyle('width', width + '%');
29329             i.bottomEl.el.setStyle('width', width + '%');
29330         }, this);
29331         
29332     }
29333     
29334 });
29335 /*
29336  * - LGPL
29337  *
29338  * Nav Progress Item
29339  * 
29340  */
29341
29342 /**
29343  * @class Roo.bootstrap.NavProgressItem
29344  * @extends Roo.bootstrap.Component
29345  * Bootstrap NavProgressItem class
29346  * @cfg {String} rid the reference id
29347  * @cfg {Boolean} active (true|false) Is item active default false
29348  * @cfg {Boolean} disabled (true|false) Is item active default false
29349  * @cfg {String} html
29350  * @cfg {String} position (top|bottom) text position default bottom
29351  * @cfg {String} icon show icon instead of number
29352  * 
29353  * @constructor
29354  * Create a new NavProgressItem
29355  * @param {Object} config The config object
29356  */
29357 Roo.bootstrap.NavProgressItem = function(config){
29358     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29359     this.addEvents({
29360         // raw events
29361         /**
29362          * @event click
29363          * The raw click event for the entire grid.
29364          * @param {Roo.bootstrap.NavProgressItem} this
29365          * @param {Roo.EventObject} e
29366          */
29367         "click" : true
29368     });
29369    
29370 };
29371
29372 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29373     
29374     rid : '',
29375     active : false,
29376     disabled : false,
29377     html : '',
29378     position : 'bottom',
29379     icon : false,
29380     
29381     getAutoCreate : function()
29382     {
29383         var iconCls = 'roo-navigation-bar-item-icon';
29384         
29385         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29386         
29387         var cfg = {
29388             tag: 'li',
29389             cls: 'roo-navigation-bar-item',
29390             cn : [
29391                 {
29392                     tag : 'i',
29393                     cls : iconCls
29394                 }
29395             ]
29396         };
29397         
29398         if(this.active){
29399             cfg.cls += ' active';
29400         }
29401         if(this.disabled){
29402             cfg.cls += ' disabled';
29403         }
29404         
29405         return cfg;
29406     },
29407     
29408     disable : function()
29409     {
29410         this.setDisabled(true);
29411     },
29412     
29413     enable : function()
29414     {
29415         this.setDisabled(false);
29416     },
29417     
29418     initEvents: function() 
29419     {
29420         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29421         
29422         this.iconEl.on('click', this.onClick, this);
29423     },
29424     
29425     onClick : function(e)
29426     {
29427         e.preventDefault();
29428         
29429         if(this.disabled){
29430             return;
29431         }
29432         
29433         if(this.fireEvent('click', this, e) === false){
29434             return;
29435         };
29436         
29437         this.parent().setActiveItem(this);
29438     },
29439     
29440     isActive: function () 
29441     {
29442         return this.active;
29443     },
29444     
29445     setActive : function(state)
29446     {
29447         if(this.active == state){
29448             return;
29449         }
29450         
29451         this.active = state;
29452         
29453         if (state) {
29454             this.el.addClass('active');
29455             return;
29456         }
29457         
29458         this.el.removeClass('active');
29459         
29460         return;
29461     },
29462     
29463     setDisabled : function(state)
29464     {
29465         if(this.disabled == state){
29466             return;
29467         }
29468         
29469         this.disabled = state;
29470         
29471         if (state) {
29472             this.el.addClass('disabled');
29473             return;
29474         }
29475         
29476         this.el.removeClass('disabled');
29477     },
29478     
29479     tooltipEl : function()
29480     {
29481         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29482     }
29483 });
29484  
29485
29486  /*
29487  * - LGPL
29488  *
29489  * FieldLabel
29490  * 
29491  */
29492
29493 /**
29494  * @class Roo.bootstrap.FieldLabel
29495  * @extends Roo.bootstrap.Component
29496  * Bootstrap FieldLabel class
29497  * @cfg {String} html contents of the element
29498  * @cfg {String} tag tag of the element default label
29499  * @cfg {String} cls class of the element
29500  * @cfg {String} target label target 
29501  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29502  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
29503  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
29504  * @cfg {String} iconTooltip default "This field is required"
29505  * 
29506  * @constructor
29507  * Create a new FieldLabel
29508  * @param {Object} config The config object
29509  */
29510
29511 Roo.bootstrap.FieldLabel = function(config){
29512     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29513     
29514     this.addEvents({
29515             /**
29516              * @event invalid
29517              * Fires after the field has been marked as invalid.
29518              * @param {Roo.form.FieldLabel} this
29519              * @param {String} msg The validation message
29520              */
29521             invalid : true,
29522             /**
29523              * @event valid
29524              * Fires after the field has been validated with no errors.
29525              * @param {Roo.form.FieldLabel} this
29526              */
29527             valid : true
29528         });
29529 };
29530
29531 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29532     
29533     tag: 'label',
29534     cls: '',
29535     html: '',
29536     target: '',
29537     allowBlank : true,
29538     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
29539     validClass : 'text-success fa fa-lg fa-check',
29540     iconTooltip : 'This field is required',
29541     
29542     getAutoCreate : function(){
29543         
29544         var cfg = {
29545             tag : this.tag,
29546             cls : 'roo-bootstrap-field-label ' + this.cls,
29547             for : this.target,
29548             cn : [
29549                 {
29550                     tag : 'i',
29551                     cls : '',
29552                     tooltip : this.iconTooltip
29553                 },
29554                 {
29555                     tag : 'span',
29556                     html : this.html
29557                 }
29558             ] 
29559         };
29560         
29561         return cfg;
29562     },
29563     
29564     initEvents: function() 
29565     {
29566         Roo.bootstrap.Element.superclass.initEvents.call(this);
29567         
29568         this.iconEl = this.el.select('i', true).first();
29569         
29570         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
29571         
29572         Roo.bootstrap.FieldLabel.register(this);
29573     },
29574     
29575     /**
29576      * Mark this field as valid
29577      */
29578     markValid : function()
29579     {
29580         this.iconEl.show();
29581         
29582         this.iconEl.removeClass(this.invalidClass);
29583         
29584         this.iconEl.addClass(this.validClass);
29585         
29586         this.fireEvent('valid', this);
29587     },
29588     
29589     /**
29590      * Mark this field as invalid
29591      * @param {String} msg The validation message
29592      */
29593     markInvalid : function(msg)
29594     {
29595         this.iconEl.show();
29596         
29597         this.iconEl.removeClass(this.validClass);
29598         
29599         this.iconEl.addClass(this.invalidClass);
29600         
29601         this.fireEvent('invalid', this, msg);
29602     }
29603     
29604    
29605 });
29606
29607 Roo.apply(Roo.bootstrap.FieldLabel, {
29608     
29609     groups: {},
29610     
29611      /**
29612     * register a FieldLabel Group
29613     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
29614     */
29615     register : function(label)
29616     {
29617         if(this.groups.hasOwnProperty(label.target)){
29618             return;
29619         }
29620      
29621         this.groups[label.target] = label;
29622         
29623     },
29624     /**
29625     * fetch a FieldLabel Group based on the target
29626     * @param {string} target
29627     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
29628     */
29629     get: function(target) {
29630         if (typeof(this.groups[target]) == 'undefined') {
29631             return false;
29632         }
29633         
29634         return this.groups[target] ;
29635     }
29636 });
29637
29638  
29639
29640  /*
29641  * - LGPL
29642  *
29643  * page DateSplitField.
29644  * 
29645  */
29646
29647
29648 /**
29649  * @class Roo.bootstrap.DateSplitField
29650  * @extends Roo.bootstrap.Component
29651  * Bootstrap DateSplitField class
29652  * @cfg {string} fieldLabel - the label associated
29653  * @cfg {Number} labelWidth set the width of label (0-12)
29654  * @cfg {String} labelAlign (top|left)
29655  * @cfg {Boolean} dayAllowBlank (true|false) default false
29656  * @cfg {Boolean} monthAllowBlank (true|false) default false
29657  * @cfg {Boolean} yearAllowBlank (true|false) default false
29658  * @cfg {string} dayPlaceholder 
29659  * @cfg {string} monthPlaceholder
29660  * @cfg {string} yearPlaceholder
29661  * @cfg {string} dayFormat default 'd'
29662  * @cfg {string} monthFormat default 'm'
29663  * @cfg {string} yearFormat default 'Y'
29664  * @cfg {Number} labellg set the width of label (1-12)
29665  * @cfg {Number} labelmd set the width of label (1-12)
29666  * @cfg {Number} labelsm set the width of label (1-12)
29667  * @cfg {Number} labelxs set the width of label (1-12)
29668
29669  *     
29670  * @constructor
29671  * Create a new DateSplitField
29672  * @param {Object} config The config object
29673  */
29674
29675 Roo.bootstrap.DateSplitField = function(config){
29676     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
29677     
29678     this.addEvents({
29679         // raw events
29680          /**
29681          * @event years
29682          * getting the data of years
29683          * @param {Roo.bootstrap.DateSplitField} this
29684          * @param {Object} years
29685          */
29686         "years" : true,
29687         /**
29688          * @event days
29689          * getting the data of days
29690          * @param {Roo.bootstrap.DateSplitField} this
29691          * @param {Object} days
29692          */
29693         "days" : true,
29694         /**
29695          * @event invalid
29696          * Fires after the field has been marked as invalid.
29697          * @param {Roo.form.Field} this
29698          * @param {String} msg The validation message
29699          */
29700         invalid : true,
29701        /**
29702          * @event valid
29703          * Fires after the field has been validated with no errors.
29704          * @param {Roo.form.Field} this
29705          */
29706         valid : true
29707     });
29708 };
29709
29710 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
29711     
29712     fieldLabel : '',
29713     labelAlign : 'top',
29714     labelWidth : 3,
29715     dayAllowBlank : false,
29716     monthAllowBlank : false,
29717     yearAllowBlank : false,
29718     dayPlaceholder : '',
29719     monthPlaceholder : '',
29720     yearPlaceholder : '',
29721     dayFormat : 'd',
29722     monthFormat : 'm',
29723     yearFormat : 'Y',
29724     isFormField : true,
29725     labellg : 0,
29726     labelmd : 0,
29727     labelsm : 0,
29728     labelxs : 0,
29729     
29730     getAutoCreate : function()
29731     {
29732         var cfg = {
29733             tag : 'div',
29734             cls : 'row roo-date-split-field-group',
29735             cn : [
29736                 {
29737                     tag : 'input',
29738                     type : 'hidden',
29739                     cls : 'form-hidden-field roo-date-split-field-group-value',
29740                     name : this.name
29741                 }
29742             ]
29743         };
29744         
29745         var labelCls = 'col-md-12';
29746         var contentCls = 'col-md-4';
29747         
29748         if(this.fieldLabel){
29749             
29750             var label = {
29751                 tag : 'div',
29752                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
29753                 cn : [
29754                     {
29755                         tag : 'label',
29756                         html : this.fieldLabel
29757                     }
29758                 ]
29759             };
29760             
29761             if(this.labelAlign == 'left'){
29762             
29763                 if(this.labelWidth > 12){
29764                     label.style = "width: " + this.labelWidth + 'px';
29765                 }
29766
29767                 if(this.labelWidth < 13 && this.labelmd == 0){
29768                     this.labelmd = this.labelWidth;
29769                 }
29770
29771                 if(this.labellg > 0){
29772                     labelCls = ' col-lg-' + this.labellg;
29773                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
29774                 }
29775
29776                 if(this.labelmd > 0){
29777                     labelCls = ' col-md-' + this.labelmd;
29778                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
29779                 }
29780
29781                 if(this.labelsm > 0){
29782                     labelCls = ' col-sm-' + this.labelsm;
29783                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
29784                 }
29785
29786                 if(this.labelxs > 0){
29787                     labelCls = ' col-xs-' + this.labelxs;
29788                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
29789                 }
29790             }
29791             
29792             label.cls += ' ' + labelCls;
29793             
29794             cfg.cn.push(label);
29795         }
29796         
29797         Roo.each(['day', 'month', 'year'], function(t){
29798             cfg.cn.push({
29799                 tag : 'div',
29800                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
29801             });
29802         }, this);
29803         
29804         return cfg;
29805     },
29806     
29807     inputEl: function ()
29808     {
29809         return this.el.select('.roo-date-split-field-group-value', true).first();
29810     },
29811     
29812     onRender : function(ct, position) 
29813     {
29814         var _this = this;
29815         
29816         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29817         
29818         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
29819         
29820         this.dayField = new Roo.bootstrap.ComboBox({
29821             allowBlank : this.dayAllowBlank,
29822             alwaysQuery : true,
29823             displayField : 'value',
29824             editable : false,
29825             fieldLabel : '',
29826             forceSelection : true,
29827             mode : 'local',
29828             placeholder : this.dayPlaceholder,
29829             selectOnFocus : true,
29830             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29831             triggerAction : 'all',
29832             typeAhead : true,
29833             valueField : 'value',
29834             store : new Roo.data.SimpleStore({
29835                 data : (function() {    
29836                     var days = [];
29837                     _this.fireEvent('days', _this, days);
29838                     return days;
29839                 })(),
29840                 fields : [ 'value' ]
29841             }),
29842             listeners : {
29843                 select : function (_self, record, index)
29844                 {
29845                     _this.setValue(_this.getValue());
29846                 }
29847             }
29848         });
29849
29850         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
29851         
29852         this.monthField = new Roo.bootstrap.MonthField({
29853             after : '<i class=\"fa fa-calendar\"></i>',
29854             allowBlank : this.monthAllowBlank,
29855             placeholder : this.monthPlaceholder,
29856             readOnly : true,
29857             listeners : {
29858                 render : function (_self)
29859                 {
29860                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
29861                         e.preventDefault();
29862                         _self.focus();
29863                     });
29864                 },
29865                 select : function (_self, oldvalue, newvalue)
29866                 {
29867                     _this.setValue(_this.getValue());
29868                 }
29869             }
29870         });
29871         
29872         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
29873         
29874         this.yearField = new Roo.bootstrap.ComboBox({
29875             allowBlank : this.yearAllowBlank,
29876             alwaysQuery : true,
29877             displayField : 'value',
29878             editable : false,
29879             fieldLabel : '',
29880             forceSelection : true,
29881             mode : 'local',
29882             placeholder : this.yearPlaceholder,
29883             selectOnFocus : true,
29884             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29885             triggerAction : 'all',
29886             typeAhead : true,
29887             valueField : 'value',
29888             store : new Roo.data.SimpleStore({
29889                 data : (function() {
29890                     var years = [];
29891                     _this.fireEvent('years', _this, years);
29892                     return years;
29893                 })(),
29894                 fields : [ 'value' ]
29895             }),
29896             listeners : {
29897                 select : function (_self, record, index)
29898                 {
29899                     _this.setValue(_this.getValue());
29900                 }
29901             }
29902         });
29903
29904         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
29905     },
29906     
29907     setValue : function(v, format)
29908     {
29909         this.inputEl.dom.value = v;
29910         
29911         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
29912         
29913         var d = Date.parseDate(v, f);
29914         
29915         if(!d){
29916             this.validate();
29917             return;
29918         }
29919         
29920         this.setDay(d.format(this.dayFormat));
29921         this.setMonth(d.format(this.monthFormat));
29922         this.setYear(d.format(this.yearFormat));
29923         
29924         this.validate();
29925         
29926         return;
29927     },
29928     
29929     setDay : function(v)
29930     {
29931         this.dayField.setValue(v);
29932         this.inputEl.dom.value = this.getValue();
29933         this.validate();
29934         return;
29935     },
29936     
29937     setMonth : function(v)
29938     {
29939         this.monthField.setValue(v, true);
29940         this.inputEl.dom.value = this.getValue();
29941         this.validate();
29942         return;
29943     },
29944     
29945     setYear : function(v)
29946     {
29947         this.yearField.setValue(v);
29948         this.inputEl.dom.value = this.getValue();
29949         this.validate();
29950         return;
29951     },
29952     
29953     getDay : function()
29954     {
29955         return this.dayField.getValue();
29956     },
29957     
29958     getMonth : function()
29959     {
29960         return this.monthField.getValue();
29961     },
29962     
29963     getYear : function()
29964     {
29965         return this.yearField.getValue();
29966     },
29967     
29968     getValue : function()
29969     {
29970         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
29971         
29972         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
29973         
29974         return date;
29975     },
29976     
29977     reset : function()
29978     {
29979         this.setDay('');
29980         this.setMonth('');
29981         this.setYear('');
29982         this.inputEl.dom.value = '';
29983         this.validate();
29984         return;
29985     },
29986     
29987     validate : function()
29988     {
29989         var d = this.dayField.validate();
29990         var m = this.monthField.validate();
29991         var y = this.yearField.validate();
29992         
29993         var valid = true;
29994         
29995         if(
29996                 (!this.dayAllowBlank && !d) ||
29997                 (!this.monthAllowBlank && !m) ||
29998                 (!this.yearAllowBlank && !y)
29999         ){
30000             valid = false;
30001         }
30002         
30003         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30004             return valid;
30005         }
30006         
30007         if(valid){
30008             this.markValid();
30009             return valid;
30010         }
30011         
30012         this.markInvalid();
30013         
30014         return valid;
30015     },
30016     
30017     markValid : function()
30018     {
30019         
30020         var label = this.el.select('label', true).first();
30021         var icon = this.el.select('i.fa-star', true).first();
30022
30023         if(label && icon){
30024             icon.remove();
30025         }
30026         
30027         this.fireEvent('valid', this);
30028     },
30029     
30030      /**
30031      * Mark this field as invalid
30032      * @param {String} msg The validation message
30033      */
30034     markInvalid : function(msg)
30035     {
30036         
30037         var label = this.el.select('label', true).first();
30038         var icon = this.el.select('i.fa-star', true).first();
30039
30040         if(label && !icon){
30041             this.el.select('.roo-date-split-field-label', true).createChild({
30042                 tag : 'i',
30043                 cls : 'text-danger fa fa-lg fa-star',
30044                 tooltip : 'This field is required',
30045                 style : 'margin-right:5px;'
30046             }, label, true);
30047         }
30048         
30049         this.fireEvent('invalid', this, msg);
30050     },
30051     
30052     clearInvalid : function()
30053     {
30054         var label = this.el.select('label', true).first();
30055         var icon = this.el.select('i.fa-star', true).first();
30056
30057         if(label && icon){
30058             icon.remove();
30059         }
30060         
30061         this.fireEvent('valid', this);
30062     },
30063     
30064     getName: function()
30065     {
30066         return this.name;
30067     }
30068     
30069 });
30070
30071  /**
30072  *
30073  * This is based on 
30074  * http://masonry.desandro.com
30075  *
30076  * The idea is to render all the bricks based on vertical width...
30077  *
30078  * The original code extends 'outlayer' - we might need to use that....
30079  * 
30080  */
30081
30082
30083 /**
30084  * @class Roo.bootstrap.LayoutMasonry
30085  * @extends Roo.bootstrap.Component
30086  * Bootstrap Layout Masonry class
30087  * 
30088  * @constructor
30089  * Create a new Element
30090  * @param {Object} config The config object
30091  */
30092
30093 Roo.bootstrap.LayoutMasonry = function(config){
30094     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30095     
30096     this.bricks = [];
30097     
30098     this.addEvents({
30099         // raw events
30100         /**
30101          * @event layout
30102          * Fire after layout the items
30103          * @param {Roo.bootstrap.LayoutMasonry} this
30104          * @param {Roo.EventObject} e
30105          */
30106         "layout" : true
30107     });
30108     
30109 };
30110
30111 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30112     
30113     /**
30114      * @cfg {Boolean} isLayoutInstant = no animation?
30115      */   
30116     isLayoutInstant : false, // needed?
30117    
30118     /**
30119      * @cfg {Number} boxWidth  width of the columns
30120      */   
30121     boxWidth : 450,
30122     
30123       /**
30124      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30125      */   
30126     boxHeight : 0,
30127     
30128     /**
30129      * @cfg {Number} padWidth padding below box..
30130      */   
30131     padWidth : 10, 
30132     
30133     /**
30134      * @cfg {Number} gutter gutter width..
30135      */   
30136     gutter : 10,
30137     
30138      /**
30139      * @cfg {Number} maxCols maximum number of columns
30140      */   
30141     
30142     maxCols: 0,
30143     
30144     /**
30145      * @cfg {Boolean} isAutoInitial defalut true
30146      */   
30147     isAutoInitial : true, 
30148     
30149     containerWidth: 0,
30150     
30151     /**
30152      * @cfg {Boolean} isHorizontal defalut false
30153      */   
30154     isHorizontal : false, 
30155
30156     currentSize : null,
30157     
30158     tag: 'div',
30159     
30160     cls: '',
30161     
30162     bricks: null, //CompositeElement
30163     
30164     cols : 1,
30165     
30166     _isLayoutInited : false,
30167     
30168 //    isAlternative : false, // only use for vertical layout...
30169     
30170     /**
30171      * @cfg {Number} alternativePadWidth padding below box..
30172      */   
30173     alternativePadWidth : 50, 
30174     
30175     getAutoCreate : function(){
30176         
30177         var cfg = {
30178             tag: this.tag,
30179             cls: 'blog-masonary-wrapper ' + this.cls,
30180             cn : {
30181                 cls : 'mas-boxes masonary'
30182             }
30183         };
30184         
30185         return cfg;
30186     },
30187     
30188     getChildContainer: function( )
30189     {
30190         if (this.boxesEl) {
30191             return this.boxesEl;
30192         }
30193         
30194         this.boxesEl = this.el.select('.mas-boxes').first();
30195         
30196         return this.boxesEl;
30197     },
30198     
30199     
30200     initEvents : function()
30201     {
30202         var _this = this;
30203         
30204         if(this.isAutoInitial){
30205             Roo.log('hook children rendered');
30206             this.on('childrenrendered', function() {
30207                 Roo.log('children rendered');
30208                 _this.initial();
30209             } ,this);
30210         }
30211     },
30212     
30213     initial : function()
30214     {
30215         this.currentSize = this.el.getBox(true);
30216         
30217         Roo.EventManager.onWindowResize(this.resize, this); 
30218
30219         if(!this.isAutoInitial){
30220             this.layout();
30221             return;
30222         }
30223         
30224         this.layout();
30225         
30226         return;
30227         //this.layout.defer(500,this);
30228         
30229     },
30230     
30231     resize : function()
30232     {
30233         var cs = this.el.getBox(true);
30234         
30235         if (
30236                 this.currentSize.width == cs.width && 
30237                 this.currentSize.x == cs.x && 
30238                 this.currentSize.height == cs.height && 
30239                 this.currentSize.y == cs.y 
30240         ) {
30241             Roo.log("no change in with or X or Y");
30242             return;
30243         }
30244         
30245         this.currentSize = cs;
30246         
30247         this.layout();
30248         
30249     },
30250     
30251     layout : function()
30252     {   
30253         this._resetLayout();
30254         
30255         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30256         
30257         this.layoutItems( isInstant );
30258       
30259         this._isLayoutInited = true;
30260         
30261         this.fireEvent('layout', this);
30262         
30263     },
30264     
30265     _resetLayout : function()
30266     {
30267         if(this.isHorizontal){
30268             this.horizontalMeasureColumns();
30269             return;
30270         }
30271         
30272         this.verticalMeasureColumns();
30273         
30274     },
30275     
30276     verticalMeasureColumns : function()
30277     {
30278         this.getContainerWidth();
30279         
30280 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30281 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30282 //            return;
30283 //        }
30284         
30285         var boxWidth = this.boxWidth + this.padWidth;
30286         
30287         if(this.containerWidth < this.boxWidth){
30288             boxWidth = this.containerWidth
30289         }
30290         
30291         var containerWidth = this.containerWidth;
30292         
30293         var cols = Math.floor(containerWidth / boxWidth);
30294         
30295         this.cols = Math.max( cols, 1 );
30296         
30297         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30298         
30299         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30300         
30301         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30302         
30303         this.colWidth = boxWidth + avail - this.padWidth;
30304         
30305         this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
30306         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30307     },
30308     
30309     horizontalMeasureColumns : function()
30310     {
30311         this.getContainerWidth();
30312         
30313         var boxWidth = this.boxWidth;
30314         
30315         if(this.containerWidth < boxWidth){
30316             boxWidth = this.containerWidth;
30317         }
30318         
30319         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30320         
30321         this.el.setHeight(boxWidth);
30322         
30323     },
30324     
30325     getContainerWidth : function()
30326     {
30327         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30328     },
30329     
30330     layoutItems : function( isInstant )
30331     {
30332         Roo.log(this.bricks);
30333         
30334         var items = Roo.apply([], this.bricks);
30335         
30336         if(this.isHorizontal){
30337             this._horizontalLayoutItems( items , isInstant );
30338             return;
30339         }
30340         
30341 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30342 //            this._verticalAlternativeLayoutItems( items , isInstant );
30343 //            return;
30344 //        }
30345         
30346         this._verticalLayoutItems( items , isInstant );
30347         
30348     },
30349     
30350     _verticalLayoutItems : function ( items , isInstant)
30351     {
30352         if ( !items || !items.length ) {
30353             return;
30354         }
30355         
30356         var standard = [
30357             ['xs', 'xs', 'xs', 'tall'],
30358             ['xs', 'xs', 'tall'],
30359             ['xs', 'xs', 'sm'],
30360             ['xs', 'xs', 'xs'],
30361             ['xs', 'tall'],
30362             ['xs', 'sm'],
30363             ['xs', 'xs'],
30364             ['xs'],
30365             
30366             ['sm', 'xs', 'xs'],
30367             ['sm', 'xs'],
30368             ['sm'],
30369             
30370             ['tall', 'xs', 'xs', 'xs'],
30371             ['tall', 'xs', 'xs'],
30372             ['tall', 'xs'],
30373             ['tall']
30374             
30375         ];
30376         
30377         var queue = [];
30378         
30379         var boxes = [];
30380         
30381         var box = [];
30382         
30383         Roo.each(items, function(item, k){
30384             
30385             switch (item.size) {
30386                 // these layouts take up a full box,
30387                 case 'md' :
30388                 case 'md-left' :
30389                 case 'md-right' :
30390                 case 'wide' :
30391                     
30392                     if(box.length){
30393                         boxes.push(box);
30394                         box = [];
30395                     }
30396                     
30397                     boxes.push([item]);
30398                     
30399                     break;
30400                     
30401                 case 'xs' :
30402                 case 'sm' :
30403                 case 'tall' :
30404                     
30405                     box.push(item);
30406                     
30407                     break;
30408                 default :
30409                     break;
30410                     
30411             }
30412             
30413         }, this);
30414         
30415         if(box.length){
30416             boxes.push(box);
30417             box = [];
30418         }
30419         
30420         var filterPattern = function(box, length)
30421         {
30422             if(!box.length){
30423                 return;
30424             }
30425             
30426             var match = false;
30427             
30428             var pattern = box.slice(0, length);
30429             
30430             var format = [];
30431             
30432             Roo.each(pattern, function(i){
30433                 format.push(i.size);
30434             }, this);
30435             
30436             Roo.each(standard, function(s){
30437                 
30438                 if(String(s) != String(format)){
30439                     return;
30440                 }
30441                 
30442                 match = true;
30443                 return false;
30444                 
30445             }, this);
30446             
30447             if(!match && length == 1){
30448                 return;
30449             }
30450             
30451             if(!match){
30452                 filterPattern(box, length - 1);
30453                 return;
30454             }
30455                 
30456             queue.push(pattern);
30457
30458             box = box.slice(length, box.length);
30459
30460             filterPattern(box, 4);
30461
30462             return;
30463             
30464         }
30465         
30466         Roo.each(boxes, function(box, k){
30467             
30468             if(!box.length){
30469                 return;
30470             }
30471             
30472             if(box.length == 1){
30473                 queue.push(box);
30474                 return;
30475             }
30476             
30477             filterPattern(box, 4);
30478             
30479         }, this);
30480         
30481         this._processVerticalLayoutQueue( queue, isInstant );
30482         
30483     },
30484     
30485 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30486 //    {
30487 //        if ( !items || !items.length ) {
30488 //            return;
30489 //        }
30490 //
30491 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
30492 //        
30493 //    },
30494     
30495     _horizontalLayoutItems : function ( items , isInstant)
30496     {
30497         if ( !items || !items.length || items.length < 3) {
30498             return;
30499         }
30500         
30501         items.reverse();
30502         
30503         var eItems = items.slice(0, 3);
30504         
30505         items = items.slice(3, items.length);
30506         
30507         var standard = [
30508             ['xs', 'xs', 'xs', 'wide'],
30509             ['xs', 'xs', 'wide'],
30510             ['xs', 'xs', 'sm'],
30511             ['xs', 'xs', 'xs'],
30512             ['xs', 'wide'],
30513             ['xs', 'sm'],
30514             ['xs', 'xs'],
30515             ['xs'],
30516             
30517             ['sm', 'xs', 'xs'],
30518             ['sm', 'xs'],
30519             ['sm'],
30520             
30521             ['wide', 'xs', 'xs', 'xs'],
30522             ['wide', 'xs', 'xs'],
30523             ['wide', 'xs'],
30524             ['wide'],
30525             
30526             ['wide-thin']
30527         ];
30528         
30529         var queue = [];
30530         
30531         var boxes = [];
30532         
30533         var box = [];
30534         
30535         Roo.each(items, function(item, k){
30536             
30537             switch (item.size) {
30538                 case 'md' :
30539                 case 'md-left' :
30540                 case 'md-right' :
30541                 case 'tall' :
30542                     
30543                     if(box.length){
30544                         boxes.push(box);
30545                         box = [];
30546                     }
30547                     
30548                     boxes.push([item]);
30549                     
30550                     break;
30551                     
30552                 case 'xs' :
30553                 case 'sm' :
30554                 case 'wide' :
30555                 case 'wide-thin' :
30556                     
30557                     box.push(item);
30558                     
30559                     break;
30560                 default :
30561                     break;
30562                     
30563             }
30564             
30565         }, this);
30566         
30567         if(box.length){
30568             boxes.push(box);
30569             box = [];
30570         }
30571         
30572         var filterPattern = function(box, length)
30573         {
30574             if(!box.length){
30575                 return;
30576             }
30577             
30578             var match = false;
30579             
30580             var pattern = box.slice(0, length);
30581             
30582             var format = [];
30583             
30584             Roo.each(pattern, function(i){
30585                 format.push(i.size);
30586             }, this);
30587             
30588             Roo.each(standard, function(s){
30589                 
30590                 if(String(s) != String(format)){
30591                     return;
30592                 }
30593                 
30594                 match = true;
30595                 return false;
30596                 
30597             }, this);
30598             
30599             if(!match && length == 1){
30600                 return;
30601             }
30602             
30603             if(!match){
30604                 filterPattern(box, length - 1);
30605                 return;
30606             }
30607                 
30608             queue.push(pattern);
30609
30610             box = box.slice(length, box.length);
30611
30612             filterPattern(box, 4);
30613
30614             return;
30615             
30616         }
30617         
30618         Roo.each(boxes, function(box, k){
30619             
30620             if(!box.length){
30621                 return;
30622             }
30623             
30624             if(box.length == 1){
30625                 queue.push(box);
30626                 return;
30627             }
30628             
30629             filterPattern(box, 4);
30630             
30631         }, this);
30632         
30633         
30634         var prune = [];
30635         
30636         var pos = this.el.getBox(true);
30637         
30638         var minX = pos.x;
30639         
30640         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30641         
30642         var hit_end = false;
30643         
30644         Roo.each(queue, function(box){
30645             
30646             if(hit_end){
30647                 
30648                 Roo.each(box, function(b){
30649                 
30650                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30651                     b.el.hide();
30652
30653                 }, this);
30654
30655                 return;
30656             }
30657             
30658             var mx = 0;
30659             
30660             Roo.each(box, function(b){
30661                 
30662                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30663                 b.el.show();
30664
30665                 mx = Math.max(mx, b.x);
30666                 
30667             }, this);
30668             
30669             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
30670             
30671             if(maxX < minX){
30672                 
30673                 Roo.each(box, function(b){
30674                 
30675                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30676                     b.el.hide();
30677                     
30678                 }, this);
30679                 
30680                 hit_end = true;
30681                 
30682                 return;
30683             }
30684             
30685             prune.push(box);
30686             
30687         }, this);
30688         
30689         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
30690     },
30691     
30692     /** Sets position of item in DOM
30693     * @param {Element} item
30694     * @param {Number} x - horizontal position
30695     * @param {Number} y - vertical position
30696     * @param {Boolean} isInstant - disables transitions
30697     */
30698     _processVerticalLayoutQueue : function( queue, isInstant )
30699     {
30700         var pos = this.el.getBox(true);
30701         var x = pos.x;
30702         var y = pos.y;
30703         var maxY = [];
30704         
30705         for (var i = 0; i < this.cols; i++){
30706             maxY[i] = pos.y;
30707         }
30708         
30709         Roo.each(queue, function(box, k){
30710             
30711             var col = k % this.cols;
30712             
30713             Roo.each(box, function(b,kk){
30714                 
30715                 b.el.position('absolute');
30716                 
30717                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30718                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30719                 
30720                 if(b.size == 'md-left' || b.size == 'md-right'){
30721                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30722                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30723                 }
30724                 
30725                 b.el.setWidth(width);
30726                 b.el.setHeight(height);
30727                 // iframe?
30728                 b.el.select('iframe',true).setSize(width,height);
30729                 
30730             }, this);
30731             
30732             for (var i = 0; i < this.cols; i++){
30733                 
30734                 if(maxY[i] < maxY[col]){
30735                     col = i;
30736                     continue;
30737                 }
30738                 
30739                 col = Math.min(col, i);
30740                 
30741             }
30742             
30743             x = pos.x + col * (this.colWidth + this.padWidth);
30744             
30745             y = maxY[col];
30746             
30747             var positions = [];
30748             
30749             switch (box.length){
30750                 case 1 :
30751                     positions = this.getVerticalOneBoxColPositions(x, y, box);
30752                     break;
30753                 case 2 :
30754                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
30755                     break;
30756                 case 3 :
30757                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
30758                     break;
30759                 case 4 :
30760                     positions = this.getVerticalFourBoxColPositions(x, y, box);
30761                     break;
30762                 default :
30763                     break;
30764             }
30765             
30766             Roo.each(box, function(b,kk){
30767                 
30768                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30769                 
30770                 var sz = b.el.getSize();
30771                 
30772                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
30773                 
30774             }, this);
30775             
30776         }, this);
30777         
30778         var mY = 0;
30779         
30780         for (var i = 0; i < this.cols; i++){
30781             mY = Math.max(mY, maxY[i]);
30782         }
30783         
30784         this.el.setHeight(mY - pos.y);
30785         
30786     },
30787     
30788 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
30789 //    {
30790 //        var pos = this.el.getBox(true);
30791 //        var x = pos.x;
30792 //        var y = pos.y;
30793 //        var maxX = pos.right;
30794 //        
30795 //        var maxHeight = 0;
30796 //        
30797 //        Roo.each(items, function(item, k){
30798 //            
30799 //            var c = k % 2;
30800 //            
30801 //            item.el.position('absolute');
30802 //                
30803 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
30804 //
30805 //            item.el.setWidth(width);
30806 //
30807 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
30808 //
30809 //            item.el.setHeight(height);
30810 //            
30811 //            if(c == 0){
30812 //                item.el.setXY([x, y], isInstant ? false : true);
30813 //            } else {
30814 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
30815 //            }
30816 //            
30817 //            y = y + height + this.alternativePadWidth;
30818 //            
30819 //            maxHeight = maxHeight + height + this.alternativePadWidth;
30820 //            
30821 //        }, this);
30822 //        
30823 //        this.el.setHeight(maxHeight);
30824 //        
30825 //    },
30826     
30827     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
30828     {
30829         var pos = this.el.getBox(true);
30830         
30831         var minX = pos.x;
30832         var minY = pos.y;
30833         
30834         var maxX = pos.right;
30835         
30836         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
30837         
30838         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30839         
30840         Roo.each(queue, function(box, k){
30841             
30842             Roo.each(box, function(b, kk){
30843                 
30844                 b.el.position('absolute');
30845                 
30846                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30847                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30848                 
30849                 if(b.size == 'md-left' || b.size == 'md-right'){
30850                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30851                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30852                 }
30853                 
30854                 b.el.setWidth(width);
30855                 b.el.setHeight(height);
30856                 
30857             }, this);
30858             
30859             if(!box.length){
30860                 return;
30861             }
30862             
30863             var positions = [];
30864             
30865             switch (box.length){
30866                 case 1 :
30867                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
30868                     break;
30869                 case 2 :
30870                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
30871                     break;
30872                 case 3 :
30873                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
30874                     break;
30875                 case 4 :
30876                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
30877                     break;
30878                 default :
30879                     break;
30880             }
30881             
30882             Roo.each(box, function(b,kk){
30883                 
30884                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30885                 
30886                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
30887                 
30888             }, this);
30889             
30890         }, this);
30891         
30892     },
30893     
30894     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
30895     {
30896         Roo.each(eItems, function(b,k){
30897             
30898             b.size = (k == 0) ? 'sm' : 'xs';
30899             b.x = (k == 0) ? 2 : 1;
30900             b.y = (k == 0) ? 2 : 1;
30901             
30902             b.el.position('absolute');
30903             
30904             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30905                 
30906             b.el.setWidth(width);
30907             
30908             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30909             
30910             b.el.setHeight(height);
30911             
30912         }, this);
30913
30914         var positions = [];
30915         
30916         positions.push({
30917             x : maxX - this.unitWidth * 2 - this.gutter,
30918             y : minY
30919         });
30920         
30921         positions.push({
30922             x : maxX - this.unitWidth,
30923             y : minY + (this.unitWidth + this.gutter) * 2
30924         });
30925         
30926         positions.push({
30927             x : maxX - this.unitWidth * 3 - this.gutter * 2,
30928             y : minY
30929         });
30930         
30931         Roo.each(eItems, function(b,k){
30932             
30933             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
30934
30935         }, this);
30936         
30937     },
30938     
30939     getVerticalOneBoxColPositions : function(x, y, box)
30940     {
30941         var pos = [];
30942         
30943         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
30944         
30945         if(box[0].size == 'md-left'){
30946             rand = 0;
30947         }
30948         
30949         if(box[0].size == 'md-right'){
30950             rand = 1;
30951         }
30952         
30953         pos.push({
30954             x : x + (this.unitWidth + this.gutter) * rand,
30955             y : y
30956         });
30957         
30958         return pos;
30959     },
30960     
30961     getVerticalTwoBoxColPositions : function(x, y, box)
30962     {
30963         var pos = [];
30964         
30965         if(box[0].size == 'xs'){
30966             
30967             pos.push({
30968                 x : x,
30969                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
30970             });
30971
30972             pos.push({
30973                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
30974                 y : y
30975             });
30976             
30977             return pos;
30978             
30979         }
30980         
30981         pos.push({
30982             x : x,
30983             y : y
30984         });
30985
30986         pos.push({
30987             x : x + (this.unitWidth + this.gutter) * 2,
30988             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
30989         });
30990         
30991         return pos;
30992         
30993     },
30994     
30995     getVerticalThreeBoxColPositions : function(x, y, box)
30996     {
30997         var pos = [];
30998         
30999         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31000             
31001             pos.push({
31002                 x : x,
31003                 y : y
31004             });
31005
31006             pos.push({
31007                 x : x + (this.unitWidth + this.gutter) * 1,
31008                 y : y
31009             });
31010             
31011             pos.push({
31012                 x : x + (this.unitWidth + this.gutter) * 2,
31013                 y : y
31014             });
31015             
31016             return pos;
31017             
31018         }
31019         
31020         if(box[0].size == 'xs' && box[1].size == 'xs'){
31021             
31022             pos.push({
31023                 x : x,
31024                 y : y
31025             });
31026
31027             pos.push({
31028                 x : x,
31029                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31030             });
31031             
31032             pos.push({
31033                 x : x + (this.unitWidth + this.gutter) * 1,
31034                 y : y
31035             });
31036             
31037             return pos;
31038             
31039         }
31040         
31041         pos.push({
31042             x : x,
31043             y : y
31044         });
31045
31046         pos.push({
31047             x : x + (this.unitWidth + this.gutter) * 2,
31048             y : y
31049         });
31050
31051         pos.push({
31052             x : x + (this.unitWidth + this.gutter) * 2,
31053             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31054         });
31055             
31056         return pos;
31057         
31058     },
31059     
31060     getVerticalFourBoxColPositions : function(x, y, box)
31061     {
31062         var pos = [];
31063         
31064         if(box[0].size == 'xs'){
31065             
31066             pos.push({
31067                 x : x,
31068                 y : y
31069             });
31070
31071             pos.push({
31072                 x : x,
31073                 y : y + (this.unitHeight + this.gutter) * 1
31074             });
31075             
31076             pos.push({
31077                 x : x,
31078                 y : y + (this.unitHeight + this.gutter) * 2
31079             });
31080             
31081             pos.push({
31082                 x : x + (this.unitWidth + this.gutter) * 1,
31083                 y : y
31084             });
31085             
31086             return pos;
31087             
31088         }
31089         
31090         pos.push({
31091             x : x,
31092             y : y
31093         });
31094
31095         pos.push({
31096             x : x + (this.unitWidth + this.gutter) * 2,
31097             y : y
31098         });
31099
31100         pos.push({
31101             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31102             y : y + (this.unitHeight + this.gutter) * 1
31103         });
31104
31105         pos.push({
31106             x : x + (this.unitWidth + this.gutter) * 2,
31107             y : y + (this.unitWidth + this.gutter) * 2
31108         });
31109
31110         return pos;
31111         
31112     },
31113     
31114     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31115     {
31116         var pos = [];
31117         
31118         if(box[0].size == 'md-left'){
31119             pos.push({
31120                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31121                 y : minY
31122             });
31123             
31124             return pos;
31125         }
31126         
31127         if(box[0].size == 'md-right'){
31128             pos.push({
31129                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31130                 y : minY + (this.unitWidth + this.gutter) * 1
31131             });
31132             
31133             return pos;
31134         }
31135         
31136         var rand = Math.floor(Math.random() * (4 - box[0].y));
31137         
31138         pos.push({
31139             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31140             y : minY + (this.unitWidth + this.gutter) * rand
31141         });
31142         
31143         return pos;
31144         
31145     },
31146     
31147     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31148     {
31149         var pos = [];
31150         
31151         if(box[0].size == 'xs'){
31152             
31153             pos.push({
31154                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31155                 y : minY
31156             });
31157
31158             pos.push({
31159                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31160                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31161             });
31162             
31163             return pos;
31164             
31165         }
31166         
31167         pos.push({
31168             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31169             y : minY
31170         });
31171
31172         pos.push({
31173             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31174             y : minY + (this.unitWidth + this.gutter) * 2
31175         });
31176         
31177         return pos;
31178         
31179     },
31180     
31181     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31182     {
31183         var pos = [];
31184         
31185         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31186             
31187             pos.push({
31188                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31189                 y : minY
31190             });
31191
31192             pos.push({
31193                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31194                 y : minY + (this.unitWidth + this.gutter) * 1
31195             });
31196             
31197             pos.push({
31198                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31199                 y : minY + (this.unitWidth + this.gutter) * 2
31200             });
31201             
31202             return pos;
31203             
31204         }
31205         
31206         if(box[0].size == 'xs' && box[1].size == 'xs'){
31207             
31208             pos.push({
31209                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31210                 y : minY
31211             });
31212
31213             pos.push({
31214                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31215                 y : minY
31216             });
31217             
31218             pos.push({
31219                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31220                 y : minY + (this.unitWidth + this.gutter) * 1
31221             });
31222             
31223             return pos;
31224             
31225         }
31226         
31227         pos.push({
31228             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31229             y : minY
31230         });
31231
31232         pos.push({
31233             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31234             y : minY + (this.unitWidth + this.gutter) * 2
31235         });
31236
31237         pos.push({
31238             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31239             y : minY + (this.unitWidth + this.gutter) * 2
31240         });
31241             
31242         return pos;
31243         
31244     },
31245     
31246     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31247     {
31248         var pos = [];
31249         
31250         if(box[0].size == 'xs'){
31251             
31252             pos.push({
31253                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31254                 y : minY
31255             });
31256
31257             pos.push({
31258                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31259                 y : minY
31260             });
31261             
31262             pos.push({
31263                 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),
31264                 y : minY
31265             });
31266             
31267             pos.push({
31268                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31269                 y : minY + (this.unitWidth + this.gutter) * 1
31270             });
31271             
31272             return pos;
31273             
31274         }
31275         
31276         pos.push({
31277             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31278             y : minY
31279         });
31280         
31281         pos.push({
31282             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31283             y : minY + (this.unitWidth + this.gutter) * 2
31284         });
31285         
31286         pos.push({
31287             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31288             y : minY + (this.unitWidth + this.gutter) * 2
31289         });
31290         
31291         pos.push({
31292             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),
31293             y : minY + (this.unitWidth + this.gutter) * 2
31294         });
31295
31296         return pos;
31297         
31298     },
31299     
31300     /**
31301     * adds a Masonry Brick
31302     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31303     */
31304     addItem : function(cfg)
31305     {
31306         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31307         //this.register(cn);
31308         cn.parentId = this.id;
31309         cn.onRender(this.el, null);
31310         return cn;
31311     },
31312     
31313     /**
31314     * register a Masonry Brick
31315     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31316     */
31317     register : function(brick)
31318     {
31319         this.bricks.push(brick);
31320         brick.masonryId = this.id;
31321     },
31322     
31323     /**
31324     * clear all the Masonry Brick
31325     */
31326     clearAll : function()
31327     {
31328         this.bricks = [];
31329         //this.getChildContainer().dom.innerHTML = "";
31330         this.el.dom.innerHTML = '';
31331     }
31332     
31333 });
31334
31335  
31336
31337  /**
31338  *
31339  * This is based on 
31340  * http://masonry.desandro.com
31341  *
31342  * The idea is to render all the bricks based on vertical width...
31343  *
31344  * The original code extends 'outlayer' - we might need to use that....
31345  * 
31346  */
31347
31348
31349 /**
31350  * @class Roo.bootstrap.LayoutMasonryAuto
31351  * @extends Roo.bootstrap.Component
31352  * Bootstrap Layout Masonry class
31353  * 
31354  * @constructor
31355  * Create a new Element
31356  * @param {Object} config The config object
31357  */
31358
31359 Roo.bootstrap.LayoutMasonryAuto = function(config){
31360     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31361 };
31362
31363 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31364     
31365       /**
31366      * @cfg {Boolean} isFitWidth  - resize the width..
31367      */   
31368     isFitWidth : false,  // options..
31369     /**
31370      * @cfg {Boolean} isOriginLeft = left align?
31371      */   
31372     isOriginLeft : true,
31373     /**
31374      * @cfg {Boolean} isOriginTop = top align?
31375      */   
31376     isOriginTop : false,
31377     /**
31378      * @cfg {Boolean} isLayoutInstant = no animation?
31379      */   
31380     isLayoutInstant : false, // needed?
31381     /**
31382      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31383      */   
31384     isResizingContainer : true,
31385     /**
31386      * @cfg {Number} columnWidth  width of the columns 
31387      */   
31388     
31389     columnWidth : 0,
31390     
31391     /**
31392      * @cfg {Number} maxCols maximum number of columns
31393      */   
31394     
31395     maxCols: 0,
31396     /**
31397      * @cfg {Number} padHeight padding below box..
31398      */   
31399     
31400     padHeight : 10, 
31401     
31402     /**
31403      * @cfg {Boolean} isAutoInitial defalut true
31404      */   
31405     
31406     isAutoInitial : true, 
31407     
31408     // private?
31409     gutter : 0,
31410     
31411     containerWidth: 0,
31412     initialColumnWidth : 0,
31413     currentSize : null,
31414     
31415     colYs : null, // array.
31416     maxY : 0,
31417     padWidth: 10,
31418     
31419     
31420     tag: 'div',
31421     cls: '',
31422     bricks: null, //CompositeElement
31423     cols : 0, // array?
31424     // element : null, // wrapped now this.el
31425     _isLayoutInited : null, 
31426     
31427     
31428     getAutoCreate : function(){
31429         
31430         var cfg = {
31431             tag: this.tag,
31432             cls: 'blog-masonary-wrapper ' + this.cls,
31433             cn : {
31434                 cls : 'mas-boxes masonary'
31435             }
31436         };
31437         
31438         return cfg;
31439     },
31440     
31441     getChildContainer: function( )
31442     {
31443         if (this.boxesEl) {
31444             return this.boxesEl;
31445         }
31446         
31447         this.boxesEl = this.el.select('.mas-boxes').first();
31448         
31449         return this.boxesEl;
31450     },
31451     
31452     
31453     initEvents : function()
31454     {
31455         var _this = this;
31456         
31457         if(this.isAutoInitial){
31458             Roo.log('hook children rendered');
31459             this.on('childrenrendered', function() {
31460                 Roo.log('children rendered');
31461                 _this.initial();
31462             } ,this);
31463         }
31464         
31465     },
31466     
31467     initial : function()
31468     {
31469         this.reloadItems();
31470
31471         this.currentSize = this.el.getBox(true);
31472
31473         /// was window resize... - let's see if this works..
31474         Roo.EventManager.onWindowResize(this.resize, this); 
31475
31476         if(!this.isAutoInitial){
31477             this.layout();
31478             return;
31479         }
31480         
31481         this.layout.defer(500,this);
31482     },
31483     
31484     reloadItems: function()
31485     {
31486         this.bricks = this.el.select('.masonry-brick', true);
31487         
31488         this.bricks.each(function(b) {
31489             //Roo.log(b.getSize());
31490             if (!b.attr('originalwidth')) {
31491                 b.attr('originalwidth',  b.getSize().width);
31492             }
31493             
31494         });
31495         
31496         Roo.log(this.bricks.elements.length);
31497     },
31498     
31499     resize : function()
31500     {
31501         Roo.log('resize');
31502         var cs = this.el.getBox(true);
31503         
31504         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
31505             Roo.log("no change in with or X");
31506             return;
31507         }
31508         this.currentSize = cs;
31509         this.layout();
31510     },
31511     
31512     layout : function()
31513     {
31514          Roo.log('layout');
31515         this._resetLayout();
31516         //this._manageStamps();
31517       
31518         // don't animate first layout
31519         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31520         this.layoutItems( isInstant );
31521       
31522         // flag for initalized
31523         this._isLayoutInited = true;
31524     },
31525     
31526     layoutItems : function( isInstant )
31527     {
31528         //var items = this._getItemsForLayout( this.items );
31529         // original code supports filtering layout items.. we just ignore it..
31530         
31531         this._layoutItems( this.bricks , isInstant );
31532       
31533         this._postLayout();
31534     },
31535     _layoutItems : function ( items , isInstant)
31536     {
31537        //this.fireEvent( 'layout', this, items );
31538     
31539
31540         if ( !items || !items.elements.length ) {
31541           // no items, emit event with empty array
31542             return;
31543         }
31544
31545         var queue = [];
31546         items.each(function(item) {
31547             Roo.log("layout item");
31548             Roo.log(item);
31549             // get x/y object from method
31550             var position = this._getItemLayoutPosition( item );
31551             // enqueue
31552             position.item = item;
31553             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
31554             queue.push( position );
31555         }, this);
31556       
31557         this._processLayoutQueue( queue );
31558     },
31559     /** Sets position of item in DOM
31560     * @param {Element} item
31561     * @param {Number} x - horizontal position
31562     * @param {Number} y - vertical position
31563     * @param {Boolean} isInstant - disables transitions
31564     */
31565     _processLayoutQueue : function( queue )
31566     {
31567         for ( var i=0, len = queue.length; i < len; i++ ) {
31568             var obj = queue[i];
31569             obj.item.position('absolute');
31570             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
31571         }
31572     },
31573       
31574     
31575     /**
31576     * Any logic you want to do after each layout,
31577     * i.e. size the container
31578     */
31579     _postLayout : function()
31580     {
31581         this.resizeContainer();
31582     },
31583     
31584     resizeContainer : function()
31585     {
31586         if ( !this.isResizingContainer ) {
31587             return;
31588         }
31589         var size = this._getContainerSize();
31590         if ( size ) {
31591             this.el.setSize(size.width,size.height);
31592             this.boxesEl.setSize(size.width,size.height);
31593         }
31594     },
31595     
31596     
31597     
31598     _resetLayout : function()
31599     {
31600         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
31601         this.colWidth = this.el.getWidth();
31602         //this.gutter = this.el.getWidth(); 
31603         
31604         this.measureColumns();
31605
31606         // reset column Y
31607         var i = this.cols;
31608         this.colYs = [];
31609         while (i--) {
31610             this.colYs.push( 0 );
31611         }
31612     
31613         this.maxY = 0;
31614     },
31615
31616     measureColumns : function()
31617     {
31618         this.getContainerWidth();
31619       // if columnWidth is 0, default to outerWidth of first item
31620         if ( !this.columnWidth ) {
31621             var firstItem = this.bricks.first();
31622             Roo.log(firstItem);
31623             this.columnWidth  = this.containerWidth;
31624             if (firstItem && firstItem.attr('originalwidth') ) {
31625                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
31626             }
31627             // columnWidth fall back to item of first element
31628             Roo.log("set column width?");
31629                         this.initialColumnWidth = this.columnWidth  ;
31630
31631             // if first elem has no width, default to size of container
31632             
31633         }
31634         
31635         
31636         if (this.initialColumnWidth) {
31637             this.columnWidth = this.initialColumnWidth;
31638         }
31639         
31640         
31641             
31642         // column width is fixed at the top - however if container width get's smaller we should
31643         // reduce it...
31644         
31645         // this bit calcs how man columns..
31646             
31647         var columnWidth = this.columnWidth += this.gutter;
31648       
31649         // calculate columns
31650         var containerWidth = this.containerWidth + this.gutter;
31651         
31652         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
31653         // fix rounding errors, typically with gutters
31654         var excess = columnWidth - containerWidth % columnWidth;
31655         
31656         
31657         // if overshoot is less than a pixel, round up, otherwise floor it
31658         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
31659         cols = Math[ mathMethod ]( cols );
31660         this.cols = Math.max( cols, 1 );
31661         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31662         
31663          // padding positioning..
31664         var totalColWidth = this.cols * this.columnWidth;
31665         var padavail = this.containerWidth - totalColWidth;
31666         // so for 2 columns - we need 3 'pads'
31667         
31668         var padNeeded = (1+this.cols) * this.padWidth;
31669         
31670         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
31671         
31672         this.columnWidth += padExtra
31673         //this.padWidth = Math.floor(padavail /  ( this.cols));
31674         
31675         // adjust colum width so that padding is fixed??
31676         
31677         // we have 3 columns ... total = width * 3
31678         // we have X left over... that should be used by 
31679         
31680         //if (this.expandC) {
31681             
31682         //}
31683         
31684         
31685         
31686     },
31687     
31688     getContainerWidth : function()
31689     {
31690        /* // container is parent if fit width
31691         var container = this.isFitWidth ? this.element.parentNode : this.element;
31692         // check that this.size and size are there
31693         // IE8 triggers resize on body size change, so they might not be
31694         
31695         var size = getSize( container );  //FIXME
31696         this.containerWidth = size && size.innerWidth; //FIXME
31697         */
31698          
31699         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31700         
31701     },
31702     
31703     _getItemLayoutPosition : function( item )  // what is item?
31704     {
31705         // we resize the item to our columnWidth..
31706       
31707         item.setWidth(this.columnWidth);
31708         item.autoBoxAdjust  = false;
31709         
31710         var sz = item.getSize();
31711  
31712         // how many columns does this brick span
31713         var remainder = this.containerWidth % this.columnWidth;
31714         
31715         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
31716         // round if off by 1 pixel, otherwise use ceil
31717         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
31718         colSpan = Math.min( colSpan, this.cols );
31719         
31720         // normally this should be '1' as we dont' currently allow multi width columns..
31721         
31722         var colGroup = this._getColGroup( colSpan );
31723         // get the minimum Y value from the columns
31724         var minimumY = Math.min.apply( Math, colGroup );
31725         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31726         
31727         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
31728          
31729         // position the brick
31730         var position = {
31731             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
31732             y: this.currentSize.y + minimumY + this.padHeight
31733         };
31734         
31735         Roo.log(position);
31736         // apply setHeight to necessary columns
31737         var setHeight = minimumY + sz.height + this.padHeight;
31738         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31739         
31740         var setSpan = this.cols + 1 - colGroup.length;
31741         for ( var i = 0; i < setSpan; i++ ) {
31742           this.colYs[ shortColIndex + i ] = setHeight ;
31743         }
31744       
31745         return position;
31746     },
31747     
31748     /**
31749      * @param {Number} colSpan - number of columns the element spans
31750      * @returns {Array} colGroup
31751      */
31752     _getColGroup : function( colSpan )
31753     {
31754         if ( colSpan < 2 ) {
31755           // if brick spans only one column, use all the column Ys
31756           return this.colYs;
31757         }
31758       
31759         var colGroup = [];
31760         // how many different places could this brick fit horizontally
31761         var groupCount = this.cols + 1 - colSpan;
31762         // for each group potential horizontal position
31763         for ( var i = 0; i < groupCount; i++ ) {
31764           // make an array of colY values for that one group
31765           var groupColYs = this.colYs.slice( i, i + colSpan );
31766           // and get the max value of the array
31767           colGroup[i] = Math.max.apply( Math, groupColYs );
31768         }
31769         return colGroup;
31770     },
31771     /*
31772     _manageStamp : function( stamp )
31773     {
31774         var stampSize =  stamp.getSize();
31775         var offset = stamp.getBox();
31776         // get the columns that this stamp affects
31777         var firstX = this.isOriginLeft ? offset.x : offset.right;
31778         var lastX = firstX + stampSize.width;
31779         var firstCol = Math.floor( firstX / this.columnWidth );
31780         firstCol = Math.max( 0, firstCol );
31781         
31782         var lastCol = Math.floor( lastX / this.columnWidth );
31783         // lastCol should not go over if multiple of columnWidth #425
31784         lastCol -= lastX % this.columnWidth ? 0 : 1;
31785         lastCol = Math.min( this.cols - 1, lastCol );
31786         
31787         // set colYs to bottom of the stamp
31788         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
31789             stampSize.height;
31790             
31791         for ( var i = firstCol; i <= lastCol; i++ ) {
31792           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
31793         }
31794     },
31795     */
31796     
31797     _getContainerSize : function()
31798     {
31799         this.maxY = Math.max.apply( Math, this.colYs );
31800         var size = {
31801             height: this.maxY
31802         };
31803       
31804         if ( this.isFitWidth ) {
31805             size.width = this._getContainerFitWidth();
31806         }
31807       
31808         return size;
31809     },
31810     
31811     _getContainerFitWidth : function()
31812     {
31813         var unusedCols = 0;
31814         // count unused columns
31815         var i = this.cols;
31816         while ( --i ) {
31817           if ( this.colYs[i] !== 0 ) {
31818             break;
31819           }
31820           unusedCols++;
31821         }
31822         // fit container to columns that have been used
31823         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
31824     },
31825     
31826     needsResizeLayout : function()
31827     {
31828         var previousWidth = this.containerWidth;
31829         this.getContainerWidth();
31830         return previousWidth !== this.containerWidth;
31831     }
31832  
31833 });
31834
31835  
31836
31837  /*
31838  * - LGPL
31839  *
31840  * element
31841  * 
31842  */
31843
31844 /**
31845  * @class Roo.bootstrap.MasonryBrick
31846  * @extends Roo.bootstrap.Component
31847  * Bootstrap MasonryBrick class
31848  * 
31849  * @constructor
31850  * Create a new MasonryBrick
31851  * @param {Object} config The config object
31852  */
31853
31854 Roo.bootstrap.MasonryBrick = function(config){
31855     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
31856     
31857     this.addEvents({
31858         // raw events
31859         /**
31860          * @event click
31861          * When a MasonryBrick is clcik
31862          * @param {Roo.bootstrap.MasonryBrick} this
31863          * @param {Roo.EventObject} e
31864          */
31865         "click" : true
31866     });
31867 };
31868
31869 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
31870     
31871     /**
31872      * @cfg {String} title
31873      */   
31874     title : '',
31875     /**
31876      * @cfg {String} html
31877      */   
31878     html : '',
31879     /**
31880      * @cfg {String} bgimage
31881      */   
31882     bgimage : '',
31883     /**
31884      * @cfg {String} videourl
31885      */   
31886     videourl : '',
31887     /**
31888      * @cfg {String} cls
31889      */   
31890     cls : '',
31891     /**
31892      * @cfg {String} href
31893      */   
31894     href : '',
31895     /**
31896      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
31897      */   
31898     size : 'xs',
31899     
31900     /**
31901      * @cfg {String} placetitle (center|bottom)
31902      */   
31903     placetitle : '',
31904     
31905     /**
31906      * @cfg {Boolean} isFitContainer defalut true
31907      */   
31908     isFitContainer : true, 
31909     
31910     /**
31911      * @cfg {Boolean} preventDefault defalut false
31912      */   
31913     preventDefault : false, 
31914     
31915     /**
31916      * @cfg {Boolean} inverse defalut false
31917      */   
31918     maskInverse : false, 
31919     
31920     getAutoCreate : function()
31921     {
31922         if(!this.isFitContainer){
31923             return this.getSplitAutoCreate();
31924         }
31925         
31926         var cls = 'masonry-brick masonry-brick-full';
31927         
31928         if(this.href.length){
31929             cls += ' masonry-brick-link';
31930         }
31931         
31932         if(this.bgimage.length){
31933             cls += ' masonry-brick-image';
31934         }
31935         
31936         if(this.maskInverse){
31937             cls += ' mask-inverse';
31938         }
31939         
31940         if(!this.html.length && !this.maskInverse){
31941             cls += ' enable-mask';
31942         }
31943         
31944         if(this.size){
31945             cls += ' masonry-' + this.size + '-brick';
31946         }
31947         
31948         if(this.placetitle.length){
31949             
31950             switch (this.placetitle) {
31951                 case 'center' :
31952                     cls += ' masonry-center-title';
31953                     break;
31954                 case 'bottom' :
31955                     cls += ' masonry-bottom-title';
31956                     break;
31957                 default:
31958                     break;
31959             }
31960             
31961         } else {
31962             if(!this.html.length && !this.bgimage.length){
31963                 cls += ' masonry-center-title';
31964             }
31965
31966             if(!this.html.length && this.bgimage.length){
31967                 cls += ' masonry-bottom-title';
31968             }
31969         }
31970         
31971         if(this.cls){
31972             cls += ' ' + this.cls;
31973         }
31974         
31975         var cfg = {
31976             tag: (this.href.length) ? 'a' : 'div',
31977             cls: cls,
31978             cn: [
31979                 {
31980                     tag: 'div',
31981                     cls: 'masonry-brick-paragraph',
31982                     cn: []
31983                 }
31984             ]
31985         };
31986         
31987         if(this.href.length){
31988             cfg.href = this.href;
31989         }
31990         
31991         var cn = cfg.cn[0].cn;
31992         
31993         if(this.title.length){
31994             cn.push({
31995                 tag: 'h4',
31996                 cls: 'masonry-brick-title',
31997                 html: this.title
31998             });
31999         }
32000         
32001         if(this.html.length){
32002             cn.push({
32003                 tag: 'p',
32004                 cls: 'masonry-brick-text',
32005                 html: this.html
32006             });
32007         }  
32008         if (!this.title.length && !this.html.length) {
32009             cfg.cn[0].cls += ' hide';
32010         }
32011         
32012         if(this.bgimage.length){
32013             cfg.cn.push({
32014                 tag: 'img',
32015                 cls: 'masonry-brick-image-view',
32016                 src: this.bgimage
32017             });
32018         }
32019         
32020         if(this.videourl.length){
32021             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32022             // youtube support only?
32023             cfg.cn.push({
32024                 tag: 'iframe',
32025                 cls: 'masonry-brick-image-view',
32026                 src: vurl,
32027                 frameborder : 0,
32028                 allowfullscreen : true
32029             });
32030             
32031             
32032         }
32033         
32034         cfg.cn.push({
32035             tag: 'div',
32036             cls: 'masonry-brick-mask'
32037         });
32038         
32039         return cfg;
32040         
32041     },
32042     
32043     getSplitAutoCreate : function()
32044     {
32045         var cls = 'masonry-brick masonry-brick-split';
32046         
32047         if(this.href.length){
32048             cls += ' masonry-brick-link';
32049         }
32050         
32051         if(this.bgimage.length){
32052             cls += ' masonry-brick-image';
32053         }
32054         
32055         if(this.size){
32056             cls += ' masonry-' + this.size + '-brick';
32057         }
32058         
32059         switch (this.placetitle) {
32060             case 'center' :
32061                 cls += ' masonry-center-title';
32062                 break;
32063             case 'bottom' :
32064                 cls += ' masonry-bottom-title';
32065                 break;
32066             default:
32067                 if(!this.bgimage.length){
32068                     cls += ' masonry-center-title';
32069                 }
32070
32071                 if(this.bgimage.length){
32072                     cls += ' masonry-bottom-title';
32073                 }
32074                 break;
32075         }
32076         
32077         if(this.cls){
32078             cls += ' ' + this.cls;
32079         }
32080         
32081         var cfg = {
32082             tag: (this.href.length) ? 'a' : 'div',
32083             cls: cls,
32084             cn: [
32085                 {
32086                     tag: 'div',
32087                     cls: 'masonry-brick-split-head',
32088                     cn: [
32089                         {
32090                             tag: 'div',
32091                             cls: 'masonry-brick-paragraph',
32092                             cn: []
32093                         }
32094                     ]
32095                 },
32096                 {
32097                     tag: 'div',
32098                     cls: 'masonry-brick-split-body',
32099                     cn: []
32100                 }
32101             ]
32102         };
32103         
32104         if(this.href.length){
32105             cfg.href = this.href;
32106         }
32107         
32108         if(this.title.length){
32109             cfg.cn[0].cn[0].cn.push({
32110                 tag: 'h4',
32111                 cls: 'masonry-brick-title',
32112                 html: this.title
32113             });
32114         }
32115         
32116         if(this.html.length){
32117             cfg.cn[1].cn.push({
32118                 tag: 'p',
32119                 cls: 'masonry-brick-text',
32120                 html: this.html
32121             });
32122         }
32123
32124         if(this.bgimage.length){
32125             cfg.cn[0].cn.push({
32126                 tag: 'img',
32127                 cls: 'masonry-brick-image-view',
32128                 src: this.bgimage
32129             });
32130         }
32131         
32132         if(this.videourl.length){
32133             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32134             // youtube support only?
32135             cfg.cn[0].cn.cn.push({
32136                 tag: 'iframe',
32137                 cls: 'masonry-brick-image-view',
32138                 src: vurl,
32139                 frameborder : 0,
32140                 allowfullscreen : true
32141             });
32142         }
32143         
32144         return cfg;
32145     },
32146     
32147     initEvents: function() 
32148     {
32149         switch (this.size) {
32150             case 'xs' :
32151                 this.x = 1;
32152                 this.y = 1;
32153                 break;
32154             case 'sm' :
32155                 this.x = 2;
32156                 this.y = 2;
32157                 break;
32158             case 'md' :
32159             case 'md-left' :
32160             case 'md-right' :
32161                 this.x = 3;
32162                 this.y = 3;
32163                 break;
32164             case 'tall' :
32165                 this.x = 2;
32166                 this.y = 3;
32167                 break;
32168             case 'wide' :
32169                 this.x = 3;
32170                 this.y = 2;
32171                 break;
32172             case 'wide-thin' :
32173                 this.x = 3;
32174                 this.y = 1;
32175                 break;
32176                         
32177             default :
32178                 break;
32179         }
32180         
32181         if(Roo.isTouch){
32182             this.el.on('touchstart', this.onTouchStart, this);
32183             this.el.on('touchmove', this.onTouchMove, this);
32184             this.el.on('touchend', this.onTouchEnd, this);
32185             this.el.on('contextmenu', this.onContextMenu, this);
32186         } else {
32187             this.el.on('mouseenter'  ,this.enter, this);
32188             this.el.on('mouseleave', this.leave, this);
32189             this.el.on('click', this.onClick, this);
32190         }
32191         
32192         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32193             this.parent().bricks.push(this);   
32194         }
32195         
32196     },
32197     
32198     onClick: function(e, el)
32199     {
32200         var time = this.endTimer - this.startTimer;
32201         
32202         if(Roo.isTouch){
32203             if(time > 1000){
32204                 e.preventDefault();
32205                 return;
32206             }
32207         }
32208         
32209         if(!this.preventDefault){
32210             return;
32211         }
32212         
32213         e.preventDefault();
32214         this.fireEvent('click', this);
32215     },
32216     
32217     enter: function(e, el)
32218     {
32219         e.preventDefault();
32220         
32221         if(!this.isFitContainer || this.maskInverse){
32222             return;
32223         }
32224         
32225         if(this.bgimage.length && this.html.length){
32226             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32227         }
32228     },
32229     
32230     leave: function(e, el)
32231     {
32232         e.preventDefault();
32233         
32234         if(!this.isFitContainer || this.maskInverse){
32235             return;
32236         }
32237         
32238         if(this.bgimage.length && this.html.length){
32239             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32240         }
32241     },
32242     
32243     onTouchStart: function(e, el)
32244     {
32245 //        e.preventDefault();
32246         
32247         this.touchmoved = false;
32248         
32249         if(!this.isFitContainer){
32250             return;
32251         }
32252         
32253         if(!this.bgimage.length || !this.html.length){
32254             return;
32255         }
32256         
32257         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32258         
32259         this.timer = new Date().getTime();
32260         
32261     },
32262     
32263     onTouchMove: function(e, el)
32264     {
32265         this.touchmoved = true;
32266     },
32267     
32268     onContextMenu : function(e,el)
32269     {
32270         e.preventDefault();
32271         e.stopPropagation();
32272         return false;
32273     },
32274     
32275     onTouchEnd: function(e, el)
32276     {
32277 //        e.preventDefault();
32278         
32279         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32280         
32281             this.leave(e,el);
32282             
32283             return;
32284         }
32285         
32286         if(!this.bgimage.length || !this.html.length){
32287             
32288             if(this.href.length){
32289                 window.location.href = this.href;
32290             }
32291             
32292             return;
32293         }
32294         
32295         if(!this.isFitContainer){
32296             return;
32297         }
32298         
32299         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32300         
32301         window.location.href = this.href;
32302     }
32303     
32304 });
32305
32306  
32307
32308  /*
32309  * - LGPL
32310  *
32311  * element
32312  * 
32313  */
32314
32315 /**
32316  * @class Roo.bootstrap.Brick
32317  * @extends Roo.bootstrap.Component
32318  * Bootstrap Brick class
32319  * 
32320  * @constructor
32321  * Create a new Brick
32322  * @param {Object} config The config object
32323  */
32324
32325 Roo.bootstrap.Brick = function(config){
32326     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32327     
32328     this.addEvents({
32329         // raw events
32330         /**
32331          * @event click
32332          * When a Brick is click
32333          * @param {Roo.bootstrap.Brick} this
32334          * @param {Roo.EventObject} e
32335          */
32336         "click" : true
32337     });
32338 };
32339
32340 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
32341     
32342     /**
32343      * @cfg {String} title
32344      */   
32345     title : '',
32346     /**
32347      * @cfg {String} html
32348      */   
32349     html : '',
32350     /**
32351      * @cfg {String} bgimage
32352      */   
32353     bgimage : '',
32354     /**
32355      * @cfg {String} cls
32356      */   
32357     cls : '',
32358     /**
32359      * @cfg {String} href
32360      */   
32361     href : '',
32362     /**
32363      * @cfg {String} video
32364      */   
32365     video : '',
32366     /**
32367      * @cfg {Boolean} square
32368      */   
32369     square : true,
32370     
32371     getAutoCreate : function()
32372     {
32373         var cls = 'roo-brick';
32374         
32375         if(this.href.length){
32376             cls += ' roo-brick-link';
32377         }
32378         
32379         if(this.bgimage.length){
32380             cls += ' roo-brick-image';
32381         }
32382         
32383         if(!this.html.length && !this.bgimage.length){
32384             cls += ' roo-brick-center-title';
32385         }
32386         
32387         if(!this.html.length && this.bgimage.length){
32388             cls += ' roo-brick-bottom-title';
32389         }
32390         
32391         if(this.cls){
32392             cls += ' ' + this.cls;
32393         }
32394         
32395         var cfg = {
32396             tag: (this.href.length) ? 'a' : 'div',
32397             cls: cls,
32398             cn: [
32399                 {
32400                     tag: 'div',
32401                     cls: 'roo-brick-paragraph',
32402                     cn: []
32403                 }
32404             ]
32405         };
32406         
32407         if(this.href.length){
32408             cfg.href = this.href;
32409         }
32410         
32411         var cn = cfg.cn[0].cn;
32412         
32413         if(this.title.length){
32414             cn.push({
32415                 tag: 'h4',
32416                 cls: 'roo-brick-title',
32417                 html: this.title
32418             });
32419         }
32420         
32421         if(this.html.length){
32422             cn.push({
32423                 tag: 'p',
32424                 cls: 'roo-brick-text',
32425                 html: this.html
32426             });
32427         } else {
32428             cn.cls += ' hide';
32429         }
32430         
32431         if(this.bgimage.length){
32432             cfg.cn.push({
32433                 tag: 'img',
32434                 cls: 'roo-brick-image-view',
32435                 src: this.bgimage
32436             });
32437         }
32438         
32439         return cfg;
32440     },
32441     
32442     initEvents: function() 
32443     {
32444         if(this.title.length || this.html.length){
32445             this.el.on('mouseenter'  ,this.enter, this);
32446             this.el.on('mouseleave', this.leave, this);
32447         }
32448         
32449         
32450         Roo.EventManager.onWindowResize(this.resize, this); 
32451         
32452         this.resize();
32453     },
32454     
32455     resize : function()
32456     {
32457         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
32458         
32459         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
32460         
32461         if(this.bgimage.length){
32462             var image = this.el.select('.roo-brick-image-view', true).first();
32463             image.setWidth(paragraph.getWidth());
32464             image.setHeight(paragraph.getWidth());
32465             
32466             this.el.setHeight(paragraph.getWidth());
32467             
32468         }
32469         
32470     },
32471     
32472     enter: function(e, el)
32473     {
32474         e.preventDefault();
32475         
32476         if(this.bgimage.length){
32477             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
32478             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
32479         }
32480     },
32481     
32482     leave: function(e, el)
32483     {
32484         e.preventDefault();
32485         
32486         if(this.bgimage.length){
32487             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
32488             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
32489         }
32490     }
32491     
32492 });
32493
32494  
32495
32496  /*
32497  * - LGPL
32498  *
32499  * Input
32500  * 
32501  */
32502
32503 /**
32504  * @class Roo.bootstrap.NumberField
32505  * @extends Roo.bootstrap.Input
32506  * Bootstrap NumberField class
32507  * 
32508  * 
32509  * 
32510  * 
32511  * @constructor
32512  * Create a new NumberField
32513  * @param {Object} config The config object
32514  */
32515
32516 Roo.bootstrap.NumberField = function(config){
32517     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
32518 };
32519
32520 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
32521     
32522     /**
32523      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
32524      */
32525     allowDecimals : true,
32526     /**
32527      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
32528      */
32529     decimalSeparator : ".",
32530     /**
32531      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
32532      */
32533     decimalPrecision : 2,
32534     /**
32535      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
32536      */
32537     allowNegative : true,
32538     /**
32539      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
32540      */
32541     minValue : Number.NEGATIVE_INFINITY,
32542     /**
32543      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
32544      */
32545     maxValue : Number.MAX_VALUE,
32546     /**
32547      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
32548      */
32549     minText : "The minimum value for this field is {0}",
32550     /**
32551      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
32552      */
32553     maxText : "The maximum value for this field is {0}",
32554     /**
32555      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
32556      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
32557      */
32558     nanText : "{0} is not a valid number",
32559     /**
32560      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
32561      */
32562     castInt : true,
32563
32564     // private
32565     initEvents : function()
32566     {   
32567         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
32568         
32569         var allowed = "0123456789";
32570         
32571         if(this.allowDecimals){
32572             allowed += this.decimalSeparator;
32573         }
32574         
32575         if(this.allowNegative){
32576             allowed += "-";
32577         }
32578         
32579         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
32580         
32581         var keyPress = function(e){
32582             
32583             var k = e.getKey();
32584             
32585             var c = e.getCharCode();
32586             
32587             if(
32588                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
32589                     allowed.indexOf(String.fromCharCode(c)) === -1
32590             ){
32591                 e.stopEvent();
32592                 return;
32593             }
32594             
32595             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
32596                 return;
32597             }
32598             
32599             if(allowed.indexOf(String.fromCharCode(c)) === -1){
32600                 e.stopEvent();
32601             }
32602         };
32603         
32604         this.el.on("keypress", keyPress, this);
32605     },
32606     
32607     validateValue : function(value)
32608     {
32609         
32610         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
32611             return false;
32612         }
32613         
32614         var num = this.parseValue(value);
32615         
32616         if(isNaN(num)){
32617             this.markInvalid(String.format(this.nanText, value));
32618             return false;
32619         }
32620         
32621         if(num < this.minValue){
32622             this.markInvalid(String.format(this.minText, this.minValue));
32623             return false;
32624         }
32625         
32626         if(num > this.maxValue){
32627             this.markInvalid(String.format(this.maxText, this.maxValue));
32628             return false;
32629         }
32630         
32631         return true;
32632     },
32633
32634     getValue : function()
32635     {
32636         return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
32637     },
32638
32639     parseValue : function(value)
32640     {
32641         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
32642         return isNaN(value) ? '' : value;
32643     },
32644
32645     fixPrecision : function(value)
32646     {
32647         var nan = isNaN(value);
32648         
32649         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
32650             return nan ? '' : value;
32651         }
32652         return parseFloat(value).toFixed(this.decimalPrecision);
32653     },
32654
32655     setValue : function(v)
32656     {
32657         v = this.fixPrecision(v);
32658         Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
32659     },
32660
32661     decimalPrecisionFcn : function(v)
32662     {
32663         return Math.floor(v);
32664     },
32665
32666     beforeBlur : function()
32667     {
32668         if(!this.castInt){
32669             return;
32670         }
32671         
32672         var v = this.parseValue(this.getRawValue());
32673         if(v){
32674             this.setValue(v);
32675         }
32676     }
32677     
32678 });
32679
32680  
32681
32682 /*
32683 * Licence: LGPL
32684 */
32685
32686 /**
32687  * @class Roo.bootstrap.DocumentSlider
32688  * @extends Roo.bootstrap.Component
32689  * Bootstrap DocumentSlider class
32690  * 
32691  * @constructor
32692  * Create a new DocumentViewer
32693  * @param {Object} config The config object
32694  */
32695
32696 Roo.bootstrap.DocumentSlider = function(config){
32697     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
32698     
32699     this.files = [];
32700     
32701     this.addEvents({
32702         /**
32703          * @event initial
32704          * Fire after initEvent
32705          * @param {Roo.bootstrap.DocumentSlider} this
32706          */
32707         "initial" : true,
32708         /**
32709          * @event update
32710          * Fire after update
32711          * @param {Roo.bootstrap.DocumentSlider} this
32712          */
32713         "update" : true,
32714         /**
32715          * @event click
32716          * Fire after click
32717          * @param {Roo.bootstrap.DocumentSlider} this
32718          */
32719         "click" : true
32720     });
32721 };
32722
32723 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
32724     
32725     files : false,
32726     
32727     indicator : 0,
32728     
32729     getAutoCreate : function()
32730     {
32731         var cfg = {
32732             tag : 'div',
32733             cls : 'roo-document-slider',
32734             cn : [
32735                 {
32736                     tag : 'div',
32737                     cls : 'roo-document-slider-header',
32738                     cn : [
32739                         {
32740                             tag : 'div',
32741                             cls : 'roo-document-slider-header-title'
32742                         }
32743                     ]
32744                 },
32745                 {
32746                     tag : 'div',
32747                     cls : 'roo-document-slider-body',
32748                     cn : [
32749                         {
32750                             tag : 'div',
32751                             cls : 'roo-document-slider-prev',
32752                             cn : [
32753                                 {
32754                                     tag : 'i',
32755                                     cls : 'fa fa-chevron-left'
32756                                 }
32757                             ]
32758                         },
32759                         {
32760                             tag : 'div',
32761                             cls : 'roo-document-slider-thumb',
32762                             cn : [
32763                                 {
32764                                     tag : 'img',
32765                                     cls : 'roo-document-slider-image'
32766                                 }
32767                             ]
32768                         },
32769                         {
32770                             tag : 'div',
32771                             cls : 'roo-document-slider-next',
32772                             cn : [
32773                                 {
32774                                     tag : 'i',
32775                                     cls : 'fa fa-chevron-right'
32776                                 }
32777                             ]
32778                         }
32779                     ]
32780                 }
32781             ]
32782         };
32783         
32784         return cfg;
32785     },
32786     
32787     initEvents : function()
32788     {
32789         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
32790         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
32791         
32792         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
32793         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
32794         
32795         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
32796         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32797         
32798         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
32799         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32800         
32801         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
32802         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32803         
32804         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
32805         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
32806         
32807         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
32808         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
32809         
32810         this.thumbEl.on('click', this.onClick, this);
32811         
32812         this.prevIndicator.on('click', this.prev, this);
32813         
32814         this.nextIndicator.on('click', this.next, this);
32815         
32816     },
32817     
32818     initial : function()
32819     {
32820         if(this.files.length){
32821             this.indicator = 1;
32822             this.update()
32823         }
32824         
32825         this.fireEvent('initial', this);
32826     },
32827     
32828     update : function()
32829     {
32830         this.imageEl.attr('src', this.files[this.indicator - 1]);
32831         
32832         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
32833         
32834         this.prevIndicator.show();
32835         
32836         if(this.indicator == 1){
32837             this.prevIndicator.hide();
32838         }
32839         
32840         this.nextIndicator.show();
32841         
32842         if(this.indicator == this.files.length){
32843             this.nextIndicator.hide();
32844         }
32845         
32846         this.thumbEl.scrollTo('top');
32847         
32848         this.fireEvent('update', this);
32849     },
32850     
32851     onClick : function(e)
32852     {
32853         e.preventDefault();
32854         
32855         this.fireEvent('click', this);
32856     },
32857     
32858     prev : function(e)
32859     {
32860         e.preventDefault();
32861         
32862         this.indicator = Math.max(1, this.indicator - 1);
32863         
32864         this.update();
32865     },
32866     
32867     next : function(e)
32868     {
32869         e.preventDefault();
32870         
32871         this.indicator = Math.min(this.files.length, this.indicator + 1);
32872         
32873         this.update();
32874     }
32875 });
32876 /*
32877  * - LGPL
32878  *
32879  * RadioSet
32880  *
32881  *
32882  */
32883
32884 /**
32885  * @class Roo.bootstrap.RadioSet
32886  * @extends Roo.bootstrap.Input
32887  * Bootstrap RadioSet class
32888  * @cfg {String} indicatorpos (left|right) default left
32889  * @cfg {Boolean} inline (true|false) inline the element (default true)
32890  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
32891  * @constructor
32892  * Create a new RadioSet
32893  * @param {Object} config The config object
32894  */
32895
32896 Roo.bootstrap.RadioSet = function(config){
32897     
32898     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
32899     
32900     this.radioes = [];
32901     
32902     Roo.bootstrap.RadioSet.register(this);
32903     
32904     this.addEvents({
32905         /**
32906         * @event check
32907         * Fires when the element is checked or unchecked.
32908         * @param {Roo.bootstrap.RadioSet} this This radio
32909         * @param {Roo.bootstrap.Radio} item The checked item
32910         */
32911        check : true
32912     });
32913     
32914 };
32915
32916 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
32917
32918     radioes : false,
32919     
32920     inline : true,
32921     
32922     weight : '',
32923     
32924     indicatorpos : 'left',
32925     
32926     getAutoCreate : function()
32927     {
32928         var label = {
32929             tag : 'label',
32930             cls : 'roo-radio-set-label',
32931             cn : [
32932                 {
32933                     tag : 'span',
32934                     html : this.fieldLabel
32935                 }
32936             ]
32937         };
32938         
32939         if(this.indicatorpos == 'left'){
32940             label.cn.unshift({
32941                 tag : 'i',
32942                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
32943                 tooltip : 'This field is required'
32944             });
32945         } else {
32946             label.cn.push({
32947                 tag : 'i',
32948                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
32949                 tooltip : 'This field is required'
32950             });
32951         }
32952         
32953         var items = {
32954             tag : 'div',
32955             cls : 'roo-radio-set-items'
32956         };
32957         
32958         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
32959         
32960         if (align === 'left' && this.fieldLabel.length) {
32961             
32962             items = {
32963                 cls : "roo-radio-set-right", 
32964                 cn: [
32965                     items
32966                 ]
32967             };
32968             
32969             if(this.labelWidth > 12){
32970                 label.style = "width: " + this.labelWidth + 'px';
32971             }
32972             
32973             if(this.labelWidth < 13 && this.labelmd == 0){
32974                 this.labelmd = this.labelWidth;
32975             }
32976             
32977             if(this.labellg > 0){
32978                 label.cls += ' col-lg-' + this.labellg;
32979                 items.cls += ' col-lg-' + (12 - this.labellg);
32980             }
32981             
32982             if(this.labelmd > 0){
32983                 label.cls += ' col-md-' + this.labelmd;
32984                 items.cls += ' col-md-' + (12 - this.labelmd);
32985             }
32986             
32987             if(this.labelsm > 0){
32988                 label.cls += ' col-sm-' + this.labelsm;
32989                 items.cls += ' col-sm-' + (12 - this.labelsm);
32990             }
32991             
32992             if(this.labelxs > 0){
32993                 label.cls += ' col-xs-' + this.labelxs;
32994                 items.cls += ' col-xs-' + (12 - this.labelxs);
32995             }
32996         }
32997         
32998         var cfg = {
32999             tag : 'div',
33000             cls : 'roo-radio-set',
33001             cn : [
33002                 {
33003                     tag : 'input',
33004                     cls : 'roo-radio-set-input',
33005                     type : 'hidden',
33006                     name : this.name,
33007                     value : this.value ? this.value :  ''
33008                 },
33009                 label,
33010                 items
33011             ]
33012         };
33013         
33014         if(this.weight.length){
33015             cfg.cls += ' roo-radio-' + this.weight;
33016         }
33017         
33018         if(this.inline) {
33019             cfg.cls += ' roo-radio-set-inline';
33020         }
33021         
33022         return cfg;
33023         
33024     },
33025
33026     initEvents : function()
33027     {
33028         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33029         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33030         
33031         if(!this.fieldLabel.length){
33032             this.labelEl.hide();
33033         }
33034         
33035         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33036         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33037         
33038         this.indicatorEl().setVisibilityMode(Roo.Element.DISPLAY);
33039         this.indicatorEl().hide();
33040         
33041         this.originalValue = this.getValue();
33042         
33043     },
33044     
33045     inputEl: function ()
33046     {
33047         return this.el.select('.roo-radio-set-input', true).first();
33048     },
33049     
33050     getChildContainer : function()
33051     {
33052         return this.itemsEl;
33053     },
33054     
33055     register : function(item)
33056     {
33057         this.radioes.push(item);
33058         
33059     },
33060     
33061     validate : function()
33062     {   
33063         var valid = false;
33064         
33065         Roo.each(this.radioes, function(i){
33066             if(!i.checked){
33067                 return;
33068             }
33069             
33070             valid = true;
33071             return false;
33072         });
33073         
33074         if(this.allowBlank) {
33075             return true;
33076         }
33077         
33078         if(this.disabled || valid){
33079             this.markValid();
33080             return true;
33081         }
33082         
33083         this.markInvalid();
33084         return false;
33085         
33086     },
33087     
33088     markValid : function()
33089     {
33090         if(this.labelEl.isVisible(true)){
33091             this.indicatorEl().hide();
33092         }
33093         
33094         this.el.removeClass([this.invalidClass, this.validClass]);
33095         this.el.addClass(this.validClass);
33096         
33097         this.fireEvent('valid', this);
33098     },
33099     
33100     markInvalid : function(msg)
33101     {
33102         if(this.allowBlank || this.disabled){
33103             return;
33104         }
33105         
33106         if(this.labelEl.isVisible(true)){
33107             this.indicatorEl().show();
33108         }
33109         
33110         this.el.removeClass([this.invalidClass, this.validClass]);
33111         this.el.addClass(this.invalidClass);
33112         
33113         this.fireEvent('invalid', this, msg);
33114         
33115     },
33116     
33117     setValue : function(v, suppressEvent)
33118     {   
33119         Roo.each(this.radioes, function(i){
33120             
33121             i.checked = false;
33122             i.el.removeClass('checked');
33123             
33124             if(i.value === v || i.value.toString() === v.toString()){
33125                 i.checked = true;
33126                 i.el.addClass('checked');
33127                 
33128                 if(suppressEvent !== true){
33129                     this.fireEvent('check', this, i);
33130                 }
33131             }
33132             
33133         }, this);
33134         
33135         Roo.bootstrap.RadioSet.superclass.setValue.call(this, v);
33136         
33137     },
33138     
33139     clearInvalid : function(){
33140         
33141         if(!this.el || this.preventMark){
33142             return;
33143         }
33144         
33145         if(this.labelEl.isVisible(true)){
33146             this.indicatorEl().hide();
33147         }
33148         
33149         this.el.removeClass([this.invalidClass]);
33150         
33151         this.fireEvent('valid', this);
33152     }
33153     
33154 });
33155
33156 Roo.apply(Roo.bootstrap.RadioSet, {
33157     
33158     groups: {},
33159     
33160     register : function(set)
33161     {
33162         this.groups[set.name] = set;
33163     },
33164     
33165     get: function(name) 
33166     {
33167         if (typeof(this.groups[name]) == 'undefined') {
33168             return false;
33169         }
33170         
33171         return this.groups[name] ;
33172     }
33173     
33174 });
33175 /*
33176  * Based on:
33177  * Ext JS Library 1.1.1
33178  * Copyright(c) 2006-2007, Ext JS, LLC.
33179  *
33180  * Originally Released Under LGPL - original licence link has changed is not relivant.
33181  *
33182  * Fork - LGPL
33183  * <script type="text/javascript">
33184  */
33185
33186
33187 /**
33188  * @class Roo.bootstrap.SplitBar
33189  * @extends Roo.util.Observable
33190  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33191  * <br><br>
33192  * Usage:
33193  * <pre><code>
33194 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33195                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33196 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33197 split.minSize = 100;
33198 split.maxSize = 600;
33199 split.animate = true;
33200 split.on('moved', splitterMoved);
33201 </code></pre>
33202  * @constructor
33203  * Create a new SplitBar
33204  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
33205  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
33206  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33207  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
33208                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33209                         position of the SplitBar).
33210  */
33211 Roo.bootstrap.SplitBar = function(cfg){
33212     
33213     /** @private */
33214     
33215     //{
33216     //  dragElement : elm
33217     //  resizingElement: el,
33218         // optional..
33219     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33220     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
33221         // existingProxy ???
33222     //}
33223     
33224     this.el = Roo.get(cfg.dragElement, true);
33225     this.el.dom.unselectable = "on";
33226     /** @private */
33227     this.resizingEl = Roo.get(cfg.resizingElement, true);
33228
33229     /**
33230      * @private
33231      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33232      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
33233      * @type Number
33234      */
33235     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
33236     
33237     /**
33238      * The minimum size of the resizing element. (Defaults to 0)
33239      * @type Number
33240      */
33241     this.minSize = 0;
33242     
33243     /**
33244      * The maximum size of the resizing element. (Defaults to 2000)
33245      * @type Number
33246      */
33247     this.maxSize = 2000;
33248     
33249     /**
33250      * Whether to animate the transition to the new size
33251      * @type Boolean
33252      */
33253     this.animate = false;
33254     
33255     /**
33256      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
33257      * @type Boolean
33258      */
33259     this.useShim = false;
33260     
33261     /** @private */
33262     this.shim = null;
33263     
33264     if(!cfg.existingProxy){
33265         /** @private */
33266         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
33267     }else{
33268         this.proxy = Roo.get(cfg.existingProxy).dom;
33269     }
33270     /** @private */
33271     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
33272     
33273     /** @private */
33274     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
33275     
33276     /** @private */
33277     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
33278     
33279     /** @private */
33280     this.dragSpecs = {};
33281     
33282     /**
33283      * @private The adapter to use to positon and resize elements
33284      */
33285     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33286     this.adapter.init(this);
33287     
33288     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33289         /** @private */
33290         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
33291         this.el.addClass("roo-splitbar-h");
33292     }else{
33293         /** @private */
33294         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
33295         this.el.addClass("roo-splitbar-v");
33296     }
33297     
33298     this.addEvents({
33299         /**
33300          * @event resize
33301          * Fires when the splitter is moved (alias for {@link #event-moved})
33302          * @param {Roo.bootstrap.SplitBar} this
33303          * @param {Number} newSize the new width or height
33304          */
33305         "resize" : true,
33306         /**
33307          * @event moved
33308          * Fires when the splitter is moved
33309          * @param {Roo.bootstrap.SplitBar} this
33310          * @param {Number} newSize the new width or height
33311          */
33312         "moved" : true,
33313         /**
33314          * @event beforeresize
33315          * Fires before the splitter is dragged
33316          * @param {Roo.bootstrap.SplitBar} this
33317          */
33318         "beforeresize" : true,
33319
33320         "beforeapply" : true
33321     });
33322
33323     Roo.util.Observable.call(this);
33324 };
33325
33326 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
33327     onStartProxyDrag : function(x, y){
33328         this.fireEvent("beforeresize", this);
33329         if(!this.overlay){
33330             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
33331             o.unselectable();
33332             o.enableDisplayMode("block");
33333             // all splitbars share the same overlay
33334             Roo.bootstrap.SplitBar.prototype.overlay = o;
33335         }
33336         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33337         this.overlay.show();
33338         Roo.get(this.proxy).setDisplayed("block");
33339         var size = this.adapter.getElementSize(this);
33340         this.activeMinSize = this.getMinimumSize();;
33341         this.activeMaxSize = this.getMaximumSize();;
33342         var c1 = size - this.activeMinSize;
33343         var c2 = Math.max(this.activeMaxSize - size, 0);
33344         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33345             this.dd.resetConstraints();
33346             this.dd.setXConstraint(
33347                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
33348                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
33349             );
33350             this.dd.setYConstraint(0, 0);
33351         }else{
33352             this.dd.resetConstraints();
33353             this.dd.setXConstraint(0, 0);
33354             this.dd.setYConstraint(
33355                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
33356                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
33357             );
33358          }
33359         this.dragSpecs.startSize = size;
33360         this.dragSpecs.startPoint = [x, y];
33361         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
33362     },
33363     
33364     /** 
33365      * @private Called after the drag operation by the DDProxy
33366      */
33367     onEndProxyDrag : function(e){
33368         Roo.get(this.proxy).setDisplayed(false);
33369         var endPoint = Roo.lib.Event.getXY(e);
33370         if(this.overlay){
33371             this.overlay.hide();
33372         }
33373         var newSize;
33374         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33375             newSize = this.dragSpecs.startSize + 
33376                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
33377                     endPoint[0] - this.dragSpecs.startPoint[0] :
33378                     this.dragSpecs.startPoint[0] - endPoint[0]
33379                 );
33380         }else{
33381             newSize = this.dragSpecs.startSize + 
33382                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
33383                     endPoint[1] - this.dragSpecs.startPoint[1] :
33384                     this.dragSpecs.startPoint[1] - endPoint[1]
33385                 );
33386         }
33387         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
33388         if(newSize != this.dragSpecs.startSize){
33389             if(this.fireEvent('beforeapply', this, newSize) !== false){
33390                 this.adapter.setElementSize(this, newSize);
33391                 this.fireEvent("moved", this, newSize);
33392                 this.fireEvent("resize", this, newSize);
33393             }
33394         }
33395     },
33396     
33397     /**
33398      * Get the adapter this SplitBar uses
33399      * @return The adapter object
33400      */
33401     getAdapter : function(){
33402         return this.adapter;
33403     },
33404     
33405     /**
33406      * Set the adapter this SplitBar uses
33407      * @param {Object} adapter A SplitBar adapter object
33408      */
33409     setAdapter : function(adapter){
33410         this.adapter = adapter;
33411         this.adapter.init(this);
33412     },
33413     
33414     /**
33415      * Gets the minimum size for the resizing element
33416      * @return {Number} The minimum size
33417      */
33418     getMinimumSize : function(){
33419         return this.minSize;
33420     },
33421     
33422     /**
33423      * Sets the minimum size for the resizing element
33424      * @param {Number} minSize The minimum size
33425      */
33426     setMinimumSize : function(minSize){
33427         this.minSize = minSize;
33428     },
33429     
33430     /**
33431      * Gets the maximum size for the resizing element
33432      * @return {Number} The maximum size
33433      */
33434     getMaximumSize : function(){
33435         return this.maxSize;
33436     },
33437     
33438     /**
33439      * Sets the maximum size for the resizing element
33440      * @param {Number} maxSize The maximum size
33441      */
33442     setMaximumSize : function(maxSize){
33443         this.maxSize = maxSize;
33444     },
33445     
33446     /**
33447      * Sets the initialize size for the resizing element
33448      * @param {Number} size The initial size
33449      */
33450     setCurrentSize : function(size){
33451         var oldAnimate = this.animate;
33452         this.animate = false;
33453         this.adapter.setElementSize(this, size);
33454         this.animate = oldAnimate;
33455     },
33456     
33457     /**
33458      * Destroy this splitbar. 
33459      * @param {Boolean} removeEl True to remove the element
33460      */
33461     destroy : function(removeEl){
33462         if(this.shim){
33463             this.shim.remove();
33464         }
33465         this.dd.unreg();
33466         this.proxy.parentNode.removeChild(this.proxy);
33467         if(removeEl){
33468             this.el.remove();
33469         }
33470     }
33471 });
33472
33473 /**
33474  * @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.
33475  */
33476 Roo.bootstrap.SplitBar.createProxy = function(dir){
33477     var proxy = new Roo.Element(document.createElement("div"));
33478     proxy.unselectable();
33479     var cls = 'roo-splitbar-proxy';
33480     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
33481     document.body.appendChild(proxy.dom);
33482     return proxy.dom;
33483 };
33484
33485 /** 
33486  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
33487  * Default Adapter. It assumes the splitter and resizing element are not positioned
33488  * elements and only gets/sets the width of the element. Generally used for table based layouts.
33489  */
33490 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
33491 };
33492
33493 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
33494     // do nothing for now
33495     init : function(s){
33496     
33497     },
33498     /**
33499      * Called before drag operations to get the current size of the resizing element. 
33500      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33501      */
33502      getElementSize : function(s){
33503         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33504             return s.resizingEl.getWidth();
33505         }else{
33506             return s.resizingEl.getHeight();
33507         }
33508     },
33509     
33510     /**
33511      * Called after drag operations to set the size of the resizing element.
33512      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33513      * @param {Number} newSize The new size to set
33514      * @param {Function} onComplete A function to be invoked when resizing is complete
33515      */
33516     setElementSize : function(s, newSize, onComplete){
33517         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33518             if(!s.animate){
33519                 s.resizingEl.setWidth(newSize);
33520                 if(onComplete){
33521                     onComplete(s, newSize);
33522                 }
33523             }else{
33524                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
33525             }
33526         }else{
33527             
33528             if(!s.animate){
33529                 s.resizingEl.setHeight(newSize);
33530                 if(onComplete){
33531                     onComplete(s, newSize);
33532                 }
33533             }else{
33534                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
33535             }
33536         }
33537     }
33538 };
33539
33540 /** 
33541  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
33542  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
33543  * Adapter that  moves the splitter element to align with the resized sizing element. 
33544  * Used with an absolute positioned SplitBar.
33545  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
33546  * document.body, make sure you assign an id to the body element.
33547  */
33548 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
33549     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33550     this.container = Roo.get(container);
33551 };
33552
33553 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
33554     init : function(s){
33555         this.basic.init(s);
33556     },
33557     
33558     getElementSize : function(s){
33559         return this.basic.getElementSize(s);
33560     },
33561     
33562     setElementSize : function(s, newSize, onComplete){
33563         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
33564     },
33565     
33566     moveSplitter : function(s){
33567         var yes = Roo.bootstrap.SplitBar;
33568         switch(s.placement){
33569             case yes.LEFT:
33570                 s.el.setX(s.resizingEl.getRight());
33571                 break;
33572             case yes.RIGHT:
33573                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
33574                 break;
33575             case yes.TOP:
33576                 s.el.setY(s.resizingEl.getBottom());
33577                 break;
33578             case yes.BOTTOM:
33579                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
33580                 break;
33581         }
33582     }
33583 };
33584
33585 /**
33586  * Orientation constant - Create a vertical SplitBar
33587  * @static
33588  * @type Number
33589  */
33590 Roo.bootstrap.SplitBar.VERTICAL = 1;
33591
33592 /**
33593  * Orientation constant - Create a horizontal SplitBar
33594  * @static
33595  * @type Number
33596  */
33597 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
33598
33599 /**
33600  * Placement constant - The resizing element is to the left of the splitter element
33601  * @static
33602  * @type Number
33603  */
33604 Roo.bootstrap.SplitBar.LEFT = 1;
33605
33606 /**
33607  * Placement constant - The resizing element is to the right of the splitter element
33608  * @static
33609  * @type Number
33610  */
33611 Roo.bootstrap.SplitBar.RIGHT = 2;
33612
33613 /**
33614  * Placement constant - The resizing element is positioned above the splitter element
33615  * @static
33616  * @type Number
33617  */
33618 Roo.bootstrap.SplitBar.TOP = 3;
33619
33620 /**
33621  * Placement constant - The resizing element is positioned under splitter element
33622  * @static
33623  * @type Number
33624  */
33625 Roo.bootstrap.SplitBar.BOTTOM = 4;
33626 Roo.namespace("Roo.bootstrap.layout");/*
33627  * Based on:
33628  * Ext JS Library 1.1.1
33629  * Copyright(c) 2006-2007, Ext JS, LLC.
33630  *
33631  * Originally Released Under LGPL - original licence link has changed is not relivant.
33632  *
33633  * Fork - LGPL
33634  * <script type="text/javascript">
33635  */
33636
33637 /**
33638  * @class Roo.bootstrap.layout.Manager
33639  * @extends Roo.bootstrap.Component
33640  * Base class for layout managers.
33641  */
33642 Roo.bootstrap.layout.Manager = function(config)
33643 {
33644     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
33645
33646
33647
33648
33649
33650     /** false to disable window resize monitoring @type Boolean */
33651     this.monitorWindowResize = true;
33652     this.regions = {};
33653     this.addEvents({
33654         /**
33655          * @event layout
33656          * Fires when a layout is performed.
33657          * @param {Roo.LayoutManager} this
33658          */
33659         "layout" : true,
33660         /**
33661          * @event regionresized
33662          * Fires when the user resizes a region.
33663          * @param {Roo.LayoutRegion} region The resized region
33664          * @param {Number} newSize The new size (width for east/west, height for north/south)
33665          */
33666         "regionresized" : true,
33667         /**
33668          * @event regioncollapsed
33669          * Fires when a region is collapsed.
33670          * @param {Roo.LayoutRegion} region The collapsed region
33671          */
33672         "regioncollapsed" : true,
33673         /**
33674          * @event regionexpanded
33675          * Fires when a region is expanded.
33676          * @param {Roo.LayoutRegion} region The expanded region
33677          */
33678         "regionexpanded" : true
33679     });
33680     this.updating = false;
33681
33682     if (config.el) {
33683         this.el = Roo.get(config.el);
33684         this.initEvents();
33685     }
33686
33687 };
33688
33689 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
33690
33691
33692     regions : null,
33693
33694     monitorWindowResize : true,
33695
33696
33697     updating : false,
33698
33699
33700     onRender : function(ct, position)
33701     {
33702         if(!this.el){
33703             this.el = Roo.get(ct);
33704             this.initEvents();
33705         }
33706         //this.fireEvent('render',this);
33707     },
33708
33709
33710     initEvents: function()
33711     {
33712
33713
33714         // ie scrollbar fix
33715         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
33716             document.body.scroll = "no";
33717         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
33718             this.el.position('relative');
33719         }
33720         this.id = this.el.id;
33721         this.el.addClass("roo-layout-container");
33722         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
33723         if(this.el.dom != document.body ) {
33724             this.el.on('resize', this.layout,this);
33725             this.el.on('show', this.layout,this);
33726         }
33727
33728     },
33729
33730     /**
33731      * Returns true if this layout is currently being updated
33732      * @return {Boolean}
33733      */
33734     isUpdating : function(){
33735         return this.updating;
33736     },
33737
33738     /**
33739      * Suspend the LayoutManager from doing auto-layouts while
33740      * making multiple add or remove calls
33741      */
33742     beginUpdate : function(){
33743         this.updating = true;
33744     },
33745
33746     /**
33747      * Restore auto-layouts and optionally disable the manager from performing a layout
33748      * @param {Boolean} noLayout true to disable a layout update
33749      */
33750     endUpdate : function(noLayout){
33751         this.updating = false;
33752         if(!noLayout){
33753             this.layout();
33754         }
33755     },
33756
33757     layout: function(){
33758         // abstract...
33759     },
33760
33761     onRegionResized : function(region, newSize){
33762         this.fireEvent("regionresized", region, newSize);
33763         this.layout();
33764     },
33765
33766     onRegionCollapsed : function(region){
33767         this.fireEvent("regioncollapsed", region);
33768     },
33769
33770     onRegionExpanded : function(region){
33771         this.fireEvent("regionexpanded", region);
33772     },
33773
33774     /**
33775      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
33776      * performs box-model adjustments.
33777      * @return {Object} The size as an object {width: (the width), height: (the height)}
33778      */
33779     getViewSize : function()
33780     {
33781         var size;
33782         if(this.el.dom != document.body){
33783             size = this.el.getSize();
33784         }else{
33785             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
33786         }
33787         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
33788         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
33789         return size;
33790     },
33791
33792     /**
33793      * Returns the Element this layout is bound to.
33794      * @return {Roo.Element}
33795      */
33796     getEl : function(){
33797         return this.el;
33798     },
33799
33800     /**
33801      * Returns the specified region.
33802      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
33803      * @return {Roo.LayoutRegion}
33804      */
33805     getRegion : function(target){
33806         return this.regions[target.toLowerCase()];
33807     },
33808
33809     onWindowResize : function(){
33810         if(this.monitorWindowResize){
33811             this.layout();
33812         }
33813     }
33814 });
33815 /*
33816  * Based on:
33817  * Ext JS Library 1.1.1
33818  * Copyright(c) 2006-2007, Ext JS, LLC.
33819  *
33820  * Originally Released Under LGPL - original licence link has changed is not relivant.
33821  *
33822  * Fork - LGPL
33823  * <script type="text/javascript">
33824  */
33825 /**
33826  * @class Roo.bootstrap.layout.Border
33827  * @extends Roo.bootstrap.layout.Manager
33828  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
33829  * please see: examples/bootstrap/nested.html<br><br>
33830  
33831 <b>The container the layout is rendered into can be either the body element or any other element.
33832 If it is not the body element, the container needs to either be an absolute positioned element,
33833 or you will need to add "position:relative" to the css of the container.  You will also need to specify
33834 the container size if it is not the body element.</b>
33835
33836 * @constructor
33837 * Create a new Border
33838 * @param {Object} config Configuration options
33839  */
33840 Roo.bootstrap.layout.Border = function(config){
33841     config = config || {};
33842     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
33843     
33844     
33845     
33846     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
33847         if(config[region]){
33848             config[region].region = region;
33849             this.addRegion(config[region]);
33850         }
33851     },this);
33852     
33853 };
33854
33855 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
33856
33857 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
33858     /**
33859      * Creates and adds a new region if it doesn't already exist.
33860      * @param {String} target The target region key (north, south, east, west or center).
33861      * @param {Object} config The regions config object
33862      * @return {BorderLayoutRegion} The new region
33863      */
33864     addRegion : function(config)
33865     {
33866         if(!this.regions[config.region]){
33867             var r = this.factory(config);
33868             this.bindRegion(r);
33869         }
33870         return this.regions[config.region];
33871     },
33872
33873     // private (kinda)
33874     bindRegion : function(r){
33875         this.regions[r.config.region] = r;
33876         
33877         r.on("visibilitychange",    this.layout, this);
33878         r.on("paneladded",          this.layout, this);
33879         r.on("panelremoved",        this.layout, this);
33880         r.on("invalidated",         this.layout, this);
33881         r.on("resized",             this.onRegionResized, this);
33882         r.on("collapsed",           this.onRegionCollapsed, this);
33883         r.on("expanded",            this.onRegionExpanded, this);
33884     },
33885
33886     /**
33887      * Performs a layout update.
33888      */
33889     layout : function()
33890     {
33891         if(this.updating) {
33892             return;
33893         }
33894         
33895         // render all the rebions if they have not been done alreayd?
33896         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
33897             if(this.regions[region] && !this.regions[region].bodyEl){
33898                 this.regions[region].onRender(this.el)
33899             }
33900         },this);
33901         
33902         var size = this.getViewSize();
33903         var w = size.width;
33904         var h = size.height;
33905         var centerW = w;
33906         var centerH = h;
33907         var centerY = 0;
33908         var centerX = 0;
33909         //var x = 0, y = 0;
33910
33911         var rs = this.regions;
33912         var north = rs["north"];
33913         var south = rs["south"]; 
33914         var west = rs["west"];
33915         var east = rs["east"];
33916         var center = rs["center"];
33917         //if(this.hideOnLayout){ // not supported anymore
33918             //c.el.setStyle("display", "none");
33919         //}
33920         if(north && north.isVisible()){
33921             var b = north.getBox();
33922             var m = north.getMargins();
33923             b.width = w - (m.left+m.right);
33924             b.x = m.left;
33925             b.y = m.top;
33926             centerY = b.height + b.y + m.bottom;
33927             centerH -= centerY;
33928             north.updateBox(this.safeBox(b));
33929         }
33930         if(south && south.isVisible()){
33931             var b = south.getBox();
33932             var m = south.getMargins();
33933             b.width = w - (m.left+m.right);
33934             b.x = m.left;
33935             var totalHeight = (b.height + m.top + m.bottom);
33936             b.y = h - totalHeight + m.top;
33937             centerH -= totalHeight;
33938             south.updateBox(this.safeBox(b));
33939         }
33940         if(west && west.isVisible()){
33941             var b = west.getBox();
33942             var m = west.getMargins();
33943             b.height = centerH - (m.top+m.bottom);
33944             b.x = m.left;
33945             b.y = centerY + m.top;
33946             var totalWidth = (b.width + m.left + m.right);
33947             centerX += totalWidth;
33948             centerW -= totalWidth;
33949             west.updateBox(this.safeBox(b));
33950         }
33951         if(east && east.isVisible()){
33952             var b = east.getBox();
33953             var m = east.getMargins();
33954             b.height = centerH - (m.top+m.bottom);
33955             var totalWidth = (b.width + m.left + m.right);
33956             b.x = w - totalWidth + m.left;
33957             b.y = centerY + m.top;
33958             centerW -= totalWidth;
33959             east.updateBox(this.safeBox(b));
33960         }
33961         if(center){
33962             var m = center.getMargins();
33963             var centerBox = {
33964                 x: centerX + m.left,
33965                 y: centerY + m.top,
33966                 width: centerW - (m.left+m.right),
33967                 height: centerH - (m.top+m.bottom)
33968             };
33969             //if(this.hideOnLayout){
33970                 //center.el.setStyle("display", "block");
33971             //}
33972             center.updateBox(this.safeBox(centerBox));
33973         }
33974         this.el.repaint();
33975         this.fireEvent("layout", this);
33976     },
33977
33978     // private
33979     safeBox : function(box){
33980         box.width = Math.max(0, box.width);
33981         box.height = Math.max(0, box.height);
33982         return box;
33983     },
33984
33985     /**
33986      * Adds a ContentPanel (or subclass) to this layout.
33987      * @param {String} target The target region key (north, south, east, west or center).
33988      * @param {Roo.ContentPanel} panel The panel to add
33989      * @return {Roo.ContentPanel} The added panel
33990      */
33991     add : function(target, panel){
33992          
33993         target = target.toLowerCase();
33994         return this.regions[target].add(panel);
33995     },
33996
33997     /**
33998      * Remove a ContentPanel (or subclass) to this layout.
33999      * @param {String} target The target region key (north, south, east, west or center).
34000      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34001      * @return {Roo.ContentPanel} The removed panel
34002      */
34003     remove : function(target, panel){
34004         target = target.toLowerCase();
34005         return this.regions[target].remove(panel);
34006     },
34007
34008     /**
34009      * Searches all regions for a panel with the specified id
34010      * @param {String} panelId
34011      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34012      */
34013     findPanel : function(panelId){
34014         var rs = this.regions;
34015         for(var target in rs){
34016             if(typeof rs[target] != "function"){
34017                 var p = rs[target].getPanel(panelId);
34018                 if(p){
34019                     return p;
34020                 }
34021             }
34022         }
34023         return null;
34024     },
34025
34026     /**
34027      * Searches all regions for a panel with the specified id and activates (shows) it.
34028      * @param {String/ContentPanel} panelId The panels id or the panel itself
34029      * @return {Roo.ContentPanel} The shown panel or null
34030      */
34031     showPanel : function(panelId) {
34032       var rs = this.regions;
34033       for(var target in rs){
34034          var r = rs[target];
34035          if(typeof r != "function"){
34036             if(r.hasPanel(panelId)){
34037                return r.showPanel(panelId);
34038             }
34039          }
34040       }
34041       return null;
34042    },
34043
34044    /**
34045      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34046      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34047      */
34048    /*
34049     restoreState : function(provider){
34050         if(!provider){
34051             provider = Roo.state.Manager;
34052         }
34053         var sm = new Roo.LayoutStateManager();
34054         sm.init(this, provider);
34055     },
34056 */
34057  
34058  
34059     /**
34060      * Adds a xtype elements to the layout.
34061      * <pre><code>
34062
34063 layout.addxtype({
34064        xtype : 'ContentPanel',
34065        region: 'west',
34066        items: [ .... ]
34067    }
34068 );
34069
34070 layout.addxtype({
34071         xtype : 'NestedLayoutPanel',
34072         region: 'west',
34073         layout: {
34074            center: { },
34075            west: { }   
34076         },
34077         items : [ ... list of content panels or nested layout panels.. ]
34078    }
34079 );
34080 </code></pre>
34081      * @param {Object} cfg Xtype definition of item to add.
34082      */
34083     addxtype : function(cfg)
34084     {
34085         // basically accepts a pannel...
34086         // can accept a layout region..!?!?
34087         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34088         
34089         
34090         // theory?  children can only be panels??
34091         
34092         //if (!cfg.xtype.match(/Panel$/)) {
34093         //    return false;
34094         //}
34095         var ret = false;
34096         
34097         if (typeof(cfg.region) == 'undefined') {
34098             Roo.log("Failed to add Panel, region was not set");
34099             Roo.log(cfg);
34100             return false;
34101         }
34102         var region = cfg.region;
34103         delete cfg.region;
34104         
34105           
34106         var xitems = [];
34107         if (cfg.items) {
34108             xitems = cfg.items;
34109             delete cfg.items;
34110         }
34111         var nb = false;
34112         
34113         switch(cfg.xtype) 
34114         {
34115             case 'Content':  // ContentPanel (el, cfg)
34116             case 'Scroll':  // ContentPanel (el, cfg)
34117             case 'View': 
34118                 cfg.autoCreate = true;
34119                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34120                 //} else {
34121                 //    var el = this.el.createChild();
34122                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34123                 //}
34124                 
34125                 this.add(region, ret);
34126                 break;
34127             
34128             /*
34129             case 'TreePanel': // our new panel!
34130                 cfg.el = this.el.createChild();
34131                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34132                 this.add(region, ret);
34133                 break;
34134             */
34135             
34136             case 'Nest': 
34137                 // create a new Layout (which is  a Border Layout...
34138                 
34139                 var clayout = cfg.layout;
34140                 clayout.el  = this.el.createChild();
34141                 clayout.items   = clayout.items  || [];
34142                 
34143                 delete cfg.layout;
34144                 
34145                 // replace this exitems with the clayout ones..
34146                 xitems = clayout.items;
34147                  
34148                 // force background off if it's in center...
34149                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34150                     cfg.background = false;
34151                 }
34152                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34153                 
34154                 
34155                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34156                 //console.log('adding nested layout panel '  + cfg.toSource());
34157                 this.add(region, ret);
34158                 nb = {}; /// find first...
34159                 break;
34160             
34161             case 'Grid':
34162                 
34163                 // needs grid and region
34164                 
34165                 //var el = this.getRegion(region).el.createChild();
34166                 /*
34167                  *var el = this.el.createChild();
34168                 // create the grid first...
34169                 cfg.grid.container = el;
34170                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34171                 */
34172                 
34173                 if (region == 'center' && this.active ) {
34174                     cfg.background = false;
34175                 }
34176                 
34177                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34178                 
34179                 this.add(region, ret);
34180                 /*
34181                 if (cfg.background) {
34182                     // render grid on panel activation (if panel background)
34183                     ret.on('activate', function(gp) {
34184                         if (!gp.grid.rendered) {
34185                     //        gp.grid.render(el);
34186                         }
34187                     });
34188                 } else {
34189                   //  cfg.grid.render(el);
34190                 }
34191                 */
34192                 break;
34193            
34194            
34195             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34196                 // it was the old xcomponent building that caused this before.
34197                 // espeically if border is the top element in the tree.
34198                 ret = this;
34199                 break; 
34200                 
34201                     
34202                 
34203                 
34204                 
34205             default:
34206                 /*
34207                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34208                     
34209                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34210                     this.add(region, ret);
34211                 } else {
34212                 */
34213                     Roo.log(cfg);
34214                     throw "Can not add '" + cfg.xtype + "' to Border";
34215                     return null;
34216              
34217                                 
34218              
34219         }
34220         this.beginUpdate();
34221         // add children..
34222         var region = '';
34223         var abn = {};
34224         Roo.each(xitems, function(i)  {
34225             region = nb && i.region ? i.region : false;
34226             
34227             var add = ret.addxtype(i);
34228            
34229             if (region) {
34230                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34231                 if (!i.background) {
34232                     abn[region] = nb[region] ;
34233                 }
34234             }
34235             
34236         });
34237         this.endUpdate();
34238
34239         // make the last non-background panel active..
34240         //if (nb) { Roo.log(abn); }
34241         if (nb) {
34242             
34243             for(var r in abn) {
34244                 region = this.getRegion(r);
34245                 if (region) {
34246                     // tried using nb[r], but it does not work..
34247                      
34248                     region.showPanel(abn[r]);
34249                    
34250                 }
34251             }
34252         }
34253         return ret;
34254         
34255     },
34256     
34257     
34258 // private
34259     factory : function(cfg)
34260     {
34261         
34262         var validRegions = Roo.bootstrap.layout.Border.regions;
34263
34264         var target = cfg.region;
34265         cfg.mgr = this;
34266         
34267         var r = Roo.bootstrap.layout;
34268         Roo.log(target);
34269         switch(target){
34270             case "north":
34271                 return new r.North(cfg);
34272             case "south":
34273                 return new r.South(cfg);
34274             case "east":
34275                 return new r.East(cfg);
34276             case "west":
34277                 return new r.West(cfg);
34278             case "center":
34279                 return new r.Center(cfg);
34280         }
34281         throw 'Layout region "'+target+'" not supported.';
34282     }
34283     
34284     
34285 });
34286  /*
34287  * Based on:
34288  * Ext JS Library 1.1.1
34289  * Copyright(c) 2006-2007, Ext JS, LLC.
34290  *
34291  * Originally Released Under LGPL - original licence link has changed is not relivant.
34292  *
34293  * Fork - LGPL
34294  * <script type="text/javascript">
34295  */
34296  
34297 /**
34298  * @class Roo.bootstrap.layout.Basic
34299  * @extends Roo.util.Observable
34300  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34301  * and does not have a titlebar, tabs or any other features. All it does is size and position 
34302  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34303  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34304  * @cfg {string}   region  the region that it inhabits..
34305  * @cfg {bool}   skipConfig skip config?
34306  * 
34307
34308  */
34309 Roo.bootstrap.layout.Basic = function(config){
34310     
34311     this.mgr = config.mgr;
34312     
34313     this.position = config.region;
34314     
34315     var skipConfig = config.skipConfig;
34316     
34317     this.events = {
34318         /**
34319          * @scope Roo.BasicLayoutRegion
34320          */
34321         
34322         /**
34323          * @event beforeremove
34324          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34325          * @param {Roo.LayoutRegion} this
34326          * @param {Roo.ContentPanel} panel The panel
34327          * @param {Object} e The cancel event object
34328          */
34329         "beforeremove" : true,
34330         /**
34331          * @event invalidated
34332          * Fires when the layout for this region is changed.
34333          * @param {Roo.LayoutRegion} this
34334          */
34335         "invalidated" : true,
34336         /**
34337          * @event visibilitychange
34338          * Fires when this region is shown or hidden 
34339          * @param {Roo.LayoutRegion} this
34340          * @param {Boolean} visibility true or false
34341          */
34342         "visibilitychange" : true,
34343         /**
34344          * @event paneladded
34345          * Fires when a panel is added. 
34346          * @param {Roo.LayoutRegion} this
34347          * @param {Roo.ContentPanel} panel The panel
34348          */
34349         "paneladded" : true,
34350         /**
34351          * @event panelremoved
34352          * Fires when a panel is removed. 
34353          * @param {Roo.LayoutRegion} this
34354          * @param {Roo.ContentPanel} panel The panel
34355          */
34356         "panelremoved" : true,
34357         /**
34358          * @event beforecollapse
34359          * Fires when this region before collapse.
34360          * @param {Roo.LayoutRegion} this
34361          */
34362         "beforecollapse" : true,
34363         /**
34364          * @event collapsed
34365          * Fires when this region is collapsed.
34366          * @param {Roo.LayoutRegion} this
34367          */
34368         "collapsed" : true,
34369         /**
34370          * @event expanded
34371          * Fires when this region is expanded.
34372          * @param {Roo.LayoutRegion} this
34373          */
34374         "expanded" : true,
34375         /**
34376          * @event slideshow
34377          * Fires when this region is slid into view.
34378          * @param {Roo.LayoutRegion} this
34379          */
34380         "slideshow" : true,
34381         /**
34382          * @event slidehide
34383          * Fires when this region slides out of view. 
34384          * @param {Roo.LayoutRegion} this
34385          */
34386         "slidehide" : true,
34387         /**
34388          * @event panelactivated
34389          * Fires when a panel is activated. 
34390          * @param {Roo.LayoutRegion} this
34391          * @param {Roo.ContentPanel} panel The activated panel
34392          */
34393         "panelactivated" : true,
34394         /**
34395          * @event resized
34396          * Fires when the user resizes this region. 
34397          * @param {Roo.LayoutRegion} this
34398          * @param {Number} newSize The new size (width for east/west, height for north/south)
34399          */
34400         "resized" : true
34401     };
34402     /** A collection of panels in this region. @type Roo.util.MixedCollection */
34403     this.panels = new Roo.util.MixedCollection();
34404     this.panels.getKey = this.getPanelId.createDelegate(this);
34405     this.box = null;
34406     this.activePanel = null;
34407     // ensure listeners are added...
34408     
34409     if (config.listeners || config.events) {
34410         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
34411             listeners : config.listeners || {},
34412             events : config.events || {}
34413         });
34414     }
34415     
34416     if(skipConfig !== true){
34417         this.applyConfig(config);
34418     }
34419 };
34420
34421 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
34422 {
34423     getPanelId : function(p){
34424         return p.getId();
34425     },
34426     
34427     applyConfig : function(config){
34428         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34429         this.config = config;
34430         
34431     },
34432     
34433     /**
34434      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
34435      * the width, for horizontal (north, south) the height.
34436      * @param {Number} newSize The new width or height
34437      */
34438     resizeTo : function(newSize){
34439         var el = this.el ? this.el :
34440                  (this.activePanel ? this.activePanel.getEl() : null);
34441         if(el){
34442             switch(this.position){
34443                 case "east":
34444                 case "west":
34445                     el.setWidth(newSize);
34446                     this.fireEvent("resized", this, newSize);
34447                 break;
34448                 case "north":
34449                 case "south":
34450                     el.setHeight(newSize);
34451                     this.fireEvent("resized", this, newSize);
34452                 break;                
34453             }
34454         }
34455     },
34456     
34457     getBox : function(){
34458         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34459     },
34460     
34461     getMargins : function(){
34462         return this.margins;
34463     },
34464     
34465     updateBox : function(box){
34466         this.box = box;
34467         var el = this.activePanel.getEl();
34468         el.dom.style.left = box.x + "px";
34469         el.dom.style.top = box.y + "px";
34470         this.activePanel.setSize(box.width, box.height);
34471     },
34472     
34473     /**
34474      * Returns the container element for this region.
34475      * @return {Roo.Element}
34476      */
34477     getEl : function(){
34478         return this.activePanel;
34479     },
34480     
34481     /**
34482      * Returns true if this region is currently visible.
34483      * @return {Boolean}
34484      */
34485     isVisible : function(){
34486         return this.activePanel ? true : false;
34487     },
34488     
34489     setActivePanel : function(panel){
34490         panel = this.getPanel(panel);
34491         if(this.activePanel && this.activePanel != panel){
34492             this.activePanel.setActiveState(false);
34493             this.activePanel.getEl().setLeftTop(-10000,-10000);
34494         }
34495         this.activePanel = panel;
34496         panel.setActiveState(true);
34497         if(this.box){
34498             panel.setSize(this.box.width, this.box.height);
34499         }
34500         this.fireEvent("panelactivated", this, panel);
34501         this.fireEvent("invalidated");
34502     },
34503     
34504     /**
34505      * Show the specified panel.
34506      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34507      * @return {Roo.ContentPanel} The shown panel or null
34508      */
34509     showPanel : function(panel){
34510         panel = this.getPanel(panel);
34511         if(panel){
34512             this.setActivePanel(panel);
34513         }
34514         return panel;
34515     },
34516     
34517     /**
34518      * Get the active panel for this region.
34519      * @return {Roo.ContentPanel} The active panel or null
34520      */
34521     getActivePanel : function(){
34522         return this.activePanel;
34523     },
34524     
34525     /**
34526      * Add the passed ContentPanel(s)
34527      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34528      * @return {Roo.ContentPanel} The panel added (if only one was added)
34529      */
34530     add : function(panel){
34531         if(arguments.length > 1){
34532             for(var i = 0, len = arguments.length; i < len; i++) {
34533                 this.add(arguments[i]);
34534             }
34535             return null;
34536         }
34537         if(this.hasPanel(panel)){
34538             this.showPanel(panel);
34539             return panel;
34540         }
34541         var el = panel.getEl();
34542         if(el.dom.parentNode != this.mgr.el.dom){
34543             this.mgr.el.dom.appendChild(el.dom);
34544         }
34545         if(panel.setRegion){
34546             panel.setRegion(this);
34547         }
34548         this.panels.add(panel);
34549         el.setStyle("position", "absolute");
34550         if(!panel.background){
34551             this.setActivePanel(panel);
34552             if(this.config.initialSize && this.panels.getCount()==1){
34553                 this.resizeTo(this.config.initialSize);
34554             }
34555         }
34556         this.fireEvent("paneladded", this, panel);
34557         return panel;
34558     },
34559     
34560     /**
34561      * Returns true if the panel is in this region.
34562      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34563      * @return {Boolean}
34564      */
34565     hasPanel : function(panel){
34566         if(typeof panel == "object"){ // must be panel obj
34567             panel = panel.getId();
34568         }
34569         return this.getPanel(panel) ? true : false;
34570     },
34571     
34572     /**
34573      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34574      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34575      * @param {Boolean} preservePanel Overrides the config preservePanel option
34576      * @return {Roo.ContentPanel} The panel that was removed
34577      */
34578     remove : function(panel, preservePanel){
34579         panel = this.getPanel(panel);
34580         if(!panel){
34581             return null;
34582         }
34583         var e = {};
34584         this.fireEvent("beforeremove", this, panel, e);
34585         if(e.cancel === true){
34586             return null;
34587         }
34588         var panelId = panel.getId();
34589         this.panels.removeKey(panelId);
34590         return panel;
34591     },
34592     
34593     /**
34594      * Returns the panel specified or null if it's not in this region.
34595      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34596      * @return {Roo.ContentPanel}
34597      */
34598     getPanel : function(id){
34599         if(typeof id == "object"){ // must be panel obj
34600             return id;
34601         }
34602         return this.panels.get(id);
34603     },
34604     
34605     /**
34606      * Returns this regions position (north/south/east/west/center).
34607      * @return {String} 
34608      */
34609     getPosition: function(){
34610         return this.position;    
34611     }
34612 });/*
34613  * Based on:
34614  * Ext JS Library 1.1.1
34615  * Copyright(c) 2006-2007, Ext JS, LLC.
34616  *
34617  * Originally Released Under LGPL - original licence link has changed is not relivant.
34618  *
34619  * Fork - LGPL
34620  * <script type="text/javascript">
34621  */
34622  
34623 /**
34624  * @class Roo.bootstrap.layout.Region
34625  * @extends Roo.bootstrap.layout.Basic
34626  * This class represents a region in a layout manager.
34627  
34628  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
34629  * @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})
34630  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
34631  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
34632  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
34633  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
34634  * @cfg {String}    title           The title for the region (overrides panel titles)
34635  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
34636  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
34637  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
34638  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
34639  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
34640  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
34641  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
34642  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
34643  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
34644  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
34645
34646  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
34647  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
34648  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
34649  * @cfg {Number}    width           For East/West panels
34650  * @cfg {Number}    height          For North/South panels
34651  * @cfg {Boolean}   split           To show the splitter
34652  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
34653  * 
34654  * @cfg {string}   cls             Extra CSS classes to add to region
34655  * 
34656  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34657  * @cfg {string}   region  the region that it inhabits..
34658  *
34659
34660  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
34661  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
34662
34663  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
34664  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
34665  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
34666  */
34667 Roo.bootstrap.layout.Region = function(config)
34668 {
34669     this.applyConfig(config);
34670
34671     var mgr = config.mgr;
34672     var pos = config.region;
34673     config.skipConfig = true;
34674     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
34675     
34676     if (mgr.el) {
34677         this.onRender(mgr.el);   
34678     }
34679      
34680     this.visible = true;
34681     this.collapsed = false;
34682     this.unrendered_panels = [];
34683 };
34684
34685 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
34686
34687     position: '', // set by wrapper (eg. north/south etc..)
34688     unrendered_panels : null,  // unrendered panels.
34689     createBody : function(){
34690         /** This region's body element 
34691         * @type Roo.Element */
34692         this.bodyEl = this.el.createChild({
34693                 tag: "div",
34694                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
34695         });
34696     },
34697
34698     onRender: function(ctr, pos)
34699     {
34700         var dh = Roo.DomHelper;
34701         /** This region's container element 
34702         * @type Roo.Element */
34703         this.el = dh.append(ctr.dom, {
34704                 tag: "div",
34705                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
34706             }, true);
34707         /** This region's title element 
34708         * @type Roo.Element */
34709     
34710         this.titleEl = dh.append(this.el.dom,
34711             {
34712                     tag: "div",
34713                     unselectable: "on",
34714                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
34715                     children:[
34716                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
34717                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
34718                     ]}, true);
34719         
34720         this.titleEl.enableDisplayMode();
34721         /** This region's title text element 
34722         * @type HTMLElement */
34723         this.titleTextEl = this.titleEl.dom.firstChild;
34724         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
34725         /*
34726         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
34727         this.closeBtn.enableDisplayMode();
34728         this.closeBtn.on("click", this.closeClicked, this);
34729         this.closeBtn.hide();
34730     */
34731         this.createBody(this.config);
34732         if(this.config.hideWhenEmpty){
34733             this.hide();
34734             this.on("paneladded", this.validateVisibility, this);
34735             this.on("panelremoved", this.validateVisibility, this);
34736         }
34737         if(this.autoScroll){
34738             this.bodyEl.setStyle("overflow", "auto");
34739         }else{
34740             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
34741         }
34742         //if(c.titlebar !== false){
34743             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
34744                 this.titleEl.hide();
34745             }else{
34746                 this.titleEl.show();
34747                 if(this.config.title){
34748                     this.titleTextEl.innerHTML = this.config.title;
34749                 }
34750             }
34751         //}
34752         if(this.config.collapsed){
34753             this.collapse(true);
34754         }
34755         if(this.config.hidden){
34756             this.hide();
34757         }
34758         
34759         if (this.unrendered_panels && this.unrendered_panels.length) {
34760             for (var i =0;i< this.unrendered_panels.length; i++) {
34761                 this.add(this.unrendered_panels[i]);
34762             }
34763             this.unrendered_panels = null;
34764             
34765         }
34766         
34767     },
34768     
34769     applyConfig : function(c)
34770     {
34771         /*
34772          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
34773             var dh = Roo.DomHelper;
34774             if(c.titlebar !== false){
34775                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
34776                 this.collapseBtn.on("click", this.collapse, this);
34777                 this.collapseBtn.enableDisplayMode();
34778                 /*
34779                 if(c.showPin === true || this.showPin){
34780                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
34781                     this.stickBtn.enableDisplayMode();
34782                     this.stickBtn.on("click", this.expand, this);
34783                     this.stickBtn.hide();
34784                 }
34785                 
34786             }
34787             */
34788             /** This region's collapsed element
34789             * @type Roo.Element */
34790             /*
34791              *
34792             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
34793                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
34794             ]}, true);
34795             
34796             if(c.floatable !== false){
34797                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
34798                this.collapsedEl.on("click", this.collapseClick, this);
34799             }
34800
34801             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
34802                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
34803                    id: "message", unselectable: "on", style:{"float":"left"}});
34804                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
34805              }
34806             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
34807             this.expandBtn.on("click", this.expand, this);
34808             
34809         }
34810         
34811         if(this.collapseBtn){
34812             this.collapseBtn.setVisible(c.collapsible == true);
34813         }
34814         
34815         this.cmargins = c.cmargins || this.cmargins ||
34816                          (this.position == "west" || this.position == "east" ?
34817                              {top: 0, left: 2, right:2, bottom: 0} :
34818                              {top: 2, left: 0, right:0, bottom: 2});
34819         */
34820         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34821         
34822         
34823         this.bottomTabs = c.tabPosition != "top";
34824         
34825         this.autoScroll = c.autoScroll || false;
34826         
34827         
34828        
34829         
34830         this.duration = c.duration || .30;
34831         this.slideDuration = c.slideDuration || .45;
34832         this.config = c;
34833        
34834     },
34835     /**
34836      * Returns true if this region is currently visible.
34837      * @return {Boolean}
34838      */
34839     isVisible : function(){
34840         return this.visible;
34841     },
34842
34843     /**
34844      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
34845      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
34846      */
34847     //setCollapsedTitle : function(title){
34848     //    title = title || "&#160;";
34849      //   if(this.collapsedTitleTextEl){
34850       //      this.collapsedTitleTextEl.innerHTML = title;
34851        // }
34852     //},
34853
34854     getBox : function(){
34855         var b;
34856       //  if(!this.collapsed){
34857             b = this.el.getBox(false, true);
34858        // }else{
34859           //  b = this.collapsedEl.getBox(false, true);
34860         //}
34861         return b;
34862     },
34863
34864     getMargins : function(){
34865         return this.margins;
34866         //return this.collapsed ? this.cmargins : this.margins;
34867     },
34868 /*
34869     highlight : function(){
34870         this.el.addClass("x-layout-panel-dragover");
34871     },
34872
34873     unhighlight : function(){
34874         this.el.removeClass("x-layout-panel-dragover");
34875     },
34876 */
34877     updateBox : function(box)
34878     {
34879         if (!this.bodyEl) {
34880             return; // not rendered yet..
34881         }
34882         
34883         this.box = box;
34884         if(!this.collapsed){
34885             this.el.dom.style.left = box.x + "px";
34886             this.el.dom.style.top = box.y + "px";
34887             this.updateBody(box.width, box.height);
34888         }else{
34889             this.collapsedEl.dom.style.left = box.x + "px";
34890             this.collapsedEl.dom.style.top = box.y + "px";
34891             this.collapsedEl.setSize(box.width, box.height);
34892         }
34893         if(this.tabs){
34894             this.tabs.autoSizeTabs();
34895         }
34896     },
34897
34898     updateBody : function(w, h)
34899     {
34900         if(w !== null){
34901             this.el.setWidth(w);
34902             w -= this.el.getBorderWidth("rl");
34903             if(this.config.adjustments){
34904                 w += this.config.adjustments[0];
34905             }
34906         }
34907         if(h !== null && h > 0){
34908             this.el.setHeight(h);
34909             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
34910             h -= this.el.getBorderWidth("tb");
34911             if(this.config.adjustments){
34912                 h += this.config.adjustments[1];
34913             }
34914             this.bodyEl.setHeight(h);
34915             if(this.tabs){
34916                 h = this.tabs.syncHeight(h);
34917             }
34918         }
34919         if(this.panelSize){
34920             w = w !== null ? w : this.panelSize.width;
34921             h = h !== null ? h : this.panelSize.height;
34922         }
34923         if(this.activePanel){
34924             var el = this.activePanel.getEl();
34925             w = w !== null ? w : el.getWidth();
34926             h = h !== null ? h : el.getHeight();
34927             this.panelSize = {width: w, height: h};
34928             this.activePanel.setSize(w, h);
34929         }
34930         if(Roo.isIE && this.tabs){
34931             this.tabs.el.repaint();
34932         }
34933     },
34934
34935     /**
34936      * Returns the container element for this region.
34937      * @return {Roo.Element}
34938      */
34939     getEl : function(){
34940         return this.el;
34941     },
34942
34943     /**
34944      * Hides this region.
34945      */
34946     hide : function(){
34947         //if(!this.collapsed){
34948             this.el.dom.style.left = "-2000px";
34949             this.el.hide();
34950         //}else{
34951          //   this.collapsedEl.dom.style.left = "-2000px";
34952          //   this.collapsedEl.hide();
34953        // }
34954         this.visible = false;
34955         this.fireEvent("visibilitychange", this, false);
34956     },
34957
34958     /**
34959      * Shows this region if it was previously hidden.
34960      */
34961     show : function(){
34962         //if(!this.collapsed){
34963             this.el.show();
34964         //}else{
34965         //    this.collapsedEl.show();
34966        // }
34967         this.visible = true;
34968         this.fireEvent("visibilitychange", this, true);
34969     },
34970 /*
34971     closeClicked : function(){
34972         if(this.activePanel){
34973             this.remove(this.activePanel);
34974         }
34975     },
34976
34977     collapseClick : function(e){
34978         if(this.isSlid){
34979            e.stopPropagation();
34980            this.slideIn();
34981         }else{
34982            e.stopPropagation();
34983            this.slideOut();
34984         }
34985     },
34986 */
34987     /**
34988      * Collapses this region.
34989      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
34990      */
34991     /*
34992     collapse : function(skipAnim, skipCheck = false){
34993         if(this.collapsed) {
34994             return;
34995         }
34996         
34997         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
34998             
34999             this.collapsed = true;
35000             if(this.split){
35001                 this.split.el.hide();
35002             }
35003             if(this.config.animate && skipAnim !== true){
35004                 this.fireEvent("invalidated", this);
35005                 this.animateCollapse();
35006             }else{
35007                 this.el.setLocation(-20000,-20000);
35008                 this.el.hide();
35009                 this.collapsedEl.show();
35010                 this.fireEvent("collapsed", this);
35011                 this.fireEvent("invalidated", this);
35012             }
35013         }
35014         
35015     },
35016 */
35017     animateCollapse : function(){
35018         // overridden
35019     },
35020
35021     /**
35022      * Expands this region if it was previously collapsed.
35023      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35024      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35025      */
35026     /*
35027     expand : function(e, skipAnim){
35028         if(e) {
35029             e.stopPropagation();
35030         }
35031         if(!this.collapsed || this.el.hasActiveFx()) {
35032             return;
35033         }
35034         if(this.isSlid){
35035             this.afterSlideIn();
35036             skipAnim = true;
35037         }
35038         this.collapsed = false;
35039         if(this.config.animate && skipAnim !== true){
35040             this.animateExpand();
35041         }else{
35042             this.el.show();
35043             if(this.split){
35044                 this.split.el.show();
35045             }
35046             this.collapsedEl.setLocation(-2000,-2000);
35047             this.collapsedEl.hide();
35048             this.fireEvent("invalidated", this);
35049             this.fireEvent("expanded", this);
35050         }
35051     },
35052 */
35053     animateExpand : function(){
35054         // overridden
35055     },
35056
35057     initTabs : function()
35058     {
35059         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35060         
35061         var ts = new Roo.bootstrap.panel.Tabs({
35062                 el: this.bodyEl.dom,
35063                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35064                 disableTooltips: this.config.disableTabTips,
35065                 toolbar : this.config.toolbar
35066             });
35067         
35068         if(this.config.hideTabs){
35069             ts.stripWrap.setDisplayed(false);
35070         }
35071         this.tabs = ts;
35072         ts.resizeTabs = this.config.resizeTabs === true;
35073         ts.minTabWidth = this.config.minTabWidth || 40;
35074         ts.maxTabWidth = this.config.maxTabWidth || 250;
35075         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35076         ts.monitorResize = false;
35077         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35078         ts.bodyEl.addClass('roo-layout-tabs-body');
35079         this.panels.each(this.initPanelAsTab, this);
35080     },
35081
35082     initPanelAsTab : function(panel){
35083         var ti = this.tabs.addTab(
35084             panel.getEl().id,
35085             panel.getTitle(),
35086             null,
35087             this.config.closeOnTab && panel.isClosable(),
35088             panel.tpl
35089         );
35090         if(panel.tabTip !== undefined){
35091             ti.setTooltip(panel.tabTip);
35092         }
35093         ti.on("activate", function(){
35094               this.setActivePanel(panel);
35095         }, this);
35096         
35097         if(this.config.closeOnTab){
35098             ti.on("beforeclose", function(t, e){
35099                 e.cancel = true;
35100                 this.remove(panel);
35101             }, this);
35102         }
35103         
35104         panel.tabItem = ti;
35105         
35106         return ti;
35107     },
35108
35109     updatePanelTitle : function(panel, title)
35110     {
35111         if(this.activePanel == panel){
35112             this.updateTitle(title);
35113         }
35114         if(this.tabs){
35115             var ti = this.tabs.getTab(panel.getEl().id);
35116             ti.setText(title);
35117             if(panel.tabTip !== undefined){
35118                 ti.setTooltip(panel.tabTip);
35119             }
35120         }
35121     },
35122
35123     updateTitle : function(title){
35124         if(this.titleTextEl && !this.config.title){
35125             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35126         }
35127     },
35128
35129     setActivePanel : function(panel)
35130     {
35131         panel = this.getPanel(panel);
35132         if(this.activePanel && this.activePanel != panel){
35133             this.activePanel.setActiveState(false);
35134         }
35135         this.activePanel = panel;
35136         panel.setActiveState(true);
35137         if(this.panelSize){
35138             panel.setSize(this.panelSize.width, this.panelSize.height);
35139         }
35140         if(this.closeBtn){
35141             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35142         }
35143         this.updateTitle(panel.getTitle());
35144         if(this.tabs){
35145             this.fireEvent("invalidated", this);
35146         }
35147         this.fireEvent("panelactivated", this, panel);
35148     },
35149
35150     /**
35151      * Shows the specified panel.
35152      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35153      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35154      */
35155     showPanel : function(panel)
35156     {
35157         panel = this.getPanel(panel);
35158         if(panel){
35159             if(this.tabs){
35160                 var tab = this.tabs.getTab(panel.getEl().id);
35161                 if(tab.isHidden()){
35162                     this.tabs.unhideTab(tab.id);
35163                 }
35164                 tab.activate();
35165             }else{
35166                 this.setActivePanel(panel);
35167             }
35168         }
35169         return panel;
35170     },
35171
35172     /**
35173      * Get the active panel for this region.
35174      * @return {Roo.ContentPanel} The active panel or null
35175      */
35176     getActivePanel : function(){
35177         return this.activePanel;
35178     },
35179
35180     validateVisibility : function(){
35181         if(this.panels.getCount() < 1){
35182             this.updateTitle("&#160;");
35183             this.closeBtn.hide();
35184             this.hide();
35185         }else{
35186             if(!this.isVisible()){
35187                 this.show();
35188             }
35189         }
35190     },
35191
35192     /**
35193      * Adds the passed ContentPanel(s) to this region.
35194      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35195      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35196      */
35197     add : function(panel)
35198     {
35199         if(arguments.length > 1){
35200             for(var i = 0, len = arguments.length; i < len; i++) {
35201                 this.add(arguments[i]);
35202             }
35203             return null;
35204         }
35205         
35206         // if we have not been rendered yet, then we can not really do much of this..
35207         if (!this.bodyEl) {
35208             this.unrendered_panels.push(panel);
35209             return panel;
35210         }
35211         
35212         
35213         
35214         
35215         if(this.hasPanel(panel)){
35216             this.showPanel(panel);
35217             return panel;
35218         }
35219         panel.setRegion(this);
35220         this.panels.add(panel);
35221        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35222             // sinle panel - no tab...?? would it not be better to render it with the tabs,
35223             // and hide them... ???
35224             this.bodyEl.dom.appendChild(panel.getEl().dom);
35225             if(panel.background !== true){
35226                 this.setActivePanel(panel);
35227             }
35228             this.fireEvent("paneladded", this, panel);
35229             return panel;
35230         }
35231         */
35232         if(!this.tabs){
35233             this.initTabs();
35234         }else{
35235             this.initPanelAsTab(panel);
35236         }
35237         
35238         
35239         if(panel.background !== true){
35240             this.tabs.activate(panel.getEl().id);
35241         }
35242         this.fireEvent("paneladded", this, panel);
35243         return panel;
35244     },
35245
35246     /**
35247      * Hides the tab for the specified panel.
35248      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35249      */
35250     hidePanel : function(panel){
35251         if(this.tabs && (panel = this.getPanel(panel))){
35252             this.tabs.hideTab(panel.getEl().id);
35253         }
35254     },
35255
35256     /**
35257      * Unhides the tab for a previously hidden panel.
35258      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35259      */
35260     unhidePanel : function(panel){
35261         if(this.tabs && (panel = this.getPanel(panel))){
35262             this.tabs.unhideTab(panel.getEl().id);
35263         }
35264     },
35265
35266     clearPanels : function(){
35267         while(this.panels.getCount() > 0){
35268              this.remove(this.panels.first());
35269         }
35270     },
35271
35272     /**
35273      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35274      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35275      * @param {Boolean} preservePanel Overrides the config preservePanel option
35276      * @return {Roo.ContentPanel} The panel that was removed
35277      */
35278     remove : function(panel, preservePanel)
35279     {
35280         panel = this.getPanel(panel);
35281         if(!panel){
35282             return null;
35283         }
35284         var e = {};
35285         this.fireEvent("beforeremove", this, panel, e);
35286         if(e.cancel === true){
35287             return null;
35288         }
35289         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35290         var panelId = panel.getId();
35291         this.panels.removeKey(panelId);
35292         if(preservePanel){
35293             document.body.appendChild(panel.getEl().dom);
35294         }
35295         if(this.tabs){
35296             this.tabs.removeTab(panel.getEl().id);
35297         }else if (!preservePanel){
35298             this.bodyEl.dom.removeChild(panel.getEl().dom);
35299         }
35300         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35301             var p = this.panels.first();
35302             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35303             tempEl.appendChild(p.getEl().dom);
35304             this.bodyEl.update("");
35305             this.bodyEl.dom.appendChild(p.getEl().dom);
35306             tempEl = null;
35307             this.updateTitle(p.getTitle());
35308             this.tabs = null;
35309             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35310             this.setActivePanel(p);
35311         }
35312         panel.setRegion(null);
35313         if(this.activePanel == panel){
35314             this.activePanel = null;
35315         }
35316         if(this.config.autoDestroy !== false && preservePanel !== true){
35317             try{panel.destroy();}catch(e){}
35318         }
35319         this.fireEvent("panelremoved", this, panel);
35320         return panel;
35321     },
35322
35323     /**
35324      * Returns the TabPanel component used by this region
35325      * @return {Roo.TabPanel}
35326      */
35327     getTabs : function(){
35328         return this.tabs;
35329     },
35330
35331     createTool : function(parentEl, className){
35332         var btn = Roo.DomHelper.append(parentEl, {
35333             tag: "div",
35334             cls: "x-layout-tools-button",
35335             children: [ {
35336                 tag: "div",
35337                 cls: "roo-layout-tools-button-inner " + className,
35338                 html: "&#160;"
35339             }]
35340         }, true);
35341         btn.addClassOnOver("roo-layout-tools-button-over");
35342         return btn;
35343     }
35344 });/*
35345  * Based on:
35346  * Ext JS Library 1.1.1
35347  * Copyright(c) 2006-2007, Ext JS, LLC.
35348  *
35349  * Originally Released Under LGPL - original licence link has changed is not relivant.
35350  *
35351  * Fork - LGPL
35352  * <script type="text/javascript">
35353  */
35354  
35355
35356
35357 /**
35358  * @class Roo.SplitLayoutRegion
35359  * @extends Roo.LayoutRegion
35360  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35361  */
35362 Roo.bootstrap.layout.Split = function(config){
35363     this.cursor = config.cursor;
35364     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
35365 };
35366
35367 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
35368 {
35369     splitTip : "Drag to resize.",
35370     collapsibleSplitTip : "Drag to resize. Double click to hide.",
35371     useSplitTips : false,
35372
35373     applyConfig : function(config){
35374         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
35375     },
35376     
35377     onRender : function(ctr,pos) {
35378         
35379         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
35380         if(!this.config.split){
35381             return;
35382         }
35383         if(!this.split){
35384             
35385             var splitEl = Roo.DomHelper.append(ctr.dom,  {
35386                             tag: "div",
35387                             id: this.el.id + "-split",
35388                             cls: "roo-layout-split roo-layout-split-"+this.position,
35389                             html: "&#160;"
35390             });
35391             /** The SplitBar for this region 
35392             * @type Roo.SplitBar */
35393             // does not exist yet...
35394             Roo.log([this.position, this.orientation]);
35395             
35396             this.split = new Roo.bootstrap.SplitBar({
35397                 dragElement : splitEl,
35398                 resizingElement: this.el,
35399                 orientation : this.orientation
35400             });
35401             
35402             this.split.on("moved", this.onSplitMove, this);
35403             this.split.useShim = this.config.useShim === true;
35404             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35405             if(this.useSplitTips){
35406                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35407             }
35408             //if(config.collapsible){
35409             //    this.split.el.on("dblclick", this.collapse,  this);
35410             //}
35411         }
35412         if(typeof this.config.minSize != "undefined"){
35413             this.split.minSize = this.config.minSize;
35414         }
35415         if(typeof this.config.maxSize != "undefined"){
35416             this.split.maxSize = this.config.maxSize;
35417         }
35418         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
35419             this.hideSplitter();
35420         }
35421         
35422     },
35423
35424     getHMaxSize : function(){
35425          var cmax = this.config.maxSize || 10000;
35426          var center = this.mgr.getRegion("center");
35427          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35428     },
35429
35430     getVMaxSize : function(){
35431          var cmax = this.config.maxSize || 10000;
35432          var center = this.mgr.getRegion("center");
35433          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35434     },
35435
35436     onSplitMove : function(split, newSize){
35437         this.fireEvent("resized", this, newSize);
35438     },
35439     
35440     /** 
35441      * Returns the {@link Roo.SplitBar} for this region.
35442      * @return {Roo.SplitBar}
35443      */
35444     getSplitBar : function(){
35445         return this.split;
35446     },
35447     
35448     hide : function(){
35449         this.hideSplitter();
35450         Roo.bootstrap.layout.Split.superclass.hide.call(this);
35451     },
35452
35453     hideSplitter : function(){
35454         if(this.split){
35455             this.split.el.setLocation(-2000,-2000);
35456             this.split.el.hide();
35457         }
35458     },
35459
35460     show : function(){
35461         if(this.split){
35462             this.split.el.show();
35463         }
35464         Roo.bootstrap.layout.Split.superclass.show.call(this);
35465     },
35466     
35467     beforeSlide: function(){
35468         if(Roo.isGecko){// firefox overflow auto bug workaround
35469             this.bodyEl.clip();
35470             if(this.tabs) {
35471                 this.tabs.bodyEl.clip();
35472             }
35473             if(this.activePanel){
35474                 this.activePanel.getEl().clip();
35475                 
35476                 if(this.activePanel.beforeSlide){
35477                     this.activePanel.beforeSlide();
35478                 }
35479             }
35480         }
35481     },
35482     
35483     afterSlide : function(){
35484         if(Roo.isGecko){// firefox overflow auto bug workaround
35485             this.bodyEl.unclip();
35486             if(this.tabs) {
35487                 this.tabs.bodyEl.unclip();
35488             }
35489             if(this.activePanel){
35490                 this.activePanel.getEl().unclip();
35491                 if(this.activePanel.afterSlide){
35492                     this.activePanel.afterSlide();
35493                 }
35494             }
35495         }
35496     },
35497
35498     initAutoHide : function(){
35499         if(this.autoHide !== false){
35500             if(!this.autoHideHd){
35501                 var st = new Roo.util.DelayedTask(this.slideIn, this);
35502                 this.autoHideHd = {
35503                     "mouseout": function(e){
35504                         if(!e.within(this.el, true)){
35505                             st.delay(500);
35506                         }
35507                     },
35508                     "mouseover" : function(e){
35509                         st.cancel();
35510                     },
35511                     scope : this
35512                 };
35513             }
35514             this.el.on(this.autoHideHd);
35515         }
35516     },
35517
35518     clearAutoHide : function(){
35519         if(this.autoHide !== false){
35520             this.el.un("mouseout", this.autoHideHd.mouseout);
35521             this.el.un("mouseover", this.autoHideHd.mouseover);
35522         }
35523     },
35524
35525     clearMonitor : function(){
35526         Roo.get(document).un("click", this.slideInIf, this);
35527     },
35528
35529     // these names are backwards but not changed for compat
35530     slideOut : function(){
35531         if(this.isSlid || this.el.hasActiveFx()){
35532             return;
35533         }
35534         this.isSlid = true;
35535         if(this.collapseBtn){
35536             this.collapseBtn.hide();
35537         }
35538         this.closeBtnState = this.closeBtn.getStyle('display');
35539         this.closeBtn.hide();
35540         if(this.stickBtn){
35541             this.stickBtn.show();
35542         }
35543         this.el.show();
35544         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
35545         this.beforeSlide();
35546         this.el.setStyle("z-index", 10001);
35547         this.el.slideIn(this.getSlideAnchor(), {
35548             callback: function(){
35549                 this.afterSlide();
35550                 this.initAutoHide();
35551                 Roo.get(document).on("click", this.slideInIf, this);
35552                 this.fireEvent("slideshow", this);
35553             },
35554             scope: this,
35555             block: true
35556         });
35557     },
35558
35559     afterSlideIn : function(){
35560         this.clearAutoHide();
35561         this.isSlid = false;
35562         this.clearMonitor();
35563         this.el.setStyle("z-index", "");
35564         if(this.collapseBtn){
35565             this.collapseBtn.show();
35566         }
35567         this.closeBtn.setStyle('display', this.closeBtnState);
35568         if(this.stickBtn){
35569             this.stickBtn.hide();
35570         }
35571         this.fireEvent("slidehide", this);
35572     },
35573
35574     slideIn : function(cb){
35575         if(!this.isSlid || this.el.hasActiveFx()){
35576             Roo.callback(cb);
35577             return;
35578         }
35579         this.isSlid = false;
35580         this.beforeSlide();
35581         this.el.slideOut(this.getSlideAnchor(), {
35582             callback: function(){
35583                 this.el.setLeftTop(-10000, -10000);
35584                 this.afterSlide();
35585                 this.afterSlideIn();
35586                 Roo.callback(cb);
35587             },
35588             scope: this,
35589             block: true
35590         });
35591     },
35592     
35593     slideInIf : function(e){
35594         if(!e.within(this.el)){
35595             this.slideIn();
35596         }
35597     },
35598
35599     animateCollapse : function(){
35600         this.beforeSlide();
35601         this.el.setStyle("z-index", 20000);
35602         var anchor = this.getSlideAnchor();
35603         this.el.slideOut(anchor, {
35604             callback : function(){
35605                 this.el.setStyle("z-index", "");
35606                 this.collapsedEl.slideIn(anchor, {duration:.3});
35607                 this.afterSlide();
35608                 this.el.setLocation(-10000,-10000);
35609                 this.el.hide();
35610                 this.fireEvent("collapsed", this);
35611             },
35612             scope: this,
35613             block: true
35614         });
35615     },
35616
35617     animateExpand : function(){
35618         this.beforeSlide();
35619         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
35620         this.el.setStyle("z-index", 20000);
35621         this.collapsedEl.hide({
35622             duration:.1
35623         });
35624         this.el.slideIn(this.getSlideAnchor(), {
35625             callback : function(){
35626                 this.el.setStyle("z-index", "");
35627                 this.afterSlide();
35628                 if(this.split){
35629                     this.split.el.show();
35630                 }
35631                 this.fireEvent("invalidated", this);
35632                 this.fireEvent("expanded", this);
35633             },
35634             scope: this,
35635             block: true
35636         });
35637     },
35638
35639     anchors : {
35640         "west" : "left",
35641         "east" : "right",
35642         "north" : "top",
35643         "south" : "bottom"
35644     },
35645
35646     sanchors : {
35647         "west" : "l",
35648         "east" : "r",
35649         "north" : "t",
35650         "south" : "b"
35651     },
35652
35653     canchors : {
35654         "west" : "tl-tr",
35655         "east" : "tr-tl",
35656         "north" : "tl-bl",
35657         "south" : "bl-tl"
35658     },
35659
35660     getAnchor : function(){
35661         return this.anchors[this.position];
35662     },
35663
35664     getCollapseAnchor : function(){
35665         return this.canchors[this.position];
35666     },
35667
35668     getSlideAnchor : function(){
35669         return this.sanchors[this.position];
35670     },
35671
35672     getAlignAdj : function(){
35673         var cm = this.cmargins;
35674         switch(this.position){
35675             case "west":
35676                 return [0, 0];
35677             break;
35678             case "east":
35679                 return [0, 0];
35680             break;
35681             case "north":
35682                 return [0, 0];
35683             break;
35684             case "south":
35685                 return [0, 0];
35686             break;
35687         }
35688     },
35689
35690     getExpandAdj : function(){
35691         var c = this.collapsedEl, cm = this.cmargins;
35692         switch(this.position){
35693             case "west":
35694                 return [-(cm.right+c.getWidth()+cm.left), 0];
35695             break;
35696             case "east":
35697                 return [cm.right+c.getWidth()+cm.left, 0];
35698             break;
35699             case "north":
35700                 return [0, -(cm.top+cm.bottom+c.getHeight())];
35701             break;
35702             case "south":
35703                 return [0, cm.top+cm.bottom+c.getHeight()];
35704             break;
35705         }
35706     }
35707 });/*
35708  * Based on:
35709  * Ext JS Library 1.1.1
35710  * Copyright(c) 2006-2007, Ext JS, LLC.
35711  *
35712  * Originally Released Under LGPL - original licence link has changed is not relivant.
35713  *
35714  * Fork - LGPL
35715  * <script type="text/javascript">
35716  */
35717 /*
35718  * These classes are private internal classes
35719  */
35720 Roo.bootstrap.layout.Center = function(config){
35721     config.region = "center";
35722     Roo.bootstrap.layout.Region.call(this, config);
35723     this.visible = true;
35724     this.minWidth = config.minWidth || 20;
35725     this.minHeight = config.minHeight || 20;
35726 };
35727
35728 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
35729     hide : function(){
35730         // center panel can't be hidden
35731     },
35732     
35733     show : function(){
35734         // center panel can't be hidden
35735     },
35736     
35737     getMinWidth: function(){
35738         return this.minWidth;
35739     },
35740     
35741     getMinHeight: function(){
35742         return this.minHeight;
35743     }
35744 });
35745
35746
35747
35748
35749  
35750
35751
35752
35753
35754
35755 Roo.bootstrap.layout.North = function(config)
35756 {
35757     config.region = 'north';
35758     config.cursor = 'n-resize';
35759     
35760     Roo.bootstrap.layout.Split.call(this, config);
35761     
35762     
35763     if(this.split){
35764         this.split.placement = Roo.bootstrap.SplitBar.TOP;
35765         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
35766         this.split.el.addClass("roo-layout-split-v");
35767     }
35768     var size = config.initialSize || config.height;
35769     if(typeof size != "undefined"){
35770         this.el.setHeight(size);
35771     }
35772 };
35773 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
35774 {
35775     orientation: Roo.bootstrap.SplitBar.VERTICAL,
35776     
35777     
35778     
35779     getBox : function(){
35780         if(this.collapsed){
35781             return this.collapsedEl.getBox();
35782         }
35783         var box = this.el.getBox();
35784         if(this.split){
35785             box.height += this.split.el.getHeight();
35786         }
35787         return box;
35788     },
35789     
35790     updateBox : function(box){
35791         if(this.split && !this.collapsed){
35792             box.height -= this.split.el.getHeight();
35793             this.split.el.setLeft(box.x);
35794             this.split.el.setTop(box.y+box.height);
35795             this.split.el.setWidth(box.width);
35796         }
35797         if(this.collapsed){
35798             this.updateBody(box.width, null);
35799         }
35800         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
35801     }
35802 });
35803
35804
35805
35806
35807
35808 Roo.bootstrap.layout.South = function(config){
35809     config.region = 'south';
35810     config.cursor = 's-resize';
35811     Roo.bootstrap.layout.Split.call(this, config);
35812     if(this.split){
35813         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
35814         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
35815         this.split.el.addClass("roo-layout-split-v");
35816     }
35817     var size = config.initialSize || config.height;
35818     if(typeof size != "undefined"){
35819         this.el.setHeight(size);
35820     }
35821 };
35822
35823 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
35824     orientation: Roo.bootstrap.SplitBar.VERTICAL,
35825     getBox : function(){
35826         if(this.collapsed){
35827             return this.collapsedEl.getBox();
35828         }
35829         var box = this.el.getBox();
35830         if(this.split){
35831             var sh = this.split.el.getHeight();
35832             box.height += sh;
35833             box.y -= sh;
35834         }
35835         return box;
35836     },
35837     
35838     updateBox : function(box){
35839         if(this.split && !this.collapsed){
35840             var sh = this.split.el.getHeight();
35841             box.height -= sh;
35842             box.y += sh;
35843             this.split.el.setLeft(box.x);
35844             this.split.el.setTop(box.y-sh);
35845             this.split.el.setWidth(box.width);
35846         }
35847         if(this.collapsed){
35848             this.updateBody(box.width, null);
35849         }
35850         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
35851     }
35852 });
35853
35854 Roo.bootstrap.layout.East = function(config){
35855     config.region = "east";
35856     config.cursor = "e-resize";
35857     Roo.bootstrap.layout.Split.call(this, config);
35858     if(this.split){
35859         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
35860         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
35861         this.split.el.addClass("roo-layout-split-h");
35862     }
35863     var size = config.initialSize || config.width;
35864     if(typeof size != "undefined"){
35865         this.el.setWidth(size);
35866     }
35867 };
35868 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
35869     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
35870     getBox : function(){
35871         if(this.collapsed){
35872             return this.collapsedEl.getBox();
35873         }
35874         var box = this.el.getBox();
35875         if(this.split){
35876             var sw = this.split.el.getWidth();
35877             box.width += sw;
35878             box.x -= sw;
35879         }
35880         return box;
35881     },
35882
35883     updateBox : function(box){
35884         if(this.split && !this.collapsed){
35885             var sw = this.split.el.getWidth();
35886             box.width -= sw;
35887             this.split.el.setLeft(box.x);
35888             this.split.el.setTop(box.y);
35889             this.split.el.setHeight(box.height);
35890             box.x += sw;
35891         }
35892         if(this.collapsed){
35893             this.updateBody(null, box.height);
35894         }
35895         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
35896     }
35897 });
35898
35899 Roo.bootstrap.layout.West = function(config){
35900     config.region = "west";
35901     config.cursor = "w-resize";
35902     
35903     Roo.bootstrap.layout.Split.call(this, config);
35904     if(this.split){
35905         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
35906         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
35907         this.split.el.addClass("roo-layout-split-h");
35908     }
35909     
35910 };
35911 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
35912     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
35913     
35914     onRender: function(ctr, pos)
35915     {
35916         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
35917         var size = this.config.initialSize || this.config.width;
35918         if(typeof size != "undefined"){
35919             this.el.setWidth(size);
35920         }
35921     },
35922     
35923     getBox : function(){
35924         if(this.collapsed){
35925             return this.collapsedEl.getBox();
35926         }
35927         var box = this.el.getBox();
35928         if(this.split){
35929             box.width += this.split.el.getWidth();
35930         }
35931         return box;
35932     },
35933     
35934     updateBox : function(box){
35935         if(this.split && !this.collapsed){
35936             var sw = this.split.el.getWidth();
35937             box.width -= sw;
35938             this.split.el.setLeft(box.x+box.width);
35939             this.split.el.setTop(box.y);
35940             this.split.el.setHeight(box.height);
35941         }
35942         if(this.collapsed){
35943             this.updateBody(null, box.height);
35944         }
35945         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
35946     }
35947 });
35948 Roo.namespace("Roo.bootstrap.panel");/*
35949  * Based on:
35950  * Ext JS Library 1.1.1
35951  * Copyright(c) 2006-2007, Ext JS, LLC.
35952  *
35953  * Originally Released Under LGPL - original licence link has changed is not relivant.
35954  *
35955  * Fork - LGPL
35956  * <script type="text/javascript">
35957  */
35958 /**
35959  * @class Roo.ContentPanel
35960  * @extends Roo.util.Observable
35961  * A basic ContentPanel element.
35962  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
35963  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
35964  * @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
35965  * @cfg {Boolean}   closable      True if the panel can be closed/removed
35966  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
35967  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
35968  * @cfg {Toolbar}   toolbar       A toolbar for this panel
35969  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
35970  * @cfg {String} title          The title for this panel
35971  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
35972  * @cfg {String} url            Calls {@link #setUrl} with this value
35973  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
35974  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
35975  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
35976  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
35977  * @cfg {Boolean} badges render the badges
35978
35979  * @constructor
35980  * Create a new ContentPanel.
35981  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
35982  * @param {String/Object} config A string to set only the title or a config object
35983  * @param {String} content (optional) Set the HTML content for this panel
35984  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
35985  */
35986 Roo.bootstrap.panel.Content = function( config){
35987     
35988     this.tpl = config.tpl || false;
35989     
35990     var el = config.el;
35991     var content = config.content;
35992
35993     if(config.autoCreate){ // xtype is available if this is called from factory
35994         el = Roo.id();
35995     }
35996     this.el = Roo.get(el);
35997     if(!this.el && config && config.autoCreate){
35998         if(typeof config.autoCreate == "object"){
35999             if(!config.autoCreate.id){
36000                 config.autoCreate.id = config.id||el;
36001             }
36002             this.el = Roo.DomHelper.append(document.body,
36003                         config.autoCreate, true);
36004         }else{
36005             var elcfg =  {   tag: "div",
36006                             cls: "roo-layout-inactive-content",
36007                             id: config.id||el
36008                             };
36009             if (config.html) {
36010                 elcfg.html = config.html;
36011                 
36012             }
36013                         
36014             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36015         }
36016     } 
36017     this.closable = false;
36018     this.loaded = false;
36019     this.active = false;
36020    
36021       
36022     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36023         
36024         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36025         
36026         this.wrapEl = this.el; //this.el.wrap();
36027         var ti = [];
36028         if (config.toolbar.items) {
36029             ti = config.toolbar.items ;
36030             delete config.toolbar.items ;
36031         }
36032         
36033         var nitems = [];
36034         this.toolbar.render(this.wrapEl, 'before');
36035         for(var i =0;i < ti.length;i++) {
36036           //  Roo.log(['add child', items[i]]);
36037             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36038         }
36039         this.toolbar.items = nitems;
36040         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36041         delete config.toolbar;
36042         
36043     }
36044     /*
36045     // xtype created footer. - not sure if will work as we normally have to render first..
36046     if (this.footer && !this.footer.el && this.footer.xtype) {
36047         if (!this.wrapEl) {
36048             this.wrapEl = this.el.wrap();
36049         }
36050     
36051         this.footer.container = this.wrapEl.createChild();
36052          
36053         this.footer = Roo.factory(this.footer, Roo);
36054         
36055     }
36056     */
36057     
36058      if(typeof config == "string"){
36059         this.title = config;
36060     }else{
36061         Roo.apply(this, config);
36062     }
36063     
36064     if(this.resizeEl){
36065         this.resizeEl = Roo.get(this.resizeEl, true);
36066     }else{
36067         this.resizeEl = this.el;
36068     }
36069     // handle view.xtype
36070     
36071  
36072     
36073     
36074     this.addEvents({
36075         /**
36076          * @event activate
36077          * Fires when this panel is activated. 
36078          * @param {Roo.ContentPanel} this
36079          */
36080         "activate" : true,
36081         /**
36082          * @event deactivate
36083          * Fires when this panel is activated. 
36084          * @param {Roo.ContentPanel} this
36085          */
36086         "deactivate" : true,
36087
36088         /**
36089          * @event resize
36090          * Fires when this panel is resized if fitToFrame is true.
36091          * @param {Roo.ContentPanel} this
36092          * @param {Number} width The width after any component adjustments
36093          * @param {Number} height The height after any component adjustments
36094          */
36095         "resize" : true,
36096         
36097          /**
36098          * @event render
36099          * Fires when this tab is created
36100          * @param {Roo.ContentPanel} this
36101          */
36102         "render" : true
36103         
36104         
36105         
36106     });
36107     
36108
36109     
36110     
36111     if(this.autoScroll){
36112         this.resizeEl.setStyle("overflow", "auto");
36113     } else {
36114         // fix randome scrolling
36115         //this.el.on('scroll', function() {
36116         //    Roo.log('fix random scolling');
36117         //    this.scrollTo('top',0); 
36118         //});
36119     }
36120     content = content || this.content;
36121     if(content){
36122         this.setContent(content);
36123     }
36124     if(config && config.url){
36125         this.setUrl(this.url, this.params, this.loadOnce);
36126     }
36127     
36128     
36129     
36130     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36131     
36132     if (this.view && typeof(this.view.xtype) != 'undefined') {
36133         this.view.el = this.el.appendChild(document.createElement("div"));
36134         this.view = Roo.factory(this.view); 
36135         this.view.render  &&  this.view.render(false, '');  
36136     }
36137     
36138     
36139     this.fireEvent('render', this);
36140 };
36141
36142 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36143     
36144     tabTip : '',
36145     
36146     setRegion : function(region){
36147         this.region = region;
36148         this.setActiveClass(region && !this.background);
36149     },
36150     
36151     
36152     setActiveClass: function(state)
36153     {
36154         if(state){
36155            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36156            this.el.setStyle('position','relative');
36157         }else{
36158            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36159            this.el.setStyle('position', 'absolute');
36160         } 
36161     },
36162     
36163     /**
36164      * Returns the toolbar for this Panel if one was configured. 
36165      * @return {Roo.Toolbar} 
36166      */
36167     getToolbar : function(){
36168         return this.toolbar;
36169     },
36170     
36171     setActiveState : function(active)
36172     {
36173         this.active = active;
36174         this.setActiveClass(active);
36175         if(!active){
36176             this.fireEvent("deactivate", this);
36177         }else{
36178             this.fireEvent("activate", this);
36179         }
36180     },
36181     /**
36182      * Updates this panel's element
36183      * @param {String} content The new content
36184      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36185     */
36186     setContent : function(content, loadScripts){
36187         this.el.update(content, loadScripts);
36188     },
36189
36190     ignoreResize : function(w, h){
36191         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36192             return true;
36193         }else{
36194             this.lastSize = {width: w, height: h};
36195             return false;
36196         }
36197     },
36198     /**
36199      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36200      * @return {Roo.UpdateManager} The UpdateManager
36201      */
36202     getUpdateManager : function(){
36203         return this.el.getUpdateManager();
36204     },
36205      /**
36206      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36207      * @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:
36208 <pre><code>
36209 panel.load({
36210     url: "your-url.php",
36211     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36212     callback: yourFunction,
36213     scope: yourObject, //(optional scope)
36214     discardUrl: false,
36215     nocache: false,
36216     text: "Loading...",
36217     timeout: 30,
36218     scripts: false
36219 });
36220 </code></pre>
36221      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36222      * 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.
36223      * @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}
36224      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36225      * @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.
36226      * @return {Roo.ContentPanel} this
36227      */
36228     load : function(){
36229         var um = this.el.getUpdateManager();
36230         um.update.apply(um, arguments);
36231         return this;
36232     },
36233
36234
36235     /**
36236      * 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.
36237      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36238      * @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)
36239      * @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)
36240      * @return {Roo.UpdateManager} The UpdateManager
36241      */
36242     setUrl : function(url, params, loadOnce){
36243         if(this.refreshDelegate){
36244             this.removeListener("activate", this.refreshDelegate);
36245         }
36246         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36247         this.on("activate", this.refreshDelegate);
36248         return this.el.getUpdateManager();
36249     },
36250     
36251     _handleRefresh : function(url, params, loadOnce){
36252         if(!loadOnce || !this.loaded){
36253             var updater = this.el.getUpdateManager();
36254             updater.update(url, params, this._setLoaded.createDelegate(this));
36255         }
36256     },
36257     
36258     _setLoaded : function(){
36259         this.loaded = true;
36260     }, 
36261     
36262     /**
36263      * Returns this panel's id
36264      * @return {String} 
36265      */
36266     getId : function(){
36267         return this.el.id;
36268     },
36269     
36270     /** 
36271      * Returns this panel's element - used by regiosn to add.
36272      * @return {Roo.Element} 
36273      */
36274     getEl : function(){
36275         return this.wrapEl || this.el;
36276     },
36277     
36278    
36279     
36280     adjustForComponents : function(width, height)
36281     {
36282         //Roo.log('adjustForComponents ');
36283         if(this.resizeEl != this.el){
36284             width -= this.el.getFrameWidth('lr');
36285             height -= this.el.getFrameWidth('tb');
36286         }
36287         if(this.toolbar){
36288             var te = this.toolbar.getEl();
36289             te.setWidth(width);
36290             height -= te.getHeight();
36291         }
36292         if(this.footer){
36293             var te = this.footer.getEl();
36294             te.setWidth(width);
36295             height -= te.getHeight();
36296         }
36297         
36298         
36299         if(this.adjustments){
36300             width += this.adjustments[0];
36301             height += this.adjustments[1];
36302         }
36303         return {"width": width, "height": height};
36304     },
36305     
36306     setSize : function(width, height){
36307         if(this.fitToFrame && !this.ignoreResize(width, height)){
36308             if(this.fitContainer && this.resizeEl != this.el){
36309                 this.el.setSize(width, height);
36310             }
36311             var size = this.adjustForComponents(width, height);
36312             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36313             this.fireEvent('resize', this, size.width, size.height);
36314         }
36315     },
36316     
36317     /**
36318      * Returns this panel's title
36319      * @return {String} 
36320      */
36321     getTitle : function(){
36322         
36323         if (typeof(this.title) != 'object') {
36324             return this.title;
36325         }
36326         
36327         var t = '';
36328         for (var k in this.title) {
36329             if (!this.title.hasOwnProperty(k)) {
36330                 continue;
36331             }
36332             
36333             if (k.indexOf('-') >= 0) {
36334                 var s = k.split('-');
36335                 for (var i = 0; i<s.length; i++) {
36336                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
36337                 }
36338             } else {
36339                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
36340             }
36341         }
36342         return t;
36343     },
36344     
36345     /**
36346      * Set this panel's title
36347      * @param {String} title
36348      */
36349     setTitle : function(title){
36350         this.title = title;
36351         if(this.region){
36352             this.region.updatePanelTitle(this, title);
36353         }
36354     },
36355     
36356     /**
36357      * Returns true is this panel was configured to be closable
36358      * @return {Boolean} 
36359      */
36360     isClosable : function(){
36361         return this.closable;
36362     },
36363     
36364     beforeSlide : function(){
36365         this.el.clip();
36366         this.resizeEl.clip();
36367     },
36368     
36369     afterSlide : function(){
36370         this.el.unclip();
36371         this.resizeEl.unclip();
36372     },
36373     
36374     /**
36375      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
36376      *   Will fail silently if the {@link #setUrl} method has not been called.
36377      *   This does not activate the panel, just updates its content.
36378      */
36379     refresh : function(){
36380         if(this.refreshDelegate){
36381            this.loaded = false;
36382            this.refreshDelegate();
36383         }
36384     },
36385     
36386     /**
36387      * Destroys this panel
36388      */
36389     destroy : function(){
36390         this.el.removeAllListeners();
36391         var tempEl = document.createElement("span");
36392         tempEl.appendChild(this.el.dom);
36393         tempEl.innerHTML = "";
36394         this.el.remove();
36395         this.el = null;
36396     },
36397     
36398     /**
36399      * form - if the content panel contains a form - this is a reference to it.
36400      * @type {Roo.form.Form}
36401      */
36402     form : false,
36403     /**
36404      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36405      *    This contains a reference to it.
36406      * @type {Roo.View}
36407      */
36408     view : false,
36409     
36410       /**
36411      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36412      * <pre><code>
36413
36414 layout.addxtype({
36415        xtype : 'Form',
36416        items: [ .... ]
36417    }
36418 );
36419
36420 </code></pre>
36421      * @param {Object} cfg Xtype definition of item to add.
36422      */
36423     
36424     
36425     getChildContainer: function () {
36426         return this.getEl();
36427     }
36428     
36429     
36430     /*
36431         var  ret = new Roo.factory(cfg);
36432         return ret;
36433         
36434         
36435         // add form..
36436         if (cfg.xtype.match(/^Form$/)) {
36437             
36438             var el;
36439             //if (this.footer) {
36440             //    el = this.footer.container.insertSibling(false, 'before');
36441             //} else {
36442                 el = this.el.createChild();
36443             //}
36444
36445             this.form = new  Roo.form.Form(cfg);
36446             
36447             
36448             if ( this.form.allItems.length) {
36449                 this.form.render(el.dom);
36450             }
36451             return this.form;
36452         }
36453         // should only have one of theses..
36454         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36455             // views.. should not be just added - used named prop 'view''
36456             
36457             cfg.el = this.el.appendChild(document.createElement("div"));
36458             // factory?
36459             
36460             var ret = new Roo.factory(cfg);
36461              
36462              ret.render && ret.render(false, ''); // render blank..
36463             this.view = ret;
36464             return ret;
36465         }
36466         return false;
36467     }
36468     \*/
36469 });
36470  
36471 /**
36472  * @class Roo.bootstrap.panel.Grid
36473  * @extends Roo.bootstrap.panel.Content
36474  * @constructor
36475  * Create a new GridPanel.
36476  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
36477  * @param {Object} config A the config object
36478   
36479  */
36480
36481
36482
36483 Roo.bootstrap.panel.Grid = function(config)
36484 {
36485     
36486       
36487     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36488         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
36489
36490     config.el = this.wrapper;
36491     //this.el = this.wrapper;
36492     
36493       if (config.container) {
36494         // ctor'ed from a Border/panel.grid
36495         
36496         
36497         this.wrapper.setStyle("overflow", "hidden");
36498         this.wrapper.addClass('roo-grid-container');
36499
36500     }
36501     
36502     
36503     if(config.toolbar){
36504         var tool_el = this.wrapper.createChild();    
36505         this.toolbar = Roo.factory(config.toolbar);
36506         var ti = [];
36507         if (config.toolbar.items) {
36508             ti = config.toolbar.items ;
36509             delete config.toolbar.items ;
36510         }
36511         
36512         var nitems = [];
36513         this.toolbar.render(tool_el);
36514         for(var i =0;i < ti.length;i++) {
36515           //  Roo.log(['add child', items[i]]);
36516             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36517         }
36518         this.toolbar.items = nitems;
36519         
36520         delete config.toolbar;
36521     }
36522     
36523     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
36524     config.grid.scrollBody = true;;
36525     config.grid.monitorWindowResize = false; // turn off autosizing
36526     config.grid.autoHeight = false;
36527     config.grid.autoWidth = false;
36528     
36529     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
36530     
36531     if (config.background) {
36532         // render grid on panel activation (if panel background)
36533         this.on('activate', function(gp) {
36534             if (!gp.grid.rendered) {
36535                 gp.grid.render(this.wrapper);
36536                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
36537             }
36538         });
36539             
36540     } else {
36541         this.grid.render(this.wrapper);
36542         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
36543
36544     }
36545     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
36546     // ??? needed ??? config.el = this.wrapper;
36547     
36548     
36549     
36550   
36551     // xtype created footer. - not sure if will work as we normally have to render first..
36552     if (this.footer && !this.footer.el && this.footer.xtype) {
36553         
36554         var ctr = this.grid.getView().getFooterPanel(true);
36555         this.footer.dataSource = this.grid.dataSource;
36556         this.footer = Roo.factory(this.footer, Roo);
36557         this.footer.render(ctr);
36558         
36559     }
36560     
36561     
36562     
36563     
36564      
36565 };
36566
36567 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
36568     getId : function(){
36569         return this.grid.id;
36570     },
36571     
36572     /**
36573      * Returns the grid for this panel
36574      * @return {Roo.bootstrap.Table} 
36575      */
36576     getGrid : function(){
36577         return this.grid;    
36578     },
36579     
36580     setSize : function(width, height){
36581         if(!this.ignoreResize(width, height)){
36582             var grid = this.grid;
36583             var size = this.adjustForComponents(width, height);
36584             var gridel = grid.getGridEl();
36585             gridel.setSize(size.width, size.height);
36586             /*
36587             var thd = grid.getGridEl().select('thead',true).first();
36588             var tbd = grid.getGridEl().select('tbody', true).first();
36589             if (tbd) {
36590                 tbd.setSize(width, height - thd.getHeight());
36591             }
36592             */
36593             grid.autoSize();
36594         }
36595     },
36596      
36597     
36598     
36599     beforeSlide : function(){
36600         this.grid.getView().scroller.clip();
36601     },
36602     
36603     afterSlide : function(){
36604         this.grid.getView().scroller.unclip();
36605     },
36606     
36607     destroy : function(){
36608         this.grid.destroy();
36609         delete this.grid;
36610         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
36611     }
36612 });
36613
36614 /**
36615  * @class Roo.bootstrap.panel.Nest
36616  * @extends Roo.bootstrap.panel.Content
36617  * @constructor
36618  * Create a new Panel, that can contain a layout.Border.
36619  * 
36620  * 
36621  * @param {Roo.BorderLayout} layout The layout for this panel
36622  * @param {String/Object} config A string to set only the title or a config object
36623  */
36624 Roo.bootstrap.panel.Nest = function(config)
36625 {
36626     // construct with only one argument..
36627     /* FIXME - implement nicer consturctors
36628     if (layout.layout) {
36629         config = layout;
36630         layout = config.layout;
36631         delete config.layout;
36632     }
36633     if (layout.xtype && !layout.getEl) {
36634         // then layout needs constructing..
36635         layout = Roo.factory(layout, Roo);
36636     }
36637     */
36638     
36639     config.el =  config.layout.getEl();
36640     
36641     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
36642     
36643     config.layout.monitorWindowResize = false; // turn off autosizing
36644     this.layout = config.layout;
36645     this.layout.getEl().addClass("roo-layout-nested-layout");
36646     
36647     
36648     
36649     
36650 };
36651
36652 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
36653
36654     setSize : function(width, height){
36655         if(!this.ignoreResize(width, height)){
36656             var size = this.adjustForComponents(width, height);
36657             var el = this.layout.getEl();
36658             if (size.height < 1) {
36659                 el.setWidth(size.width);   
36660             } else {
36661                 el.setSize(size.width, size.height);
36662             }
36663             var touch = el.dom.offsetWidth;
36664             this.layout.layout();
36665             // ie requires a double layout on the first pass
36666             if(Roo.isIE && !this.initialized){
36667                 this.initialized = true;
36668                 this.layout.layout();
36669             }
36670         }
36671     },
36672     
36673     // activate all subpanels if not currently active..
36674     
36675     setActiveState : function(active){
36676         this.active = active;
36677         this.setActiveClass(active);
36678         
36679         if(!active){
36680             this.fireEvent("deactivate", this);
36681             return;
36682         }
36683         
36684         this.fireEvent("activate", this);
36685         // not sure if this should happen before or after..
36686         if (!this.layout) {
36687             return; // should not happen..
36688         }
36689         var reg = false;
36690         for (var r in this.layout.regions) {
36691             reg = this.layout.getRegion(r);
36692             if (reg.getActivePanel()) {
36693                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
36694                 reg.setActivePanel(reg.getActivePanel());
36695                 continue;
36696             }
36697             if (!reg.panels.length) {
36698                 continue;
36699             }
36700             reg.showPanel(reg.getPanel(0));
36701         }
36702         
36703         
36704         
36705         
36706     },
36707     
36708     /**
36709      * Returns the nested BorderLayout for this panel
36710      * @return {Roo.BorderLayout} 
36711      */
36712     getLayout : function(){
36713         return this.layout;
36714     },
36715     
36716      /**
36717      * Adds a xtype elements to the layout of the nested panel
36718      * <pre><code>
36719
36720 panel.addxtype({
36721        xtype : 'ContentPanel',
36722        region: 'west',
36723        items: [ .... ]
36724    }
36725 );
36726
36727 panel.addxtype({
36728         xtype : 'NestedLayoutPanel',
36729         region: 'west',
36730         layout: {
36731            center: { },
36732            west: { }   
36733         },
36734         items : [ ... list of content panels or nested layout panels.. ]
36735    }
36736 );
36737 </code></pre>
36738      * @param {Object} cfg Xtype definition of item to add.
36739      */
36740     addxtype : function(cfg) {
36741         return this.layout.addxtype(cfg);
36742     
36743     }
36744 });        /*
36745  * Based on:
36746  * Ext JS Library 1.1.1
36747  * Copyright(c) 2006-2007, Ext JS, LLC.
36748  *
36749  * Originally Released Under LGPL - original licence link has changed is not relivant.
36750  *
36751  * Fork - LGPL
36752  * <script type="text/javascript">
36753  */
36754 /**
36755  * @class Roo.TabPanel
36756  * @extends Roo.util.Observable
36757  * A lightweight tab container.
36758  * <br><br>
36759  * Usage:
36760  * <pre><code>
36761 // basic tabs 1, built from existing content
36762 var tabs = new Roo.TabPanel("tabs1");
36763 tabs.addTab("script", "View Script");
36764 tabs.addTab("markup", "View Markup");
36765 tabs.activate("script");
36766
36767 // more advanced tabs, built from javascript
36768 var jtabs = new Roo.TabPanel("jtabs");
36769 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
36770
36771 // set up the UpdateManager
36772 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
36773 var updater = tab2.getUpdateManager();
36774 updater.setDefaultUrl("ajax1.htm");
36775 tab2.on('activate', updater.refresh, updater, true);
36776
36777 // Use setUrl for Ajax loading
36778 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
36779 tab3.setUrl("ajax2.htm", null, true);
36780
36781 // Disabled tab
36782 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
36783 tab4.disable();
36784
36785 jtabs.activate("jtabs-1");
36786  * </code></pre>
36787  * @constructor
36788  * Create a new TabPanel.
36789  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
36790  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
36791  */
36792 Roo.bootstrap.panel.Tabs = function(config){
36793     /**
36794     * The container element for this TabPanel.
36795     * @type Roo.Element
36796     */
36797     this.el = Roo.get(config.el);
36798     delete config.el;
36799     if(config){
36800         if(typeof config == "boolean"){
36801             this.tabPosition = config ? "bottom" : "top";
36802         }else{
36803             Roo.apply(this, config);
36804         }
36805     }
36806     
36807     if(this.tabPosition == "bottom"){
36808         this.bodyEl = Roo.get(this.createBody(this.el.dom));
36809         this.el.addClass("roo-tabs-bottom");
36810     }
36811     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
36812     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
36813     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
36814     if(Roo.isIE){
36815         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
36816     }
36817     if(this.tabPosition != "bottom"){
36818         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
36819          * @type Roo.Element
36820          */
36821         this.bodyEl = Roo.get(this.createBody(this.el.dom));
36822         this.el.addClass("roo-tabs-top");
36823     }
36824     this.items = [];
36825
36826     this.bodyEl.setStyle("position", "relative");
36827
36828     this.active = null;
36829     this.activateDelegate = this.activate.createDelegate(this);
36830
36831     this.addEvents({
36832         /**
36833          * @event tabchange
36834          * Fires when the active tab changes
36835          * @param {Roo.TabPanel} this
36836          * @param {Roo.TabPanelItem} activePanel The new active tab
36837          */
36838         "tabchange": true,
36839         /**
36840          * @event beforetabchange
36841          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
36842          * @param {Roo.TabPanel} this
36843          * @param {Object} e Set cancel to true on this object to cancel the tab change
36844          * @param {Roo.TabPanelItem} tab The tab being changed to
36845          */
36846         "beforetabchange" : true
36847     });
36848
36849     Roo.EventManager.onWindowResize(this.onResize, this);
36850     this.cpad = this.el.getPadding("lr");
36851     this.hiddenCount = 0;
36852
36853
36854     // toolbar on the tabbar support...
36855     if (this.toolbar) {
36856         alert("no toolbar support yet");
36857         this.toolbar  = false;
36858         /*
36859         var tcfg = this.toolbar;
36860         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
36861         this.toolbar = new Roo.Toolbar(tcfg);
36862         if (Roo.isSafari) {
36863             var tbl = tcfg.container.child('table', true);
36864             tbl.setAttribute('width', '100%');
36865         }
36866         */
36867         
36868     }
36869    
36870
36871
36872     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
36873 };
36874
36875 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
36876     /*
36877      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
36878      */
36879     tabPosition : "top",
36880     /*
36881      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
36882      */
36883     currentTabWidth : 0,
36884     /*
36885      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
36886      */
36887     minTabWidth : 40,
36888     /*
36889      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
36890      */
36891     maxTabWidth : 250,
36892     /*
36893      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
36894      */
36895     preferredTabWidth : 175,
36896     /*
36897      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
36898      */
36899     resizeTabs : false,
36900     /*
36901      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
36902      */
36903     monitorResize : true,
36904     /*
36905      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
36906      */
36907     toolbar : false,
36908
36909     /**
36910      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
36911      * @param {String} id The id of the div to use <b>or create</b>
36912      * @param {String} text The text for the tab
36913      * @param {String} content (optional) Content to put in the TabPanelItem body
36914      * @param {Boolean} closable (optional) True to create a close icon on the tab
36915      * @return {Roo.TabPanelItem} The created TabPanelItem
36916      */
36917     addTab : function(id, text, content, closable, tpl)
36918     {
36919         var item = new Roo.bootstrap.panel.TabItem({
36920             panel: this,
36921             id : id,
36922             text : text,
36923             closable : closable,
36924             tpl : tpl
36925         });
36926         this.addTabItem(item);
36927         if(content){
36928             item.setContent(content);
36929         }
36930         return item;
36931     },
36932
36933     /**
36934      * Returns the {@link Roo.TabPanelItem} with the specified id/index
36935      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
36936      * @return {Roo.TabPanelItem}
36937      */
36938     getTab : function(id){
36939         return this.items[id];
36940     },
36941
36942     /**
36943      * Hides the {@link Roo.TabPanelItem} with the specified id/index
36944      * @param {String/Number} id The id or index of the TabPanelItem to hide.
36945      */
36946     hideTab : function(id){
36947         var t = this.items[id];
36948         if(!t.isHidden()){
36949            t.setHidden(true);
36950            this.hiddenCount++;
36951            this.autoSizeTabs();
36952         }
36953     },
36954
36955     /**
36956      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
36957      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
36958      */
36959     unhideTab : function(id){
36960         var t = this.items[id];
36961         if(t.isHidden()){
36962            t.setHidden(false);
36963            this.hiddenCount--;
36964            this.autoSizeTabs();
36965         }
36966     },
36967
36968     /**
36969      * Adds an existing {@link Roo.TabPanelItem}.
36970      * @param {Roo.TabPanelItem} item The TabPanelItem to add
36971      */
36972     addTabItem : function(item){
36973         this.items[item.id] = item;
36974         this.items.push(item);
36975       //  if(this.resizeTabs){
36976     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
36977   //         this.autoSizeTabs();
36978 //        }else{
36979 //            item.autoSize();
36980        // }
36981     },
36982
36983     /**
36984      * Removes a {@link Roo.TabPanelItem}.
36985      * @param {String/Number} id The id or index of the TabPanelItem to remove.
36986      */
36987     removeTab : function(id){
36988         var items = this.items;
36989         var tab = items[id];
36990         if(!tab) { return; }
36991         var index = items.indexOf(tab);
36992         if(this.active == tab && items.length > 1){
36993             var newTab = this.getNextAvailable(index);
36994             if(newTab) {
36995                 newTab.activate();
36996             }
36997         }
36998         this.stripEl.dom.removeChild(tab.pnode.dom);
36999         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37000             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37001         }
37002         items.splice(index, 1);
37003         delete this.items[tab.id];
37004         tab.fireEvent("close", tab);
37005         tab.purgeListeners();
37006         this.autoSizeTabs();
37007     },
37008
37009     getNextAvailable : function(start){
37010         var items = this.items;
37011         var index = start;
37012         // look for a next tab that will slide over to
37013         // replace the one being removed
37014         while(index < items.length){
37015             var item = items[++index];
37016             if(item && !item.isHidden()){
37017                 return item;
37018             }
37019         }
37020         // if one isn't found select the previous tab (on the left)
37021         index = start;
37022         while(index >= 0){
37023             var item = items[--index];
37024             if(item && !item.isHidden()){
37025                 return item;
37026             }
37027         }
37028         return null;
37029     },
37030
37031     /**
37032      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37033      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37034      */
37035     disableTab : function(id){
37036         var tab = this.items[id];
37037         if(tab && this.active != tab){
37038             tab.disable();
37039         }
37040     },
37041
37042     /**
37043      * Enables a {@link Roo.TabPanelItem} that is disabled.
37044      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37045      */
37046     enableTab : function(id){
37047         var tab = this.items[id];
37048         tab.enable();
37049     },
37050
37051     /**
37052      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37053      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37054      * @return {Roo.TabPanelItem} The TabPanelItem.
37055      */
37056     activate : function(id){
37057         var tab = this.items[id];
37058         if(!tab){
37059             return null;
37060         }
37061         if(tab == this.active || tab.disabled){
37062             return tab;
37063         }
37064         var e = {};
37065         this.fireEvent("beforetabchange", this, e, tab);
37066         if(e.cancel !== true && !tab.disabled){
37067             if(this.active){
37068                 this.active.hide();
37069             }
37070             this.active = this.items[id];
37071             this.active.show();
37072             this.fireEvent("tabchange", this, this.active);
37073         }
37074         return tab;
37075     },
37076
37077     /**
37078      * Gets the active {@link Roo.TabPanelItem}.
37079      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37080      */
37081     getActiveTab : function(){
37082         return this.active;
37083     },
37084
37085     /**
37086      * Updates the tab body element to fit the height of the container element
37087      * for overflow scrolling
37088      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37089      */
37090     syncHeight : function(targetHeight){
37091         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37092         var bm = this.bodyEl.getMargins();
37093         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37094         this.bodyEl.setHeight(newHeight);
37095         return newHeight;
37096     },
37097
37098     onResize : function(){
37099         if(this.monitorResize){
37100             this.autoSizeTabs();
37101         }
37102     },
37103
37104     /**
37105      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37106      */
37107     beginUpdate : function(){
37108         this.updating = true;
37109     },
37110
37111     /**
37112      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37113      */
37114     endUpdate : function(){
37115         this.updating = false;
37116         this.autoSizeTabs();
37117     },
37118
37119     /**
37120      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37121      */
37122     autoSizeTabs : function(){
37123         var count = this.items.length;
37124         var vcount = count - this.hiddenCount;
37125         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37126             return;
37127         }
37128         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37129         var availWidth = Math.floor(w / vcount);
37130         var b = this.stripBody;
37131         if(b.getWidth() > w){
37132             var tabs = this.items;
37133             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37134             if(availWidth < this.minTabWidth){
37135                 /*if(!this.sleft){    // incomplete scrolling code
37136                     this.createScrollButtons();
37137                 }
37138                 this.showScroll();
37139                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37140             }
37141         }else{
37142             if(this.currentTabWidth < this.preferredTabWidth){
37143                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37144             }
37145         }
37146     },
37147
37148     /**
37149      * Returns the number of tabs in this TabPanel.
37150      * @return {Number}
37151      */
37152      getCount : function(){
37153          return this.items.length;
37154      },
37155
37156     /**
37157      * Resizes all the tabs to the passed width
37158      * @param {Number} The new width
37159      */
37160     setTabWidth : function(width){
37161         this.currentTabWidth = width;
37162         for(var i = 0, len = this.items.length; i < len; i++) {
37163                 if(!this.items[i].isHidden()) {
37164                 this.items[i].setWidth(width);
37165             }
37166         }
37167     },
37168
37169     /**
37170      * Destroys this TabPanel
37171      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37172      */
37173     destroy : function(removeEl){
37174         Roo.EventManager.removeResizeListener(this.onResize, this);
37175         for(var i = 0, len = this.items.length; i < len; i++){
37176             this.items[i].purgeListeners();
37177         }
37178         if(removeEl === true){
37179             this.el.update("");
37180             this.el.remove();
37181         }
37182     },
37183     
37184     createStrip : function(container)
37185     {
37186         var strip = document.createElement("nav");
37187         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37188         container.appendChild(strip);
37189         return strip;
37190     },
37191     
37192     createStripList : function(strip)
37193     {
37194         // div wrapper for retard IE
37195         // returns the "tr" element.
37196         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37197         //'<div class="x-tabs-strip-wrap">'+
37198           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37199           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37200         return strip.firstChild; //.firstChild.firstChild.firstChild;
37201     },
37202     createBody : function(container)
37203     {
37204         var body = document.createElement("div");
37205         Roo.id(body, "tab-body");
37206         //Roo.fly(body).addClass("x-tabs-body");
37207         Roo.fly(body).addClass("tab-content");
37208         container.appendChild(body);
37209         return body;
37210     },
37211     createItemBody :function(bodyEl, id){
37212         var body = Roo.getDom(id);
37213         if(!body){
37214             body = document.createElement("div");
37215             body.id = id;
37216         }
37217         //Roo.fly(body).addClass("x-tabs-item-body");
37218         Roo.fly(body).addClass("tab-pane");
37219          bodyEl.insertBefore(body, bodyEl.firstChild);
37220         return body;
37221     },
37222     /** @private */
37223     createStripElements :  function(stripEl, text, closable, tpl)
37224     {
37225         var td = document.createElement("li"); // was td..
37226         
37227         
37228         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
37229         
37230         
37231         stripEl.appendChild(td);
37232         /*if(closable){
37233             td.className = "x-tabs-closable";
37234             if(!this.closeTpl){
37235                 this.closeTpl = new Roo.Template(
37236                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37237                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
37238                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
37239                 );
37240             }
37241             var el = this.closeTpl.overwrite(td, {"text": text});
37242             var close = el.getElementsByTagName("div")[0];
37243             var inner = el.getElementsByTagName("em")[0];
37244             return {"el": el, "close": close, "inner": inner};
37245         } else {
37246         */
37247         // not sure what this is..
37248 //            if(!this.tabTpl){
37249                 //this.tabTpl = new Roo.Template(
37250                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37251                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
37252                 //);
37253 //                this.tabTpl = new Roo.Template(
37254 //                   '<a href="#">' +
37255 //                   '<span unselectable="on"' +
37256 //                            (this.disableTooltips ? '' : ' title="{text}"') +
37257 //                            ' >{text}</span></a>'
37258 //                );
37259 //                
37260 //            }
37261
37262
37263             var template = tpl || this.tabTpl || false;
37264             
37265             if(!template){
37266                 
37267                 template = new Roo.Template(
37268                    '<a href="#">' +
37269                    '<span unselectable="on"' +
37270                             (this.disableTooltips ? '' : ' title="{text}"') +
37271                             ' >{text}</span></a>'
37272                 );
37273             }
37274             
37275             switch (typeof(template)) {
37276                 case 'object' :
37277                     break;
37278                 case 'string' :
37279                     template = new Roo.Template(template);
37280                     break;
37281                 default :
37282                     break;
37283             }
37284             
37285             var el = template.overwrite(td, {"text": text});
37286             
37287             var inner = el.getElementsByTagName("span")[0];
37288             
37289             return {"el": el, "inner": inner};
37290             
37291     }
37292         
37293     
37294 });
37295
37296 /**
37297  * @class Roo.TabPanelItem
37298  * @extends Roo.util.Observable
37299  * Represents an individual item (tab plus body) in a TabPanel.
37300  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
37301  * @param {String} id The id of this TabPanelItem
37302  * @param {String} text The text for the tab of this TabPanelItem
37303  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
37304  */
37305 Roo.bootstrap.panel.TabItem = function(config){
37306     /**
37307      * The {@link Roo.TabPanel} this TabPanelItem belongs to
37308      * @type Roo.TabPanel
37309      */
37310     this.tabPanel = config.panel;
37311     /**
37312      * The id for this TabPanelItem
37313      * @type String
37314      */
37315     this.id = config.id;
37316     /** @private */
37317     this.disabled = false;
37318     /** @private */
37319     this.text = config.text;
37320     /** @private */
37321     this.loaded = false;
37322     this.closable = config.closable;
37323
37324     /**
37325      * The body element for this TabPanelItem.
37326      * @type Roo.Element
37327      */
37328     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
37329     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
37330     this.bodyEl.setStyle("display", "block");
37331     this.bodyEl.setStyle("zoom", "1");
37332     //this.hideAction();
37333
37334     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
37335     /** @private */
37336     this.el = Roo.get(els.el);
37337     this.inner = Roo.get(els.inner, true);
37338     this.textEl = Roo.get(this.el.dom.firstChild, true);
37339     this.pnode = Roo.get(els.el.parentNode, true);
37340     this.el.on("mousedown", this.onTabMouseDown, this);
37341     this.el.on("click", this.onTabClick, this);
37342     /** @private */
37343     if(config.closable){
37344         var c = Roo.get(els.close, true);
37345         c.dom.title = this.closeText;
37346         c.addClassOnOver("close-over");
37347         c.on("click", this.closeClick, this);
37348      }
37349
37350     this.addEvents({
37351          /**
37352          * @event activate
37353          * Fires when this tab becomes the active tab.
37354          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37355          * @param {Roo.TabPanelItem} this
37356          */
37357         "activate": true,
37358         /**
37359          * @event beforeclose
37360          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
37361          * @param {Roo.TabPanelItem} this
37362          * @param {Object} e Set cancel to true on this object to cancel the close.
37363          */
37364         "beforeclose": true,
37365         /**
37366          * @event close
37367          * Fires when this tab is closed.
37368          * @param {Roo.TabPanelItem} this
37369          */
37370          "close": true,
37371         /**
37372          * @event deactivate
37373          * Fires when this tab is no longer the active tab.
37374          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37375          * @param {Roo.TabPanelItem} this
37376          */
37377          "deactivate" : true
37378     });
37379     this.hidden = false;
37380
37381     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
37382 };
37383
37384 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
37385            {
37386     purgeListeners : function(){
37387        Roo.util.Observable.prototype.purgeListeners.call(this);
37388        this.el.removeAllListeners();
37389     },
37390     /**
37391      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
37392      */
37393     show : function(){
37394         this.pnode.addClass("active");
37395         this.showAction();
37396         if(Roo.isOpera){
37397             this.tabPanel.stripWrap.repaint();
37398         }
37399         this.fireEvent("activate", this.tabPanel, this);
37400     },
37401
37402     /**
37403      * Returns true if this tab is the active tab.
37404      * @return {Boolean}
37405      */
37406     isActive : function(){
37407         return this.tabPanel.getActiveTab() == this;
37408     },
37409
37410     /**
37411      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
37412      */
37413     hide : function(){
37414         this.pnode.removeClass("active");
37415         this.hideAction();
37416         this.fireEvent("deactivate", this.tabPanel, this);
37417     },
37418
37419     hideAction : function(){
37420         this.bodyEl.hide();
37421         this.bodyEl.setStyle("position", "absolute");
37422         this.bodyEl.setLeft("-20000px");
37423         this.bodyEl.setTop("-20000px");
37424     },
37425
37426     showAction : function(){
37427         this.bodyEl.setStyle("position", "relative");
37428         this.bodyEl.setTop("");
37429         this.bodyEl.setLeft("");
37430         this.bodyEl.show();
37431     },
37432
37433     /**
37434      * Set the tooltip for the tab.
37435      * @param {String} tooltip The tab's tooltip
37436      */
37437     setTooltip : function(text){
37438         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
37439             this.textEl.dom.qtip = text;
37440             this.textEl.dom.removeAttribute('title');
37441         }else{
37442             this.textEl.dom.title = text;
37443         }
37444     },
37445
37446     onTabClick : function(e){
37447         e.preventDefault();
37448         this.tabPanel.activate(this.id);
37449     },
37450
37451     onTabMouseDown : function(e){
37452         e.preventDefault();
37453         this.tabPanel.activate(this.id);
37454     },
37455 /*
37456     getWidth : function(){
37457         return this.inner.getWidth();
37458     },
37459
37460     setWidth : function(width){
37461         var iwidth = width - this.pnode.getPadding("lr");
37462         this.inner.setWidth(iwidth);
37463         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
37464         this.pnode.setWidth(width);
37465     },
37466 */
37467     /**
37468      * Show or hide the tab
37469      * @param {Boolean} hidden True to hide or false to show.
37470      */
37471     setHidden : function(hidden){
37472         this.hidden = hidden;
37473         this.pnode.setStyle("display", hidden ? "none" : "");
37474     },
37475
37476     /**
37477      * Returns true if this tab is "hidden"
37478      * @return {Boolean}
37479      */
37480     isHidden : function(){
37481         return this.hidden;
37482     },
37483
37484     /**
37485      * Returns the text for this tab
37486      * @return {String}
37487      */
37488     getText : function(){
37489         return this.text;
37490     },
37491     /*
37492     autoSize : function(){
37493         //this.el.beginMeasure();
37494         this.textEl.setWidth(1);
37495         /*
37496          *  #2804 [new] Tabs in Roojs
37497          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
37498          */
37499         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
37500         //this.el.endMeasure();
37501     //},
37502
37503     /**
37504      * Sets the text for the tab (Note: this also sets the tooltip text)
37505      * @param {String} text The tab's text and tooltip
37506      */
37507     setText : function(text){
37508         this.text = text;
37509         this.textEl.update(text);
37510         this.setTooltip(text);
37511         //if(!this.tabPanel.resizeTabs){
37512         //    this.autoSize();
37513         //}
37514     },
37515     /**
37516      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
37517      */
37518     activate : function(){
37519         this.tabPanel.activate(this.id);
37520     },
37521
37522     /**
37523      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
37524      */
37525     disable : function(){
37526         if(this.tabPanel.active != this){
37527             this.disabled = true;
37528             this.pnode.addClass("disabled");
37529         }
37530     },
37531
37532     /**
37533      * Enables this TabPanelItem if it was previously disabled.
37534      */
37535     enable : function(){
37536         this.disabled = false;
37537         this.pnode.removeClass("disabled");
37538     },
37539
37540     /**
37541      * Sets the content for this TabPanelItem.
37542      * @param {String} content The content
37543      * @param {Boolean} loadScripts true to look for and load scripts
37544      */
37545     setContent : function(content, loadScripts){
37546         this.bodyEl.update(content, loadScripts);
37547     },
37548
37549     /**
37550      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
37551      * @return {Roo.UpdateManager} The UpdateManager
37552      */
37553     getUpdateManager : function(){
37554         return this.bodyEl.getUpdateManager();
37555     },
37556
37557     /**
37558      * Set a URL to be used to load the content for this TabPanelItem.
37559      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
37560      * @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)
37561      * @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)
37562      * @return {Roo.UpdateManager} The UpdateManager
37563      */
37564     setUrl : function(url, params, loadOnce){
37565         if(this.refreshDelegate){
37566             this.un('activate', this.refreshDelegate);
37567         }
37568         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37569         this.on("activate", this.refreshDelegate);
37570         return this.bodyEl.getUpdateManager();
37571     },
37572
37573     /** @private */
37574     _handleRefresh : function(url, params, loadOnce){
37575         if(!loadOnce || !this.loaded){
37576             var updater = this.bodyEl.getUpdateManager();
37577             updater.update(url, params, this._setLoaded.createDelegate(this));
37578         }
37579     },
37580
37581     /**
37582      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
37583      *   Will fail silently if the setUrl method has not been called.
37584      *   This does not activate the panel, just updates its content.
37585      */
37586     refresh : function(){
37587         if(this.refreshDelegate){
37588            this.loaded = false;
37589            this.refreshDelegate();
37590         }
37591     },
37592
37593     /** @private */
37594     _setLoaded : function(){
37595         this.loaded = true;
37596     },
37597
37598     /** @private */
37599     closeClick : function(e){
37600         var o = {};
37601         e.stopEvent();
37602         this.fireEvent("beforeclose", this, o);
37603         if(o.cancel !== true){
37604             this.tabPanel.removeTab(this.id);
37605         }
37606     },
37607     /**
37608      * The text displayed in the tooltip for the close icon.
37609      * @type String
37610      */
37611     closeText : "Close this tab"
37612 });