roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         
105         cfg.id = this.id || Roo.id();
106         
107         // fill in the extra attributes 
108         if (this.xattr && typeof(this.xattr) =='object') {
109             for (var i in this.xattr) {
110                 cfg[i] = this.xattr[i];
111             }
112         }
113         
114         if(this.dataId){
115             cfg.dataId = this.dataId;
116         }
117         
118         if (this.cls) {
119             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
120         }
121         
122         if (this.style) { // fixme needs to support more complex style data.
123             cfg.style = this.style;
124         }
125         
126         if(this.name){
127             cfg.name = this.name;
128         }
129         
130         this.el = ct.createChild(cfg, position);
131         
132         if (this.tooltip) {
133             this.tooltipEl().attr('tooltip', this.tooltip);
134         }
135         
136         if(this.tabIndex !== undefined){
137             this.el.dom.setAttribute('tabIndex', this.tabIndex);
138         }
139         
140         this.initEvents();
141         
142     },
143     /**
144      * Fetch the element to add children to
145      * @return {Roo.Element} defaults to this.el
146      */
147     getChildContainer : function()
148     {
149         return this.el;
150     },
151     /**
152      * Fetch the element to display the tooltip on.
153      * @return {Roo.Element} defaults to this.el
154      */
155     tooltipEl : function()
156     {
157         return this.el;
158     },
159         
160     addxtype  : function(tree,cntr)
161     {
162         var cn = this;
163         
164         cn = Roo.factory(tree);
165         //Roo.log(['addxtype', cn]);
166            
167         cn.parentType = this.xtype; //??
168         cn.parentId = this.id;
169         
170         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171         if (typeof(cn.container_method) == 'string') {
172             cntr = cn.container_method;
173         }
174         
175         
176         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
177         
178         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
179         
180         var build_from_html =  Roo.XComponent.build_from_html;
181           
182         var is_body  = (tree.xtype == 'Body') ;
183           
184         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
185           
186         var self_cntr_el = Roo.get(this[cntr](false));
187         
188         // do not try and build conditional elements 
189         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
190             return false;
191         }
192         
193         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195                 return this.addxtypeChild(tree,cntr, is_body);
196             }
197             
198             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
199                 
200             if(echild){
201                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
202             }
203             
204             Roo.log('skipping render');
205             return cn;
206             
207         }
208         
209         var ret = false;
210         if (!build_from_html) {
211             return false;
212         }
213         
214         // this i think handles overlaying multiple children of the same type
215         // with the sam eelement.. - which might be buggy..
216         while (true) {
217             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
218             
219             if (!echild) {
220                 break;
221             }
222             
223             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
224                 break;
225             }
226             
227             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
228         }
229        
230         return ret;
231     },
232     
233     
234     addxtypeChild : function (tree, cntr, is_body)
235     {
236         Roo.debug && Roo.log('addxtypeChild:' + cntr);
237         var cn = this;
238         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
239         
240         
241         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
242                     (typeof(tree['flexy:foreach']) != 'undefined');
243           
244         
245         
246          skip_children = false;
247         // render the element if it's not BODY.
248         if (!is_body) {
249            
250             cn = Roo.factory(tree);
251            
252             cn.parentType = this.xtype; //??
253             cn.parentId = this.id;
254             
255             var build_from_html =  Roo.XComponent.build_from_html;
256             
257             
258             // does the container contain child eleemnts with 'xtype' attributes.
259             // that match this xtype..
260             // note - when we render we create these as well..
261             // so we should check to see if body has xtype set.
262             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
263                
264                 var self_cntr_el = Roo.get(this[cntr](false));
265                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
266                 if (echild) { 
267                     //Roo.log(Roo.XComponent.build_from_html);
268                     //Roo.log("got echild:");
269                     //Roo.log(echild);
270                 }
271                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
272                 // and are not displayed -this causes this to use up the wrong element when matching.
273                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
274                 
275                 
276                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
277                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
278                   
279                   
280                   
281                     cn.el = echild;
282                   //  Roo.log("GOT");
283                     //echild.dom.removeAttribute('xtype');
284                 } else {
285                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
286                     Roo.debug && Roo.log(self_cntr_el);
287                     Roo.debug && Roo.log(echild);
288                     Roo.debug && Roo.log(cn);
289                 }
290             }
291            
292             
293            
294             // if object has flexy:if - then it may or may not be rendered.
295             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
296                 // skip a flexy if element.
297                 Roo.debug && Roo.log('skipping render');
298                 Roo.debug && Roo.log(tree);
299                 if (!cn.el) {
300                     Roo.debug && Roo.log('skipping all children');
301                     skip_children = true;
302                 }
303                 
304              } else {
305                  
306                 // actually if flexy:foreach is found, we really want to create 
307                 // multiple copies here...
308                 //Roo.log('render');
309                 //Roo.log(this[cntr]());
310                 // some elements do not have render methods.. like the layouts...
311                 cn.render && cn.render(this[cntr](true));
312              }
313             // then add the element..
314         }
315         
316         
317         // handle the kids..
318         
319         var nitems = [];
320         /*
321         if (typeof (tree.menu) != 'undefined') {
322             tree.menu.parentType = cn.xtype;
323             tree.menu.triggerEl = cn.el;
324             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
325             
326         }
327         */
328         if (!tree.items || !tree.items.length) {
329             cn.items = nitems;
330             //Roo.log(["no children", this]);
331             
332             return cn;
333         }
334          
335         var items = tree.items;
336         delete tree.items;
337         
338         //Roo.log(items.length);
339             // add the items..
340         if (!skip_children) {    
341             for(var i =0;i < items.length;i++) {
342               //  Roo.log(['add child', items[i]]);
343                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
344             }
345         }
346         
347         cn.items = nitems;
348         
349         //Roo.log("fire childrenrendered");
350         
351         cn.fireEvent('childrenrendered', this);
352         
353         return cn;
354     },
355     /**
356      * Show a component - removes 'hidden' class
357      */
358     show : function()
359     {
360         if (this.el) {
361             this.el.removeClass('hidden');
362         }
363     },
364     /**
365      * Hide a component - adds 'hidden' class
366      */
367     hide: function()
368     {
369         if (this.el && !this.el.hasClass('hidden')) {
370             this.el.addClass('hidden');
371         }
372         
373     }
374 });
375
376  /*
377  * - LGPL
378  *
379  * Body
380  *
381  */
382
383 /**
384  * @class Roo.bootstrap.Body
385  * @extends Roo.bootstrap.Component
386  * Bootstrap Body class
387  *
388  * @constructor
389  * Create a new body
390  * @param {Object} config The config object
391  */
392
393 Roo.bootstrap.Body = function(config){
394
395     config = config || {};
396
397     Roo.bootstrap.Body.superclass.constructor.call(this, config);
398     this.el = Roo.get(config.el ? config.el : document.body );
399     if (this.cls && this.cls.length) {
400         Roo.get(document.body).addClass(this.cls);
401     }
402 };
403
404 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
405
406     is_body : true,// just to make sure it's constructed?
407
408         autoCreate : {
409         cls: 'container'
410     },
411     onRender : function(ct, position)
412     {
413        /* Roo.log("Roo.bootstrap.Body - onRender");
414         if (this.cls && this.cls.length) {
415             Roo.get(document.body).addClass(this.cls);
416         }
417         // style??? xttr???
418         */
419     }
420
421
422
423
424 });
425 /*
426  * - LGPL
427  *
428  * button group
429  * 
430  */
431
432
433 /**
434  * @class Roo.bootstrap.ButtonGroup
435  * @extends Roo.bootstrap.Component
436  * Bootstrap ButtonGroup class
437  * @cfg {String} size lg | sm | xs (default empty normal)
438  * @cfg {String} align vertical | justified  (default none)
439  * @cfg {String} direction up | down (default down)
440  * @cfg {Boolean} toolbar false | true
441  * @cfg {Boolean} btn true | false
442  * 
443  * 
444  * @constructor
445  * Create a new Input
446  * @param {Object} config The config object
447  */
448
449 Roo.bootstrap.ButtonGroup = function(config){
450     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
451 };
452
453 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
454     
455     size: '',
456     align: '',
457     direction: '',
458     toolbar: false,
459     btn: true,
460
461     getAutoCreate : function(){
462         var cfg = {
463             cls: 'btn-group',
464             html : null
465         };
466         
467         cfg.html = this.html || cfg.html;
468         
469         if (this.toolbar) {
470             cfg = {
471                 cls: 'btn-toolbar',
472                 html: null
473             };
474             
475             return cfg;
476         }
477         
478         if (['vertical','justified'].indexOf(this.align)!==-1) {
479             cfg.cls = 'btn-group-' + this.align;
480             
481             if (this.align == 'justified') {
482                 console.log(this.items);
483             }
484         }
485         
486         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
487             cfg.cls += ' btn-group-' + this.size;
488         }
489         
490         if (this.direction == 'up') {
491             cfg.cls += ' dropup' ;
492         }
493         
494         return cfg;
495     }
496    
497 });
498
499  /*
500  * - LGPL
501  *
502  * button
503  * 
504  */
505
506 /**
507  * @class Roo.bootstrap.Button
508  * @extends Roo.bootstrap.Component
509  * Bootstrap Button class
510  * @cfg {String} html The button content
511  * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default 
512  * @cfg {String} size ( lg | sm | xs)
513  * @cfg {String} tag ( a | input | submit)
514  * @cfg {String} href empty or href
515  * @cfg {Boolean} disabled default false;
516  * @cfg {Boolean} isClose default false;
517  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
518  * @cfg {String} badge text for badge
519  * @cfg {String} theme default 
520  * @cfg {Boolean} inverse 
521  * @cfg {Boolean} toggle 
522  * @cfg {String} ontext text for on toggle state
523  * @cfg {String} offtext text for off toggle state
524  * @cfg {Boolean} defaulton 
525  * @cfg {Boolean} preventDefault  default true
526  * @cfg {Boolean} removeClass remove the standard class..
527  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
528  * 
529  * @constructor
530  * Create a new button
531  * @param {Object} config The config object
532  */
533
534
535 Roo.bootstrap.Button = function(config){
536     Roo.bootstrap.Button.superclass.constructor.call(this, config);
537     this.addEvents({
538         // raw events
539         /**
540          * @event click
541          * When a butotn is pressed
542          * @param {Roo.bootstrap.Button} this
543          * @param {Roo.EventObject} e
544          */
545         "click" : true,
546          /**
547          * @event toggle
548          * After the button has been toggles
549          * @param {Roo.EventObject} e
550          * @param {boolean} pressed (also available as button.pressed)
551          */
552         "toggle" : true
553     });
554 };
555
556 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
557     html: false,
558     active: false,
559     weight: '',
560     size: '',
561     tag: 'button',
562     href: '',
563     disabled: false,
564     isClose: false,
565     glyphicon: '',
566     badge: '',
567     theme: 'default',
568     inverse: false,
569     
570     toggle: false,
571     ontext: 'ON',
572     offtext: 'OFF',
573     defaulton: true,
574     preventDefault: true,
575     removeClass: false,
576     name: false,
577     target: false,
578     
579     
580     pressed : null,
581      
582     
583     getAutoCreate : function(){
584         
585         var cfg = {
586             tag : 'button',
587             cls : 'roo-button',
588             html: ''
589         };
590         
591         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
592             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
593             this.tag = 'button';
594         } else {
595             cfg.tag = this.tag;
596         }
597         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
598         
599         if (this.toggle == true) {
600             cfg={
601                 tag: 'div',
602                 cls: 'slider-frame roo-button',
603                 cn: [
604                     {
605                         tag: 'span',
606                         'data-on-text':'ON',
607                         'data-off-text':'OFF',
608                         cls: 'slider-button',
609                         html: this.offtext
610                     }
611                 ]
612             };
613             
614             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
615                 cfg.cls += ' '+this.weight;
616             }
617             
618             return cfg;
619         }
620         
621         if (this.isClose) {
622             cfg.cls += ' close';
623             
624             cfg["aria-hidden"] = true;
625             
626             cfg.html = "&times;";
627             
628             return cfg;
629         }
630         
631          
632         if (this.theme==='default') {
633             cfg.cls = 'btn roo-button';
634             
635             //if (this.parentType != 'Navbar') {
636             this.weight = this.weight.length ?  this.weight : 'default';
637             //}
638             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
639                 
640                 cfg.cls += ' btn-' + this.weight;
641             }
642         } else if (this.theme==='glow') {
643             
644             cfg.tag = 'a';
645             cfg.cls = 'btn-glow roo-button';
646             
647             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
648                 
649                 cfg.cls += ' ' + this.weight;
650             }
651         }
652    
653         
654         if (this.inverse) {
655             this.cls += ' inverse';
656         }
657         
658         
659         if (this.active) {
660             cfg.cls += ' active';
661         }
662         
663         if (this.disabled) {
664             cfg.disabled = 'disabled';
665         }
666         
667         if (this.items) {
668             Roo.log('changing to ul' );
669             cfg.tag = 'ul';
670             this.glyphicon = 'caret';
671         }
672         
673         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
674          
675         //gsRoo.log(this.parentType);
676         if (this.parentType === 'Navbar' && !this.parent().bar) {
677             Roo.log('changing to li?');
678             
679             cfg.tag = 'li';
680             
681             cfg.cls = '';
682             cfg.cn =  [{
683                 tag : 'a',
684                 cls : 'roo-button',
685                 html : this.html,
686                 href : this.href || '#'
687             }];
688             if (this.menu) {
689                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
690                 cfg.cls += ' dropdown';
691             }   
692             
693             delete cfg.html;
694             
695         }
696         
697        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
698         
699         if (this.glyphicon) {
700             cfg.html = ' ' + cfg.html;
701             
702             cfg.cn = [
703                 {
704                     tag: 'span',
705                     cls: 'glyphicon glyphicon-' + this.glyphicon
706                 }
707             ];
708         }
709         
710         if (this.badge) {
711             cfg.html += ' ';
712             
713             cfg.tag = 'a';
714             
715 //            cfg.cls='btn roo-button';
716             
717             cfg.href=this.href;
718             
719             var value = cfg.html;
720             
721             if(this.glyphicon){
722                 value = {
723                             tag: 'span',
724                             cls: 'glyphicon glyphicon-' + this.glyphicon,
725                             html: this.html
726                         };
727                 
728             }
729             
730             cfg.cn = [
731                 value,
732                 {
733                     tag: 'span',
734                     cls: 'badge',
735                     html: this.badge
736                 }
737             ];
738             
739             cfg.html='';
740         }
741         
742         if (this.menu) {
743             cfg.cls += ' dropdown';
744             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
745         }
746         
747         if (cfg.tag !== 'a' && this.href !== '') {
748             throw "Tag must be a to set href.";
749         } else if (this.href.length > 0) {
750             cfg.href = this.href;
751         }
752         
753         if(this.removeClass){
754             cfg.cls = '';
755         }
756         
757         if(this.target){
758             cfg.target = this.target;
759         }
760         
761         return cfg;
762     },
763     initEvents: function() {
764        // Roo.log('init events?');
765 //        Roo.log(this.el.dom);
766         // add the menu...
767         
768         if (typeof (this.menu) != 'undefined') {
769             this.menu.parentType = this.xtype;
770             this.menu.triggerEl = this.el;
771             this.addxtype(Roo.apply({}, this.menu));
772         }
773
774
775        if (this.el.hasClass('roo-button')) {
776             this.el.on('click', this.onClick, this);
777        } else {
778             this.el.select('.roo-button').on('click', this.onClick, this);
779        }
780        
781        if(this.removeClass){
782            this.el.on('click', this.onClick, this);
783        }
784        
785        this.el.enableDisplayMode();
786         
787     },
788     onClick : function(e)
789     {
790         if (this.disabled) {
791             return;
792         }
793         
794         
795         Roo.log('button on click ');
796         if(this.preventDefault){
797             e.preventDefault();
798         }
799         if (this.pressed === true || this.pressed === false) {
800             this.pressed = !this.pressed;
801             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
802             this.fireEvent('toggle', this, e, this.pressed);
803         }
804         
805         
806         this.fireEvent('click', this, e);
807     },
808     
809     /**
810      * Enables this button
811      */
812     enable : function()
813     {
814         this.disabled = false;
815         this.el.removeClass('disabled');
816     },
817     
818     /**
819      * Disable this button
820      */
821     disable : function()
822     {
823         this.disabled = true;
824         this.el.addClass('disabled');
825     },
826      /**
827      * sets the active state on/off, 
828      * @param {Boolean} state (optional) Force a particular state
829      */
830     setActive : function(v) {
831         
832         this.el[v ? 'addClass' : 'removeClass']('active');
833     },
834      /**
835      * toggles the current active state 
836      */
837     toggleActive : function()
838     {
839        var active = this.el.hasClass('active');
840        this.setActive(!active);
841        
842         
843     },
844     setText : function(str)
845     {
846         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
847     },
848     getText : function()
849     {
850         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
851     },
852     hide: function() {
853        
854      
855         this.el.hide();   
856     },
857     show: function() {
858        
859         this.el.show();   
860     }
861     
862     
863 });
864
865  /*
866  * - LGPL
867  *
868  * column
869  * 
870  */
871
872 /**
873  * @class Roo.bootstrap.Column
874  * @extends Roo.bootstrap.Component
875  * Bootstrap Column class
876  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
877  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
878  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
879  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
880  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
881  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
882  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
883  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
884  *
885  * 
886  * @cfg {Boolean} hidden (true|false) hide the element
887  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
888  * @cfg {String} fa (ban|check|...) font awesome icon
889  * @cfg {Number} fasize (1|2|....) font awsome size
890
891  * @cfg {String} icon (info-sign|check|...) glyphicon name
892
893  * @cfg {String} html content of column.
894  * 
895  * @constructor
896  * Create a new Column
897  * @param {Object} config The config object
898  */
899
900 Roo.bootstrap.Column = function(config){
901     Roo.bootstrap.Column.superclass.constructor.call(this, config);
902 };
903
904 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
905     
906     xs: false,
907     sm: false,
908     md: false,
909     lg: false,
910     xsoff: false,
911     smoff: false,
912     mdoff: false,
913     lgoff: false,
914     html: '',
915     offset: 0,
916     alert: false,
917     fa: false,
918     icon : false,
919     hidden : false,
920     fasize : 1,
921     
922     getAutoCreate : function(){
923         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
924         
925         cfg = {
926             tag: 'div',
927             cls: 'column'
928         };
929         
930         var settings=this;
931         ['xs','sm','md','lg'].map(function(size){
932             //Roo.log( size + ':' + settings[size]);
933             
934             if (settings[size+'off'] !== false) {
935                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
936             }
937             
938             if (settings[size] === false) {
939                 return;
940             }
941             
942             if (!settings[size]) { // 0 = hidden
943                 cfg.cls += ' hidden-' + size;
944                 return;
945             }
946             cfg.cls += ' col-' + size + '-' + settings[size];
947             
948         });
949         
950         if (this.hidden) {
951             cfg.cls += ' hidden';
952         }
953         
954         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
955             cfg.cls +=' alert alert-' + this.alert;
956         }
957         
958         
959         if (this.html.length) {
960             cfg.html = this.html;
961         }
962         if (this.fa) {
963             var fasize = '';
964             if (this.fasize > 1) {
965                 fasize = ' fa-' + this.fasize + 'x';
966             }
967             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
968             
969             
970         }
971         if (this.icon) {
972             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
973         }
974         
975         return cfg;
976     }
977    
978 });
979
980  
981
982  /*
983  * - LGPL
984  *
985  * page container.
986  * 
987  */
988
989
990 /**
991  * @class Roo.bootstrap.Container
992  * @extends Roo.bootstrap.Component
993  * Bootstrap Container class
994  * @cfg {Boolean} jumbotron is it a jumbotron element
995  * @cfg {String} html content of element
996  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
997  * @cfg {String} panel (primary|success|info|warning|danger) render as panel  - type - primary/success.....
998  * @cfg {String} header content of header (for panel)
999  * @cfg {String} footer content of footer (for panel)
1000  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1001  * @cfg {String} tag (header|aside|section) type of HTML tag.
1002  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1003  * @cfg {String} fa font awesome icon
1004  * @cfg {String} icon (info-sign|check|...) glyphicon name
1005  * @cfg {Boolean} hidden (true|false) hide the element
1006  * @cfg {Boolean} expandable (true|false) default false
1007  * @cfg {Boolean} expanded (true|false) default true
1008  * @cfg {String} rheader contet on the right of header
1009  * @cfg {Boolean} clickable (true|false) default false
1010
1011  *     
1012  * @constructor
1013  * Create a new Container
1014  * @param {Object} config The config object
1015  */
1016
1017 Roo.bootstrap.Container = function(config){
1018     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1019     
1020     this.addEvents({
1021         // raw events
1022          /**
1023          * @event expand
1024          * After the panel has been expand
1025          * 
1026          * @param {Roo.bootstrap.Container} this
1027          */
1028         "expand" : true,
1029         /**
1030          * @event collapse
1031          * After the panel has been collapsed
1032          * 
1033          * @param {Roo.bootstrap.Container} this
1034          */
1035         "collapse" : true,
1036         /**
1037          * @event click
1038          * When a element is chick
1039          * @param {Roo.bootstrap.Container} this
1040          * @param {Roo.EventObject} e
1041          */
1042         "click" : true
1043     });
1044 };
1045
1046 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1047     
1048     jumbotron : false,
1049     well: '',
1050     panel : '',
1051     header: '',
1052     footer : '',
1053     sticky: '',
1054     tag : false,
1055     alert : false,
1056     fa: false,
1057     icon : false,
1058     expandable : false,
1059     rheader : '',
1060     expanded : true,
1061     clickable: false,
1062   
1063      
1064     getChildContainer : function() {
1065         
1066         if(!this.el){
1067             return false;
1068         }
1069         
1070         if (this.panel.length) {
1071             return this.el.select('.panel-body',true).first();
1072         }
1073         
1074         return this.el;
1075     },
1076     
1077     
1078     getAutoCreate : function(){
1079         
1080         var cfg = {
1081             tag : this.tag || 'div',
1082             html : '',
1083             cls : ''
1084         };
1085         if (this.jumbotron) {
1086             cfg.cls = 'jumbotron';
1087         }
1088         
1089         
1090         
1091         // - this is applied by the parent..
1092         //if (this.cls) {
1093         //    cfg.cls = this.cls + '';
1094         //}
1095         
1096         if (this.sticky.length) {
1097             
1098             var bd = Roo.get(document.body);
1099             if (!bd.hasClass('bootstrap-sticky')) {
1100                 bd.addClass('bootstrap-sticky');
1101                 Roo.select('html',true).setStyle('height', '100%');
1102             }
1103              
1104             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1105         }
1106         
1107         
1108         if (this.well.length) {
1109             switch (this.well) {
1110                 case 'lg':
1111                 case 'sm':
1112                     cfg.cls +=' well well-' +this.well;
1113                     break;
1114                 default:
1115                     cfg.cls +=' well';
1116                     break;
1117             }
1118         }
1119         
1120         if (this.hidden) {
1121             cfg.cls += ' hidden';
1122         }
1123         
1124         
1125         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1126             cfg.cls +=' alert alert-' + this.alert;
1127         }
1128         
1129         var body = cfg;
1130         
1131         if (this.panel.length) {
1132             cfg.cls += ' panel panel-' + this.panel;
1133             cfg.cn = [];
1134             if (this.header.length) {
1135                 
1136                 var h = [];
1137                 
1138                 if(this.expandable){
1139                     
1140                     cfg.cls = cfg.cls + ' expandable';
1141                     
1142                     h.push({
1143                         tag: 'i',
1144                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1145                     });
1146                     
1147                 }
1148                 
1149                 h.push(
1150                     {
1151                         tag: 'span',
1152                         cls : 'panel-title',
1153                         html : (this.expandable ? '&nbsp;' : '') + this.header
1154                     },
1155                     {
1156                         tag: 'span',
1157                         cls: 'panel-header-right',
1158                         html: this.rheader
1159                     }
1160                 );
1161                 
1162                 cfg.cn.push({
1163                     cls : 'panel-heading',
1164                     style : this.expandable ? 'cursor: pointer' : '',
1165                     cn : h
1166                 });
1167                 
1168             }
1169             
1170             body = false;
1171             cfg.cn.push({
1172                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1173                 html : this.html
1174             });
1175             
1176             
1177             if (this.footer.length) {
1178                 cfg.cn.push({
1179                     cls : 'panel-footer',
1180                     html : this.footer
1181                     
1182                 });
1183             }
1184             
1185         }
1186         
1187         if (body) {
1188             body.html = this.html || cfg.html;
1189             // prefix with the icons..
1190             if (this.fa) {
1191                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1192             }
1193             if (this.icon) {
1194                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1195             }
1196             
1197             
1198         }
1199         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1200             cfg.cls =  'container';
1201         }
1202         
1203         return cfg;
1204     },
1205     
1206     initEvents: function() 
1207     {
1208         if(this.expandable){
1209             var headerEl = this.headerEl();
1210         
1211             if(headerEl){
1212                 headerEl.on('click', this.onToggleClick, this);
1213             }
1214         }
1215         
1216         if(this.clickable){
1217             this.el.on('click', this.onClick, this);
1218         }
1219         
1220     },
1221     
1222     onToggleClick : function()
1223     {
1224         var headerEl = this.headerEl();
1225         
1226         if(!headerEl){
1227             return;
1228         }
1229         
1230         if(this.expanded){
1231             this.collapse();
1232             return;
1233         }
1234         
1235         this.expand();
1236     },
1237     
1238     expand : function()
1239     {
1240         if(this.fireEvent('expand', this)) {
1241             
1242             this.expanded = true;
1243             
1244             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1245             
1246             this.el.select('.panel-body',true).first().removeClass('hide');
1247             
1248             var toggleEl = this.toggleEl();
1249
1250             if(!toggleEl){
1251                 return;
1252             }
1253
1254             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1255         }
1256         
1257     },
1258     
1259     collapse : function()
1260     {
1261         if(this.fireEvent('collapse', this)) {
1262             
1263             this.expanded = false;
1264             
1265             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1266             this.el.select('.panel-body',true).first().addClass('hide');
1267         
1268             var toggleEl = this.toggleEl();
1269
1270             if(!toggleEl){
1271                 return;
1272             }
1273
1274             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1275         }
1276     },
1277     
1278     toggleEl : function()
1279     {
1280         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1281             return;
1282         }
1283         
1284         return this.el.select('.panel-heading .fa',true).first();
1285     },
1286     
1287     headerEl : function()
1288     {
1289         if(!this.el || !this.panel.length || !this.header.length){
1290             return;
1291         }
1292         
1293         return this.el.select('.panel-heading',true).first()
1294     },
1295     
1296     titleEl : function()
1297     {
1298         if(!this.el || !this.panel.length || !this.header.length){
1299             return;
1300         }
1301         
1302         return this.el.select('.panel-title',true).first();
1303     },
1304     
1305     setTitle : function(v)
1306     {
1307         var titleEl = this.titleEl();
1308         
1309         if(!titleEl){
1310             return;
1311         }
1312         
1313         titleEl.dom.innerHTML = v;
1314     },
1315     
1316     getTitle : function()
1317     {
1318         
1319         var titleEl = this.titleEl();
1320         
1321         if(!titleEl){
1322             return '';
1323         }
1324         
1325         return titleEl.dom.innerHTML;
1326     },
1327     
1328     setRightTitle : function(v)
1329     {
1330         var t = this.el.select('.panel-header-right',true).first();
1331         
1332         if(!t){
1333             return;
1334         }
1335         
1336         t.dom.innerHTML = v;
1337     },
1338     
1339     onClick : function(e)
1340     {
1341         e.preventDefault();
1342         
1343         this.fireEvent('click', this, e);
1344     }
1345    
1346 });
1347
1348  /*
1349  * - LGPL
1350  *
1351  * image
1352  * 
1353  */
1354
1355
1356 /**
1357  * @class Roo.bootstrap.Img
1358  * @extends Roo.bootstrap.Component
1359  * Bootstrap Img class
1360  * @cfg {Boolean} imgResponsive false | true
1361  * @cfg {String} border rounded | circle | thumbnail
1362  * @cfg {String} src image source
1363  * @cfg {String} alt image alternative text
1364  * @cfg {String} href a tag href
1365  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1366  * @cfg {String} xsUrl xs image source
1367  * @cfg {String} smUrl sm image source
1368  * @cfg {String} mdUrl md image source
1369  * @cfg {String} lgUrl lg image source
1370  * 
1371  * @constructor
1372  * Create a new Input
1373  * @param {Object} config The config object
1374  */
1375
1376 Roo.bootstrap.Img = function(config){
1377     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1378     
1379     this.addEvents({
1380         // img events
1381         /**
1382          * @event click
1383          * The img click event for the img.
1384          * @param {Roo.EventObject} e
1385          */
1386         "click" : true
1387     });
1388 };
1389
1390 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1391     
1392     imgResponsive: true,
1393     border: '',
1394     src: 'about:blank',
1395     href: false,
1396     target: false,
1397     xsUrl: '',
1398     smUrl: '',
1399     mdUrl: '',
1400     lgUrl: '',
1401
1402     getAutoCreate : function()
1403     {   
1404         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1405             return this.createSingleImg();
1406         }
1407         
1408         var cfg = {
1409             tag: 'div',
1410             cls: 'roo-image-responsive-group',
1411             cn: []
1412         };
1413         var _this = this;
1414         
1415         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1416             
1417             if(!_this[size + 'Url']){
1418                 return;
1419             }
1420             
1421             var img = {
1422                 tag: 'img',
1423                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1424                 html: _this.html || cfg.html,
1425                 src: _this[size + 'Url']
1426             };
1427             
1428             img.cls += ' roo-image-responsive-' + size;
1429             
1430             var s = ['xs', 'sm', 'md', 'lg'];
1431             
1432             s.splice(s.indexOf(size), 1);
1433             
1434             Roo.each(s, function(ss){
1435                 img.cls += ' hidden-' + ss;
1436             });
1437             
1438             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1439                 cfg.cls += ' img-' + _this.border;
1440             }
1441             
1442             if(_this.alt){
1443                 cfg.alt = _this.alt;
1444             }
1445             
1446             if(_this.href){
1447                 var a = {
1448                     tag: 'a',
1449                     href: _this.href,
1450                     cn: [
1451                         img
1452                     ]
1453                 };
1454
1455                 if(this.target){
1456                     a.target = _this.target;
1457                 }
1458             }
1459             
1460             cfg.cn.push((_this.href) ? a : img);
1461             
1462         });
1463         
1464         return cfg;
1465     },
1466     
1467     createSingleImg : function()
1468     {
1469         var cfg = {
1470             tag: 'img',
1471             cls: (this.imgResponsive) ? 'img-responsive' : '',
1472             html : null,
1473             src : 'about:blank'  // just incase src get's set to undefined?!?
1474         };
1475         
1476         cfg.html = this.html || cfg.html;
1477         
1478         cfg.src = this.src || cfg.src;
1479         
1480         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1481             cfg.cls += ' img-' + this.border;
1482         }
1483         
1484         if(this.alt){
1485             cfg.alt = this.alt;
1486         }
1487         
1488         if(this.href){
1489             var a = {
1490                 tag: 'a',
1491                 href: this.href,
1492                 cn: [
1493                     cfg
1494                 ]
1495             };
1496             
1497             if(this.target){
1498                 a.target = this.target;
1499             }
1500             
1501         }
1502         
1503         return (this.href) ? a : cfg;
1504     },
1505     
1506     initEvents: function() 
1507     {
1508         if(!this.href){
1509             this.el.on('click', this.onClick, this);
1510         }
1511         
1512     },
1513     
1514     onClick : function(e)
1515     {
1516         Roo.log('img onclick');
1517         this.fireEvent('click', this, e);
1518     },
1519     /**
1520      * Sets the url of the image - used to update it
1521      * @param {String} url the url of the image
1522      */
1523     
1524     setSrc : function(url)
1525     {
1526         this.src =  url;
1527         
1528         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1529             this.el.dom.src =  url;
1530             return;
1531         }
1532         
1533         this.el.select('img', true).first().dom.src =  url;
1534     }
1535     
1536     
1537    
1538 });
1539
1540  /*
1541  * - LGPL
1542  *
1543  * image
1544  * 
1545  */
1546
1547
1548 /**
1549  * @class Roo.bootstrap.Link
1550  * @extends Roo.bootstrap.Component
1551  * Bootstrap Link Class
1552  * @cfg {String} alt image alternative text
1553  * @cfg {String} href a tag href
1554  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1555  * @cfg {String} html the content of the link.
1556  * @cfg {String} anchor name for the anchor link
1557  * @cfg {String} fa - favicon
1558
1559  * @cfg {Boolean} preventDefault (true | false) default false
1560
1561  * 
1562  * @constructor
1563  * Create a new Input
1564  * @param {Object} config The config object
1565  */
1566
1567 Roo.bootstrap.Link = function(config){
1568     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1569     
1570     this.addEvents({
1571         // img events
1572         /**
1573          * @event click
1574          * The img click event for the img.
1575          * @param {Roo.EventObject} e
1576          */
1577         "click" : true
1578     });
1579 };
1580
1581 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1582     
1583     href: false,
1584     target: false,
1585     preventDefault: false,
1586     anchor : false,
1587     alt : false,
1588     fa: false,
1589
1590
1591     getAutoCreate : function()
1592     {
1593         var html = this.html || '';
1594         
1595         if (this.fa !== false) {
1596             html = '<i class="fa fa-' + this.fa + '"></i>';
1597         }
1598         var cfg = {
1599             tag: 'a'
1600         };
1601         // anchor's do not require html/href...
1602         if (this.anchor === false) {
1603             cfg.html = html;
1604             cfg.href = this.href || '#';
1605         } else {
1606             cfg.name = this.anchor;
1607             if (this.html !== false || this.fa !== false) {
1608                 cfg.html = html;
1609             }
1610             if (this.href !== false) {
1611                 cfg.href = this.href;
1612             }
1613         }
1614         
1615         if(this.alt !== false){
1616             cfg.alt = this.alt;
1617         }
1618         
1619         
1620         if(this.target !== false) {
1621             cfg.target = this.target;
1622         }
1623         
1624         return cfg;
1625     },
1626     
1627     initEvents: function() {
1628         
1629         if(!this.href || this.preventDefault){
1630             this.el.on('click', this.onClick, this);
1631         }
1632     },
1633     
1634     onClick : function(e)
1635     {
1636         if(this.preventDefault){
1637             e.preventDefault();
1638         }
1639         //Roo.log('img onclick');
1640         this.fireEvent('click', this, e);
1641     }
1642    
1643 });
1644
1645  /*
1646  * - LGPL
1647  *
1648  * header
1649  * 
1650  */
1651
1652 /**
1653  * @class Roo.bootstrap.Header
1654  * @extends Roo.bootstrap.Component
1655  * Bootstrap Header class
1656  * @cfg {String} html content of header
1657  * @cfg {Number} level (1|2|3|4|5|6) default 1
1658  * 
1659  * @constructor
1660  * Create a new Header
1661  * @param {Object} config The config object
1662  */
1663
1664
1665 Roo.bootstrap.Header  = function(config){
1666     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1667 };
1668
1669 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1670     
1671     //href : false,
1672     html : false,
1673     level : 1,
1674     
1675     
1676     
1677     getAutoCreate : function(){
1678         
1679         
1680         
1681         var cfg = {
1682             tag: 'h' + (1 *this.level),
1683             html: this.html || ''
1684         } ;
1685         
1686         return cfg;
1687     }
1688    
1689 });
1690
1691  
1692
1693  /*
1694  * Based on:
1695  * Ext JS Library 1.1.1
1696  * Copyright(c) 2006-2007, Ext JS, LLC.
1697  *
1698  * Originally Released Under LGPL - original licence link has changed is not relivant.
1699  *
1700  * Fork - LGPL
1701  * <script type="text/javascript">
1702  */
1703  
1704 /**
1705  * @class Roo.bootstrap.MenuMgr
1706  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1707  * @singleton
1708  */
1709 Roo.bootstrap.MenuMgr = function(){
1710    var menus, active, groups = {}, attached = false, lastShow = new Date();
1711
1712    // private - called when first menu is created
1713    function init(){
1714        menus = {};
1715        active = new Roo.util.MixedCollection();
1716        Roo.get(document).addKeyListener(27, function(){
1717            if(active.length > 0){
1718                hideAll();
1719            }
1720        });
1721    }
1722
1723    // private
1724    function hideAll(){
1725        if(active && active.length > 0){
1726            var c = active.clone();
1727            c.each(function(m){
1728                m.hide();
1729            });
1730        }
1731    }
1732
1733    // private
1734    function onHide(m){
1735        active.remove(m);
1736        if(active.length < 1){
1737            Roo.get(document).un("mouseup", onMouseDown);
1738             
1739            attached = false;
1740        }
1741    }
1742
1743    // private
1744    function onShow(m){
1745        var last = active.last();
1746        lastShow = new Date();
1747        active.add(m);
1748        if(!attached){
1749           Roo.get(document).on("mouseup", onMouseDown);
1750            
1751            attached = true;
1752        }
1753        if(m.parentMenu){
1754           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1755           m.parentMenu.activeChild = m;
1756        }else if(last && last.isVisible()){
1757           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1758        }
1759    }
1760
1761    // private
1762    function onBeforeHide(m){
1763        if(m.activeChild){
1764            m.activeChild.hide();
1765        }
1766        if(m.autoHideTimer){
1767            clearTimeout(m.autoHideTimer);
1768            delete m.autoHideTimer;
1769        }
1770    }
1771
1772    // private
1773    function onBeforeShow(m){
1774        var pm = m.parentMenu;
1775        if(!pm && !m.allowOtherMenus){
1776            hideAll();
1777        }else if(pm && pm.activeChild && active != m){
1778            pm.activeChild.hide();
1779        }
1780    }
1781
1782    // private this should really trigger on mouseup..
1783    function onMouseDown(e){
1784         Roo.log("on Mouse Up");
1785         
1786         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1787             Roo.log("MenuManager hideAll");
1788             hideAll();
1789             e.stopEvent();
1790         }
1791         
1792         
1793    }
1794
1795    // private
1796    function onBeforeCheck(mi, state){
1797        if(state){
1798            var g = groups[mi.group];
1799            for(var i = 0, l = g.length; i < l; i++){
1800                if(g[i] != mi){
1801                    g[i].setChecked(false);
1802                }
1803            }
1804        }
1805    }
1806
1807    return {
1808
1809        /**
1810         * Hides all menus that are currently visible
1811         */
1812        hideAll : function(){
1813             hideAll();  
1814        },
1815
1816        // private
1817        register : function(menu){
1818            if(!menus){
1819                init();
1820            }
1821            menus[menu.id] = menu;
1822            menu.on("beforehide", onBeforeHide);
1823            menu.on("hide", onHide);
1824            menu.on("beforeshow", onBeforeShow);
1825            menu.on("show", onShow);
1826            var g = menu.group;
1827            if(g && menu.events["checkchange"]){
1828                if(!groups[g]){
1829                    groups[g] = [];
1830                }
1831                groups[g].push(menu);
1832                menu.on("checkchange", onCheck);
1833            }
1834        },
1835
1836         /**
1837          * Returns a {@link Roo.menu.Menu} object
1838          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1839          * be used to generate and return a new Menu instance.
1840          */
1841        get : function(menu){
1842            if(typeof menu == "string"){ // menu id
1843                return menus[menu];
1844            }else if(menu.events){  // menu instance
1845                return menu;
1846            }
1847            /*else if(typeof menu.length == 'number'){ // array of menu items?
1848                return new Roo.bootstrap.Menu({items:menu});
1849            }else{ // otherwise, must be a config
1850                return new Roo.bootstrap.Menu(menu);
1851            }
1852            */
1853            return false;
1854        },
1855
1856        // private
1857        unregister : function(menu){
1858            delete menus[menu.id];
1859            menu.un("beforehide", onBeforeHide);
1860            menu.un("hide", onHide);
1861            menu.un("beforeshow", onBeforeShow);
1862            menu.un("show", onShow);
1863            var g = menu.group;
1864            if(g && menu.events["checkchange"]){
1865                groups[g].remove(menu);
1866                menu.un("checkchange", onCheck);
1867            }
1868        },
1869
1870        // private
1871        registerCheckable : function(menuItem){
1872            var g = menuItem.group;
1873            if(g){
1874                if(!groups[g]){
1875                    groups[g] = [];
1876                }
1877                groups[g].push(menuItem);
1878                menuItem.on("beforecheckchange", onBeforeCheck);
1879            }
1880        },
1881
1882        // private
1883        unregisterCheckable : function(menuItem){
1884            var g = menuItem.group;
1885            if(g){
1886                groups[g].remove(menuItem);
1887                menuItem.un("beforecheckchange", onBeforeCheck);
1888            }
1889        }
1890    };
1891 }();/*
1892  * - LGPL
1893  *
1894  * menu
1895  * 
1896  */
1897
1898 /**
1899  * @class Roo.bootstrap.Menu
1900  * @extends Roo.bootstrap.Component
1901  * Bootstrap Menu class - container for MenuItems
1902  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1903  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1904  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1905  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1906  * 
1907  * @constructor
1908  * Create a new Menu
1909  * @param {Object} config The config object
1910  */
1911
1912
1913 Roo.bootstrap.Menu = function(config){
1914     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1915     if (this.registerMenu && this.type != 'treeview')  {
1916         Roo.bootstrap.MenuMgr.register(this);
1917     }
1918     this.addEvents({
1919         /**
1920          * @event beforeshow
1921          * Fires before this menu is displayed
1922          * @param {Roo.menu.Menu} this
1923          */
1924         beforeshow : true,
1925         /**
1926          * @event beforehide
1927          * Fires before this menu is hidden
1928          * @param {Roo.menu.Menu} this
1929          */
1930         beforehide : true,
1931         /**
1932          * @event show
1933          * Fires after this menu is displayed
1934          * @param {Roo.menu.Menu} this
1935          */
1936         show : true,
1937         /**
1938          * @event hide
1939          * Fires after this menu is hidden
1940          * @param {Roo.menu.Menu} this
1941          */
1942         hide : true,
1943         /**
1944          * @event click
1945          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1946          * @param {Roo.menu.Menu} this
1947          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1948          * @param {Roo.EventObject} e
1949          */
1950         click : true,
1951         /**
1952          * @event mouseover
1953          * Fires when the mouse is hovering over this menu
1954          * @param {Roo.menu.Menu} this
1955          * @param {Roo.EventObject} e
1956          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1957          */
1958         mouseover : true,
1959         /**
1960          * @event mouseout
1961          * Fires when the mouse exits this menu
1962          * @param {Roo.menu.Menu} this
1963          * @param {Roo.EventObject} e
1964          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1965          */
1966         mouseout : true,
1967         /**
1968          * @event itemclick
1969          * Fires when a menu item contained in this menu is clicked
1970          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1971          * @param {Roo.EventObject} e
1972          */
1973         itemclick: true
1974     });
1975     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1976 };
1977
1978 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1979     
1980    /// html : false,
1981     //align : '',
1982     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1983     type: false,
1984     /**
1985      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1986      */
1987     registerMenu : true,
1988     
1989     menuItems :false, // stores the menu items..
1990     
1991     hidden:true,
1992         
1993     parentMenu : false,
1994     
1995     stopEvent : true,
1996     
1997     isLink : false,
1998     
1999     getChildContainer : function() {
2000         return this.el;  
2001     },
2002     
2003     getAutoCreate : function(){
2004          
2005         //if (['right'].indexOf(this.align)!==-1) {
2006         //    cfg.cn[1].cls += ' pull-right'
2007         //}
2008         
2009         
2010         var cfg = {
2011             tag : 'ul',
2012             cls : 'dropdown-menu' ,
2013             style : 'z-index:1000'
2014             
2015         };
2016         
2017         if (this.type === 'submenu') {
2018             cfg.cls = 'submenu active';
2019         }
2020         if (this.type === 'treeview') {
2021             cfg.cls = 'treeview-menu';
2022         }
2023         
2024         return cfg;
2025     },
2026     initEvents : function() {
2027         
2028        // Roo.log("ADD event");
2029        // Roo.log(this.triggerEl.dom);
2030         
2031         this.triggerEl.on('click', this.onTriggerClick, this);
2032         
2033         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2034         
2035         this.triggerEl.addClass('dropdown-toggle');
2036         
2037         if (Roo.isTouch) {
2038             this.el.on('touchstart'  , this.onTouch, this);
2039         }
2040         this.el.on('click' , this.onClick, this);
2041
2042         this.el.on("mouseover", this.onMouseOver, this);
2043         this.el.on("mouseout", this.onMouseOut, this);
2044         
2045     },
2046     
2047     findTargetItem : function(e)
2048     {
2049         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2050         if(!t){
2051             return false;
2052         }
2053         //Roo.log(t);         Roo.log(t.id);
2054         if(t && t.id){
2055             //Roo.log(this.menuitems);
2056             return this.menuitems.get(t.id);
2057             
2058             //return this.items.get(t.menuItemId);
2059         }
2060         
2061         return false;
2062     },
2063     
2064     onTouch : function(e) 
2065     {
2066         Roo.log("menu.onTouch");
2067         //e.stopEvent(); this make the user popdown broken
2068         this.onClick(e);
2069     },
2070     
2071     onClick : function(e)
2072     {
2073         Roo.log("menu.onClick");
2074         
2075         var t = this.findTargetItem(e);
2076         if(!t || t.isContainer){
2077             return;
2078         }
2079         Roo.log(e);
2080         /*
2081         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2082             if(t == this.activeItem && t.shouldDeactivate(e)){
2083                 this.activeItem.deactivate();
2084                 delete this.activeItem;
2085                 return;
2086             }
2087             if(t.canActivate){
2088                 this.setActiveItem(t, true);
2089             }
2090             return;
2091             
2092             
2093         }
2094         */
2095        
2096         Roo.log('pass click event');
2097         
2098         t.onClick(e);
2099         
2100         this.fireEvent("click", this, t, e);
2101         
2102         var _this = this;
2103         
2104         (function() { _this.hide(); }).defer(100);
2105     },
2106     
2107     onMouseOver : function(e){
2108         var t  = this.findTargetItem(e);
2109         //Roo.log(t);
2110         //if(t){
2111         //    if(t.canActivate && !t.disabled){
2112         //        this.setActiveItem(t, true);
2113         //    }
2114         //}
2115         
2116         this.fireEvent("mouseover", this, e, t);
2117     },
2118     isVisible : function(){
2119         return !this.hidden;
2120     },
2121      onMouseOut : function(e){
2122         var t  = this.findTargetItem(e);
2123         
2124         //if(t ){
2125         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2126         //        this.activeItem.deactivate();
2127         //        delete this.activeItem;
2128         //    }
2129         //}
2130         this.fireEvent("mouseout", this, e, t);
2131     },
2132     
2133     
2134     /**
2135      * Displays this menu relative to another element
2136      * @param {String/HTMLElement/Roo.Element} element The element to align to
2137      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2138      * the element (defaults to this.defaultAlign)
2139      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2140      */
2141     show : function(el, pos, parentMenu){
2142         this.parentMenu = parentMenu;
2143         if(!this.el){
2144             this.render();
2145         }
2146         this.fireEvent("beforeshow", this);
2147         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2148     },
2149      /**
2150      * Displays this menu at a specific xy position
2151      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2152      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2153      */
2154     showAt : function(xy, parentMenu, /* private: */_e){
2155         this.parentMenu = parentMenu;
2156         if(!this.el){
2157             this.render();
2158         }
2159         if(_e !== false){
2160             this.fireEvent("beforeshow", this);
2161             //xy = this.el.adjustForConstraints(xy);
2162         }
2163         
2164         //this.el.show();
2165         this.hideMenuItems();
2166         this.hidden = false;
2167         this.triggerEl.addClass('open');
2168         
2169         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2170             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2171         }
2172         
2173         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2174             this.el.setXY(xy);
2175         }
2176         
2177         this.focus();
2178         this.fireEvent("show", this);
2179     },
2180     
2181     focus : function(){
2182         return;
2183         if(!this.hidden){
2184             this.doFocus.defer(50, this);
2185         }
2186     },
2187
2188     doFocus : function(){
2189         if(!this.hidden){
2190             this.focusEl.focus();
2191         }
2192     },
2193
2194     /**
2195      * Hides this menu and optionally all parent menus
2196      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2197      */
2198     hide : function(deep)
2199     {
2200         
2201         this.hideMenuItems();
2202         if(this.el && this.isVisible()){
2203             this.fireEvent("beforehide", this);
2204             if(this.activeItem){
2205                 this.activeItem.deactivate();
2206                 this.activeItem = null;
2207             }
2208             this.triggerEl.removeClass('open');;
2209             this.hidden = true;
2210             this.fireEvent("hide", this);
2211         }
2212         if(deep === true && this.parentMenu){
2213             this.parentMenu.hide(true);
2214         }
2215     },
2216     
2217     onTriggerClick : function(e)
2218     {
2219         Roo.log('trigger click');
2220         
2221         var target = e.getTarget();
2222         
2223         Roo.log(target.nodeName.toLowerCase());
2224         
2225         if(target.nodeName.toLowerCase() === 'i'){
2226             e.preventDefault();
2227         }
2228         
2229     },
2230     
2231     onTriggerPress  : function(e)
2232     {
2233         Roo.log('trigger press');
2234         //Roo.log(e.getTarget());
2235        // Roo.log(this.triggerEl.dom);
2236        
2237         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2238         var pel = Roo.get(e.getTarget());
2239         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2240             Roo.log('is treeview or dropdown?');
2241             return;
2242         }
2243         
2244         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2245             return;
2246         }
2247         
2248         if (this.isVisible()) {
2249             Roo.log('hide');
2250             this.hide();
2251         } else {
2252             Roo.log('show');
2253             this.show(this.triggerEl, false, false);
2254         }
2255         
2256         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2257             e.stopEvent();
2258         }
2259         
2260     },
2261        
2262     
2263     hideMenuItems : function()
2264     {
2265         Roo.log("hide Menu Items");
2266         if (!this.el) { 
2267             return;
2268         }
2269         //$(backdrop).remove()
2270         this.el.select('.open',true).each(function(aa) {
2271             
2272             aa.removeClass('open');
2273           //var parent = getParent($(this))
2274           //var relatedTarget = { relatedTarget: this }
2275           
2276            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2277           //if (e.isDefaultPrevented()) return
2278            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2279         });
2280     },
2281     addxtypeChild : function (tree, cntr) {
2282         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2283           
2284         this.menuitems.add(comp);
2285         return comp;
2286
2287     },
2288     getEl : function()
2289     {
2290         Roo.log(this.el);
2291         return this.el;
2292     }
2293 });
2294
2295  
2296  /*
2297  * - LGPL
2298  *
2299  * menu item
2300  * 
2301  */
2302
2303
2304 /**
2305  * @class Roo.bootstrap.MenuItem
2306  * @extends Roo.bootstrap.Component
2307  * Bootstrap MenuItem class
2308  * @cfg {String} html the menu label
2309  * @cfg {String} href the link
2310  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2311  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2312  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2313  * @cfg {String} fa favicon to show on left of menu item.
2314  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2315  * 
2316  * 
2317  * @constructor
2318  * Create a new MenuItem
2319  * @param {Object} config The config object
2320  */
2321
2322
2323 Roo.bootstrap.MenuItem = function(config){
2324     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2325     this.addEvents({
2326         // raw events
2327         /**
2328          * @event click
2329          * The raw click event for the entire grid.
2330          * @param {Roo.bootstrap.MenuItem} this
2331          * @param {Roo.EventObject} e
2332          */
2333         "click" : true
2334     });
2335 };
2336
2337 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2338     
2339     href : false,
2340     html : false,
2341     preventDefault: false,
2342     isContainer : false,
2343     active : false,
2344     fa: false,
2345     
2346     getAutoCreate : function(){
2347         
2348         if(this.isContainer){
2349             return {
2350                 tag: 'li',
2351                 cls: 'dropdown-menu-item'
2352             };
2353         }
2354         var ctag = {
2355             tag: 'span',
2356             html: 'Link'
2357         };
2358         
2359         var anc = {
2360             tag : 'a',
2361             href : '#',
2362             cn : [  ]
2363         };
2364         
2365         if (this.fa !== false) {
2366             anc.cn.push({
2367                 tag : 'i',
2368                 cls : 'fa fa-' + this.fa
2369             });
2370         }
2371         
2372         anc.cn.push(ctag);
2373         
2374         
2375         var cfg= {
2376             tag: 'li',
2377             cls: 'dropdown-menu-item',
2378             cn: [ anc ]
2379         };
2380         if (this.parent().type == 'treeview') {
2381             cfg.cls = 'treeview-menu';
2382         }
2383         if (this.active) {
2384             cfg.cls += ' active';
2385         }
2386         
2387         
2388         
2389         anc.href = this.href || cfg.cn[0].href ;
2390         ctag.html = this.html || cfg.cn[0].html ;
2391         return cfg;
2392     },
2393     
2394     initEvents: function()
2395     {
2396         if (this.parent().type == 'treeview') {
2397             this.el.select('a').on('click', this.onClick, this);
2398         }
2399         if (this.menu) {
2400             this.menu.parentType = this.xtype;
2401             this.menu.triggerEl = this.el;
2402             this.menu = this.addxtype(Roo.apply({}, this.menu));
2403         }
2404         
2405     },
2406     onClick : function(e)
2407     {
2408         Roo.log('item on click ');
2409         
2410         if(this.preventDefault){
2411             e.preventDefault();
2412         }
2413         //this.parent().hideMenuItems();
2414         
2415         this.fireEvent('click', this, e);
2416     },
2417     getEl : function()
2418     {
2419         return this.el;
2420     } 
2421 });
2422
2423  
2424
2425  /*
2426  * - LGPL
2427  *
2428  * menu separator
2429  * 
2430  */
2431
2432
2433 /**
2434  * @class Roo.bootstrap.MenuSeparator
2435  * @extends Roo.bootstrap.Component
2436  * Bootstrap MenuSeparator class
2437  * 
2438  * @constructor
2439  * Create a new MenuItem
2440  * @param {Object} config The config object
2441  */
2442
2443
2444 Roo.bootstrap.MenuSeparator = function(config){
2445     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2446 };
2447
2448 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2449     
2450     getAutoCreate : function(){
2451         var cfg = {
2452             cls: 'divider',
2453             tag : 'li'
2454         };
2455         
2456         return cfg;
2457     }
2458    
2459 });
2460
2461  
2462
2463  
2464 /*
2465 * Licence: LGPL
2466 */
2467
2468 /**
2469  * @class Roo.bootstrap.Modal
2470  * @extends Roo.bootstrap.Component
2471  * Bootstrap Modal class
2472  * @cfg {String} title Title of dialog
2473  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2474  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2475  * @cfg {Boolean} specificTitle default false
2476  * @cfg {Array} buttons Array of buttons or standard button set..
2477  * @cfg {String} buttonPosition (left|right|center) default right
2478  * @cfg {Boolean} animate default true
2479  * @cfg {Boolean} allow_close default true
2480  * @cfg {Boolean} fitwindow default false
2481  * @cfg {String} size (sm|lg) default empty
2482  *
2483  *
2484  * @constructor
2485  * Create a new Modal Dialog
2486  * @param {Object} config The config object
2487  */
2488
2489 Roo.bootstrap.Modal = function(config){
2490     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2491     this.addEvents({
2492         // raw events
2493         /**
2494          * @event btnclick
2495          * The raw btnclick event for the button
2496          * @param {Roo.EventObject} e
2497          */
2498         "btnclick" : true
2499     });
2500     this.buttons = this.buttons || [];
2501
2502     if (this.tmpl) {
2503         this.tmpl = Roo.factory(this.tmpl);
2504     }
2505
2506 };
2507
2508 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2509
2510     title : 'test dialog',
2511
2512     buttons : false,
2513
2514     // set on load...
2515
2516     html: false,
2517
2518     tmp: false,
2519
2520     specificTitle: false,
2521
2522     buttonPosition: 'right',
2523
2524     allow_close : true,
2525
2526     animate : true,
2527
2528     fitwindow: false,
2529
2530
2531      // private
2532     dialogEl: false,
2533     bodyEl:  false,
2534     footerEl:  false,
2535     titleEl:  false,
2536     closeEl:  false,
2537
2538     size: '',
2539
2540
2541     onRender : function(ct, position)
2542     {
2543         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2544
2545         if(!this.el){
2546             var cfg = Roo.apply({},  this.getAutoCreate());
2547             cfg.id = Roo.id();
2548             //if(!cfg.name){
2549             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2550             //}
2551             //if (!cfg.name.length) {
2552             //    delete cfg.name;
2553            // }
2554             if (this.cls) {
2555                 cfg.cls += ' ' + this.cls;
2556             }
2557             if (this.style) {
2558                 cfg.style = this.style;
2559             }
2560             this.el = Roo.get(document.body).createChild(cfg, position);
2561         }
2562         //var type = this.el.dom.type;
2563
2564
2565         if(this.tabIndex !== undefined){
2566             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2567         }
2568
2569         this.dialogEl = this.el.select('.modal-dialog',true).first();
2570         this.bodyEl = this.el.select('.modal-body',true).first();
2571         this.closeEl = this.el.select('.modal-header .close', true).first();
2572         this.headerEl = this.el.select('.modal-header',true).first();
2573         this.titleEl = this.el.select('.modal-title',true).first();
2574         this.footerEl = this.el.select('.modal-footer',true).first();
2575
2576         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2577         this.maskEl.enableDisplayMode("block");
2578         this.maskEl.hide();
2579         //this.el.addClass("x-dlg-modal");
2580
2581         if (this.buttons.length) {
2582             Roo.each(this.buttons, function(bb) {
2583                 var b = Roo.apply({}, bb);
2584                 b.xns = b.xns || Roo.bootstrap;
2585                 b.xtype = b.xtype || 'Button';
2586                 if (typeof(b.listeners) == 'undefined') {
2587                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2588                 }
2589
2590                 var btn = Roo.factory(b);
2591
2592                 btn.render(this.el.select('.modal-footer div').first());
2593
2594             },this);
2595         }
2596         // render the children.
2597         var nitems = [];
2598
2599         if(typeof(this.items) != 'undefined'){
2600             var items = this.items;
2601             delete this.items;
2602
2603             for(var i =0;i < items.length;i++) {
2604                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2605             }
2606         }
2607
2608         this.items = nitems;
2609
2610         // where are these used - they used to be body/close/footer
2611
2612
2613         this.initEvents();
2614         //this.el.addClass([this.fieldClass, this.cls]);
2615
2616     },
2617
2618     getAutoCreate : function(){
2619
2620
2621         var bdy = {
2622                 cls : 'modal-body',
2623                 html : this.html || ''
2624         };
2625
2626         var title = {
2627             tag: 'h4',
2628             cls : 'modal-title',
2629             html : this.title
2630         };
2631
2632         if(this.specificTitle){
2633             title = this.title;
2634
2635         };
2636
2637         var header = [];
2638         if (this.allow_close) {
2639             header.push({
2640                 tag: 'button',
2641                 cls : 'close',
2642                 html : '&times'
2643             });
2644         }
2645
2646         header.push(title);
2647
2648         var size = '';
2649
2650         if(this.size.length){
2651             size = 'modal-' + this.size;
2652         }
2653
2654         var modal = {
2655             cls: "modal",
2656             style : 'display: none',
2657             cn : [
2658                 {
2659                     cls: "modal-dialog " + size,
2660                     cn : [
2661                         {
2662                             cls : "modal-content",
2663                             cn : [
2664                                 {
2665                                     cls : 'modal-header',
2666                                     cn : header
2667                                 },
2668                                 bdy,
2669                                 {
2670                                     cls : 'modal-footer',
2671                                     cn : [
2672                                         {
2673                                             tag: 'div',
2674                                             cls: 'btn-' + this.buttonPosition
2675                                         }
2676                                     ]
2677
2678                                 }
2679
2680
2681                             ]
2682
2683                         }
2684                     ]
2685
2686                 }
2687             ]
2688         };
2689
2690         if(this.animate){
2691             modal.cls += ' fade';
2692         }
2693
2694         return modal;
2695
2696     },
2697     getChildContainer : function() {
2698
2699          return this.bodyEl;
2700
2701     },
2702     getButtonContainer : function() {
2703          return this.el.select('.modal-footer div',true).first();
2704
2705     },
2706     initEvents : function()
2707     {
2708         if (this.allow_close) {
2709             this.closeEl.on('click', this.hide, this);
2710         }
2711         Roo.EventManager.onWindowResize(this.resize, this, true);
2712
2713
2714     },
2715
2716     resize : function()
2717     {
2718         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2719         if (this.fitwindow) {
2720             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2721             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2722             this.setSize(w,h);
2723         }
2724     },
2725
2726     setSize : function(w,h)
2727     {
2728         if (!w && !h) {
2729             return;
2730         }
2731         this.resizeTo(w,h);
2732     },
2733
2734     show : function() {
2735
2736         if (!this.rendered) {
2737             this.render();
2738         }
2739
2740         this.el.setStyle('display', 'block');
2741
2742         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2743             var _this = this;
2744             (function(){
2745                 this.el.addClass('in');
2746             }).defer(50, this);
2747         }else{
2748             this.el.addClass('in');
2749
2750         }
2751
2752         // not sure how we can show data in here..
2753         //if (this.tmpl) {
2754         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2755         //}
2756
2757         Roo.get(document.body).addClass("x-body-masked");
2758         
2759         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2760         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2761         this.maskEl.show();
2762         
2763         this.resize();
2764         
2765         this.fireEvent('show', this);
2766
2767         // set zindex here - otherwise it appears to be ignored...
2768         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2769
2770         (function () {
2771             this.items.forEach( function(e) {
2772                 e.layout ? e.layout() : false;
2773
2774             });
2775         }).defer(100,this);
2776
2777     },
2778     hide : function()
2779     {
2780         if(this.fireEvent("beforehide", this) !== false){
2781             this.maskEl.hide();
2782             Roo.get(document.body).removeClass("x-body-masked");
2783             this.el.removeClass('in');
2784             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2785
2786             if(this.animate){ // why
2787                 var _this = this;
2788                 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2789             }else{
2790                 this.el.setStyle('display', 'none');
2791             }
2792             this.fireEvent('hide', this);
2793         }
2794     },
2795
2796     addButton : function(str, cb)
2797     {
2798
2799
2800         var b = Roo.apply({}, { html : str } );
2801         b.xns = b.xns || Roo.bootstrap;
2802         b.xtype = b.xtype || 'Button';
2803         if (typeof(b.listeners) == 'undefined') {
2804             b.listeners = { click : cb.createDelegate(this)  };
2805         }
2806
2807         var btn = Roo.factory(b);
2808
2809         btn.render(this.el.select('.modal-footer div').first());
2810
2811         return btn;
2812
2813     },
2814
2815     setDefaultButton : function(btn)
2816     {
2817         //this.el.select('.modal-footer').()
2818     },
2819     diff : false,
2820
2821     resizeTo: function(w,h)
2822     {
2823         // skip.. ?? why??
2824
2825         this.dialogEl.setWidth(w);
2826         if (this.diff === false) {
2827             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2828         }
2829
2830         this.bodyEl.setHeight(h-this.diff);
2831
2832
2833     },
2834     setContentSize  : function(w, h)
2835     {
2836
2837     },
2838     onButtonClick: function(btn,e)
2839     {
2840         //Roo.log([a,b,c]);
2841         this.fireEvent('btnclick', btn.name, e);
2842     },
2843      /**
2844      * Set the title of the Dialog
2845      * @param {String} str new Title
2846      */
2847     setTitle: function(str) {
2848         this.titleEl.dom.innerHTML = str;
2849     },
2850     /**
2851      * Set the body of the Dialog
2852      * @param {String} str new Title
2853      */
2854     setBody: function(str) {
2855         this.bodyEl.dom.innerHTML = str;
2856     },
2857     /**
2858      * Set the body of the Dialog using the template
2859      * @param {Obj} data - apply this data to the template and replace the body contents.
2860      */
2861     applyBody: function(obj)
2862     {
2863         if (!this.tmpl) {
2864             Roo.log("Error - using apply Body without a template");
2865             //code
2866         }
2867         this.tmpl.overwrite(this.bodyEl, obj);
2868     }
2869
2870 });
2871
2872
2873 Roo.apply(Roo.bootstrap.Modal,  {
2874     /**
2875          * Button config that displays a single OK button
2876          * @type Object
2877          */
2878         OK :  [{
2879             name : 'ok',
2880             weight : 'primary',
2881             html : 'OK'
2882         }],
2883         /**
2884          * Button config that displays Yes and No buttons
2885          * @type Object
2886          */
2887         YESNO : [
2888             {
2889                 name  : 'no',
2890                 html : 'No'
2891             },
2892             {
2893                 name  :'yes',
2894                 weight : 'primary',
2895                 html : 'Yes'
2896             }
2897         ],
2898
2899         /**
2900          * Button config that displays OK and Cancel buttons
2901          * @type Object
2902          */
2903         OKCANCEL : [
2904             {
2905                name : 'cancel',
2906                 html : 'Cancel'
2907             },
2908             {
2909                 name : 'ok',
2910                 weight : 'primary',
2911                 html : 'OK'
2912             }
2913         ],
2914         /**
2915          * Button config that displays Yes, No and Cancel buttons
2916          * @type Object
2917          */
2918         YESNOCANCEL : [
2919             {
2920                 name : 'yes',
2921                 weight : 'primary',
2922                 html : 'Yes'
2923             },
2924             {
2925                 name : 'no',
2926                 html : 'No'
2927             },
2928             {
2929                 name : 'cancel',
2930                 html : 'Cancel'
2931             }
2932         ],
2933         
2934         zIndex : 10001
2935 });
2936 /*
2937  * - LGPL
2938  *
2939  * messagebox - can be used as a replace
2940  * 
2941  */
2942 /**
2943  * @class Roo.MessageBox
2944  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2945  * Example usage:
2946  *<pre><code>
2947 // Basic alert:
2948 Roo.Msg.alert('Status', 'Changes saved successfully.');
2949
2950 // Prompt for user data:
2951 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2952     if (btn == 'ok'){
2953         // process text value...
2954     }
2955 });
2956
2957 // Show a dialog using config options:
2958 Roo.Msg.show({
2959    title:'Save Changes?',
2960    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2961    buttons: Roo.Msg.YESNOCANCEL,
2962    fn: processResult,
2963    animEl: 'elId'
2964 });
2965 </code></pre>
2966  * @singleton
2967  */
2968 Roo.bootstrap.MessageBox = function(){
2969     var dlg, opt, mask, waitTimer;
2970     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2971     var buttons, activeTextEl, bwidth;
2972
2973     
2974     // private
2975     var handleButton = function(button){
2976         dlg.hide();
2977         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2978     };
2979
2980     // private
2981     var handleHide = function(){
2982         if(opt && opt.cls){
2983             dlg.el.removeClass(opt.cls);
2984         }
2985         //if(waitTimer){
2986         //    Roo.TaskMgr.stop(waitTimer);
2987         //    waitTimer = null;
2988         //}
2989     };
2990
2991     // private
2992     var updateButtons = function(b){
2993         var width = 0;
2994         if(!b){
2995             buttons["ok"].hide();
2996             buttons["cancel"].hide();
2997             buttons["yes"].hide();
2998             buttons["no"].hide();
2999             //dlg.footer.dom.style.display = 'none';
3000             return width;
3001         }
3002         dlg.footerEl.dom.style.display = '';
3003         for(var k in buttons){
3004             if(typeof buttons[k] != "function"){
3005                 if(b[k]){
3006                     buttons[k].show();
3007                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3008                     width += buttons[k].el.getWidth()+15;
3009                 }else{
3010                     buttons[k].hide();
3011                 }
3012             }
3013         }
3014         return width;
3015     };
3016
3017     // private
3018     var handleEsc = function(d, k, e){
3019         if(opt && opt.closable !== false){
3020             dlg.hide();
3021         }
3022         if(e){
3023             e.stopEvent();
3024         }
3025     };
3026
3027     return {
3028         /**
3029          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3030          * @return {Roo.BasicDialog} The BasicDialog element
3031          */
3032         getDialog : function(){
3033            if(!dlg){
3034                 dlg = new Roo.bootstrap.Modal( {
3035                     //draggable: true,
3036                     //resizable:false,
3037                     //constraintoviewport:false,
3038                     //fixedcenter:true,
3039                     //collapsible : false,
3040                     //shim:true,
3041                     //modal: true,
3042                   //  width:400,
3043                   //  height:100,
3044                     //buttonAlign:"center",
3045                     closeClick : function(){
3046                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3047                             handleButton("no");
3048                         }else{
3049                             handleButton("cancel");
3050                         }
3051                     }
3052                 });
3053                 dlg.render();
3054                 dlg.on("hide", handleHide);
3055                 mask = dlg.mask;
3056                 //dlg.addKeyListener(27, handleEsc);
3057                 buttons = {};
3058                 this.buttons = buttons;
3059                 var bt = this.buttonText;
3060                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3061                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3062                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3063                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3064                 //Roo.log(buttons);
3065                 bodyEl = dlg.bodyEl.createChild({
3066
3067                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3068                         '<textarea class="roo-mb-textarea"></textarea>' +
3069                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3070                 });
3071                 msgEl = bodyEl.dom.firstChild;
3072                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3073                 textboxEl.enableDisplayMode();
3074                 textboxEl.addKeyListener([10,13], function(){
3075                     if(dlg.isVisible() && opt && opt.buttons){
3076                         if(opt.buttons.ok){
3077                             handleButton("ok");
3078                         }else if(opt.buttons.yes){
3079                             handleButton("yes");
3080                         }
3081                     }
3082                 });
3083                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3084                 textareaEl.enableDisplayMode();
3085                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3086                 progressEl.enableDisplayMode();
3087                 
3088                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3089                 //var pf = progressEl.dom.firstChild;
3090                 //if (pf) {
3091                     //pp = Roo.get(pf.firstChild);
3092                     //pp.setHeight(pf.offsetHeight);
3093                 //}
3094                 
3095             }
3096             return dlg;
3097         },
3098
3099         /**
3100          * Updates the message box body text
3101          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3102          * the XHTML-compliant non-breaking space character '&amp;#160;')
3103          * @return {Roo.MessageBox} This message box
3104          */
3105         updateText : function(text)
3106         {
3107             if(!dlg.isVisible() && !opt.width){
3108                 dlg.dialogEl.setWidth(this.maxWidth);
3109                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3110             }
3111             msgEl.innerHTML = text || '&#160;';
3112       
3113             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3114             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3115             var w = Math.max(
3116                     Math.min(opt.width || cw , this.maxWidth), 
3117                     Math.max(opt.minWidth || this.minWidth, bwidth)
3118             );
3119             if(opt.prompt){
3120                 activeTextEl.setWidth(w);
3121             }
3122             if(dlg.isVisible()){
3123                 dlg.fixedcenter = false;
3124             }
3125             // to big, make it scroll. = But as usual stupid IE does not support
3126             // !important..
3127             
3128             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3129                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3130                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3131             } else {
3132                 bodyEl.dom.style.height = '';
3133                 bodyEl.dom.style.overflowY = '';
3134             }
3135             if (cw > w) {
3136                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3137             } else {
3138                 bodyEl.dom.style.overflowX = '';
3139             }
3140             
3141             dlg.setContentSize(w, bodyEl.getHeight());
3142             if(dlg.isVisible()){
3143                 dlg.fixedcenter = true;
3144             }
3145             return this;
3146         },
3147
3148         /**
3149          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3150          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3151          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3152          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3153          * @return {Roo.MessageBox} This message box
3154          */
3155         updateProgress : function(value, text){
3156             if(text){
3157                 this.updateText(text);
3158             }
3159             if (pp) { // weird bug on my firefox - for some reason this is not defined
3160                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3161             }
3162             return this;
3163         },        
3164
3165         /**
3166          * Returns true if the message box is currently displayed
3167          * @return {Boolean} True if the message box is visible, else false
3168          */
3169         isVisible : function(){
3170             return dlg && dlg.isVisible();  
3171         },
3172
3173         /**
3174          * Hides the message box if it is displayed
3175          */
3176         hide : function(){
3177             if(this.isVisible()){
3178                 dlg.hide();
3179             }  
3180         },
3181
3182         /**
3183          * Displays a new message box, or reinitializes an existing message box, based on the config options
3184          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3185          * The following config object properties are supported:
3186          * <pre>
3187 Property    Type             Description
3188 ----------  ---------------  ------------------------------------------------------------------------------------
3189 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3190                                    closes (defaults to undefined)
3191 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3192                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3193 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3194                                    progress and wait dialogs will ignore this property and always hide the
3195                                    close button as they can only be closed programmatically.
3196 cls               String           A custom CSS class to apply to the message box element
3197 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3198                                    displayed (defaults to 75)
3199 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3200                                    function will be btn (the name of the button that was clicked, if applicable,
3201                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3202                                    Progress and wait dialogs will ignore this option since they do not respond to
3203                                    user actions and can only be closed programmatically, so any required function
3204                                    should be called by the same code after it closes the dialog.
3205 icon              String           A CSS class that provides a background image to be used as an icon for
3206                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3207 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3208 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3209 modal             Boolean          False to allow user interaction with the page while the message box is
3210                                    displayed (defaults to true)
3211 msg               String           A string that will replace the existing message box body text (defaults
3212                                    to the XHTML-compliant non-breaking space character '&#160;')
3213 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3214 progress          Boolean          True to display a progress bar (defaults to false)
3215 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3216 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3217 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3218 title             String           The title text
3219 value             String           The string value to set into the active textbox element if displayed
3220 wait              Boolean          True to display a progress bar (defaults to false)
3221 width             Number           The width of the dialog in pixels
3222 </pre>
3223          *
3224          * Example usage:
3225          * <pre><code>
3226 Roo.Msg.show({
3227    title: 'Address',
3228    msg: 'Please enter your address:',
3229    width: 300,
3230    buttons: Roo.MessageBox.OKCANCEL,
3231    multiline: true,
3232    fn: saveAddress,
3233    animEl: 'addAddressBtn'
3234 });
3235 </code></pre>
3236          * @param {Object} config Configuration options
3237          * @return {Roo.MessageBox} This message box
3238          */
3239         show : function(options)
3240         {
3241             
3242             // this causes nightmares if you show one dialog after another
3243             // especially on callbacks..
3244              
3245             if(this.isVisible()){
3246                 
3247                 this.hide();
3248                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3249                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3250                 Roo.log("New Dialog Message:" +  options.msg )
3251                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3252                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3253                 
3254             }
3255             var d = this.getDialog();
3256             opt = options;
3257             d.setTitle(opt.title || "&#160;");
3258             d.closeEl.setDisplayed(opt.closable !== false);
3259             activeTextEl = textboxEl;
3260             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3261             if(opt.prompt){
3262                 if(opt.multiline){
3263                     textboxEl.hide();
3264                     textareaEl.show();
3265                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3266                         opt.multiline : this.defaultTextHeight);
3267                     activeTextEl = textareaEl;
3268                 }else{
3269                     textboxEl.show();
3270                     textareaEl.hide();
3271                 }
3272             }else{
3273                 textboxEl.hide();
3274                 textareaEl.hide();
3275             }
3276             progressEl.setDisplayed(opt.progress === true);
3277             this.updateProgress(0);
3278             activeTextEl.dom.value = opt.value || "";
3279             if(opt.prompt){
3280                 dlg.setDefaultButton(activeTextEl);
3281             }else{
3282                 var bs = opt.buttons;
3283                 var db = null;
3284                 if(bs && bs.ok){
3285                     db = buttons["ok"];
3286                 }else if(bs && bs.yes){
3287                     db = buttons["yes"];
3288                 }
3289                 dlg.setDefaultButton(db);
3290             }
3291             bwidth = updateButtons(opt.buttons);
3292             this.updateText(opt.msg);
3293             if(opt.cls){
3294                 d.el.addClass(opt.cls);
3295             }
3296             d.proxyDrag = opt.proxyDrag === true;
3297             d.modal = opt.modal !== false;
3298             d.mask = opt.modal !== false ? mask : false;
3299             if(!d.isVisible()){
3300                 // force it to the end of the z-index stack so it gets a cursor in FF
3301                 document.body.appendChild(dlg.el.dom);
3302                 d.animateTarget = null;
3303                 d.show(options.animEl);
3304             }
3305             return this;
3306         },
3307
3308         /**
3309          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3310          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3311          * and closing the message box when the process is complete.
3312          * @param {String} title The title bar text
3313          * @param {String} msg The message box body text
3314          * @return {Roo.MessageBox} This message box
3315          */
3316         progress : function(title, msg){
3317             this.show({
3318                 title : title,
3319                 msg : msg,
3320                 buttons: false,
3321                 progress:true,
3322                 closable:false,
3323                 minWidth: this.minProgressWidth,
3324                 modal : true
3325             });
3326             return this;
3327         },
3328
3329         /**
3330          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3331          * If a callback function is passed it will be called after the user clicks the button, and the
3332          * id of the button that was clicked will be passed as the only parameter to the callback
3333          * (could also be the top-right close button).
3334          * @param {String} title The title bar text
3335          * @param {String} msg The message box body text
3336          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3337          * @param {Object} scope (optional) The scope of the callback function
3338          * @return {Roo.MessageBox} This message box
3339          */
3340         alert : function(title, msg, fn, scope)
3341         {
3342             this.show({
3343                 title : title,
3344                 msg : msg,
3345                 buttons: this.OK,
3346                 fn: fn,
3347                 closable : false,
3348                 scope : scope,
3349                 modal : true
3350             });
3351             return this;
3352         },
3353
3354         /**
3355          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3356          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3357          * You are responsible for closing the message box when the process is complete.
3358          * @param {String} msg The message box body text
3359          * @param {String} title (optional) The title bar text
3360          * @return {Roo.MessageBox} This message box
3361          */
3362         wait : function(msg, title){
3363             this.show({
3364                 title : title,
3365                 msg : msg,
3366                 buttons: false,
3367                 closable:false,
3368                 progress:true,
3369                 modal:true,
3370                 width:300,
3371                 wait:true
3372             });
3373             waitTimer = Roo.TaskMgr.start({
3374                 run: function(i){
3375                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3376                 },
3377                 interval: 1000
3378             });
3379             return this;
3380         },
3381
3382         /**
3383          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3384          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3385          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3386          * @param {String} title The title bar text
3387          * @param {String} msg The message box body text
3388          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3389          * @param {Object} scope (optional) The scope of the callback function
3390          * @return {Roo.MessageBox} This message box
3391          */
3392         confirm : function(title, msg, fn, scope){
3393             this.show({
3394                 title : title,
3395                 msg : msg,
3396                 buttons: this.YESNO,
3397                 fn: fn,
3398                 scope : scope,
3399                 modal : true
3400             });
3401             return this;
3402         },
3403
3404         /**
3405          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3406          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3407          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3408          * (could also be the top-right close button) and the text that was entered will be passed as the two
3409          * parameters to the callback.
3410          * @param {String} title The title bar text
3411          * @param {String} msg The message box body text
3412          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3413          * @param {Object} scope (optional) The scope of the callback function
3414          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3415          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3416          * @return {Roo.MessageBox} This message box
3417          */
3418         prompt : function(title, msg, fn, scope, multiline){
3419             this.show({
3420                 title : title,
3421                 msg : msg,
3422                 buttons: this.OKCANCEL,
3423                 fn: fn,
3424                 minWidth:250,
3425                 scope : scope,
3426                 prompt:true,
3427                 multiline: multiline,
3428                 modal : true
3429             });
3430             return this;
3431         },
3432
3433         /**
3434          * Button config that displays a single OK button
3435          * @type Object
3436          */
3437         OK : {ok:true},
3438         /**
3439          * Button config that displays Yes and No buttons
3440          * @type Object
3441          */
3442         YESNO : {yes:true, no:true},
3443         /**
3444          * Button config that displays OK and Cancel buttons
3445          * @type Object
3446          */
3447         OKCANCEL : {ok:true, cancel:true},
3448         /**
3449          * Button config that displays Yes, No and Cancel buttons
3450          * @type Object
3451          */
3452         YESNOCANCEL : {yes:true, no:true, cancel:true},
3453
3454         /**
3455          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3456          * @type Number
3457          */
3458         defaultTextHeight : 75,
3459         /**
3460          * The maximum width in pixels of the message box (defaults to 600)
3461          * @type Number
3462          */
3463         maxWidth : 600,
3464         /**
3465          * The minimum width in pixels of the message box (defaults to 100)
3466          * @type Number
3467          */
3468         minWidth : 100,
3469         /**
3470          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3471          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3472          * @type Number
3473          */
3474         minProgressWidth : 250,
3475         /**
3476          * An object containing the default button text strings that can be overriden for localized language support.
3477          * Supported properties are: ok, cancel, yes and no.
3478          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3479          * @type Object
3480          */
3481         buttonText : {
3482             ok : "OK",
3483             cancel : "Cancel",
3484             yes : "Yes",
3485             no : "No"
3486         }
3487     };
3488 }();
3489
3490 /**
3491  * Shorthand for {@link Roo.MessageBox}
3492  */
3493 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3494 Roo.Msg = Roo.Msg || Roo.MessageBox;
3495 /*
3496  * - LGPL
3497  *
3498  * navbar
3499  * 
3500  */
3501
3502 /**
3503  * @class Roo.bootstrap.Navbar
3504  * @extends Roo.bootstrap.Component
3505  * Bootstrap Navbar class
3506
3507  * @constructor
3508  * Create a new Navbar
3509  * @param {Object} config The config object
3510  */
3511
3512
3513 Roo.bootstrap.Navbar = function(config){
3514     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3515     this.addEvents({
3516         // raw events
3517         /**
3518          * @event beforetoggle
3519          * Fire before toggle the menu
3520          * @param {Roo.EventObject} e
3521          */
3522         "beforetoggle" : true
3523     });
3524 };
3525
3526 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3527     
3528     
3529    
3530     // private
3531     navItems : false,
3532     loadMask : false,
3533     
3534     
3535     getAutoCreate : function(){
3536         
3537         
3538         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3539         
3540     },
3541     
3542     initEvents :function ()
3543     {
3544         //Roo.log(this.el.select('.navbar-toggle',true));
3545         this.el.select('.navbar-toggle',true).on('click', function() {
3546             if(this.fireEvent('beforetoggle', this) !== false){
3547                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3548             }
3549             
3550         }, this);
3551         
3552         var mark = {
3553             tag: "div",
3554             cls:"x-dlg-mask"
3555         };
3556         
3557         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3558         
3559         var size = this.el.getSize();
3560         this.maskEl.setSize(size.width, size.height);
3561         this.maskEl.enableDisplayMode("block");
3562         this.maskEl.hide();
3563         
3564         if(this.loadMask){
3565             this.maskEl.show();
3566         }
3567     },
3568     
3569     
3570     getChildContainer : function()
3571     {
3572         if (this.el.select('.collapse').getCount()) {
3573             return this.el.select('.collapse',true).first();
3574         }
3575         
3576         return this.el;
3577     },
3578     
3579     mask : function()
3580     {
3581         this.maskEl.show();
3582     },
3583     
3584     unmask : function()
3585     {
3586         this.maskEl.hide();
3587     } 
3588     
3589     
3590     
3591     
3592 });
3593
3594
3595
3596  
3597
3598  /*
3599  * - LGPL
3600  *
3601  * navbar
3602  * 
3603  */
3604
3605 /**
3606  * @class Roo.bootstrap.NavSimplebar
3607  * @extends Roo.bootstrap.Navbar
3608  * Bootstrap Sidebar class
3609  *
3610  * @cfg {Boolean} inverse is inverted color
3611  * 
3612  * @cfg {String} type (nav | pills | tabs)
3613  * @cfg {Boolean} arrangement stacked | justified
3614  * @cfg {String} align (left | right) alignment
3615  * 
3616  * @cfg {Boolean} main (true|false) main nav bar? default false
3617  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3618  * 
3619  * @cfg {String} tag (header|footer|nav|div) default is nav 
3620
3621  * 
3622  * 
3623  * 
3624  * @constructor
3625  * Create a new Sidebar
3626  * @param {Object} config The config object
3627  */
3628
3629
3630 Roo.bootstrap.NavSimplebar = function(config){
3631     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3632 };
3633
3634 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3635     
3636     inverse: false,
3637     
3638     type: false,
3639     arrangement: '',
3640     align : false,
3641     
3642     
3643     
3644     main : false,
3645     
3646     
3647     tag : false,
3648     
3649     
3650     getAutoCreate : function(){
3651         
3652         
3653         var cfg = {
3654             tag : this.tag || 'div',
3655             cls : 'navbar'
3656         };
3657           
3658         
3659         cfg.cn = [
3660             {
3661                 cls: 'nav',
3662                 tag : 'ul'
3663             }
3664         ];
3665         
3666          
3667         this.type = this.type || 'nav';
3668         if (['tabs','pills'].indexOf(this.type)!==-1) {
3669             cfg.cn[0].cls += ' nav-' + this.type
3670         
3671         
3672         } else {
3673             if (this.type!=='nav') {
3674                 Roo.log('nav type must be nav/tabs/pills')
3675             }
3676             cfg.cn[0].cls += ' navbar-nav'
3677         }
3678         
3679         
3680         
3681         
3682         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3683             cfg.cn[0].cls += ' nav-' + this.arrangement;
3684         }
3685         
3686         
3687         if (this.align === 'right') {
3688             cfg.cn[0].cls += ' navbar-right';
3689         }
3690         
3691         if (this.inverse) {
3692             cfg.cls += ' navbar-inverse';
3693             
3694         }
3695         
3696         
3697         return cfg;
3698     
3699         
3700     }
3701     
3702     
3703     
3704 });
3705
3706
3707
3708  
3709
3710  
3711        /*
3712  * - LGPL
3713  *
3714  * navbar
3715  * 
3716  */
3717
3718 /**
3719  * @class Roo.bootstrap.NavHeaderbar
3720  * @extends Roo.bootstrap.NavSimplebar
3721  * Bootstrap Sidebar class
3722  *
3723  * @cfg {String} brand what is brand
3724  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3725  * @cfg {String} brand_href href of the brand
3726  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3727  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3728  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3729  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3730  * 
3731  * @constructor
3732  * Create a new Sidebar
3733  * @param {Object} config The config object
3734  */
3735
3736
3737 Roo.bootstrap.NavHeaderbar = function(config){
3738     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3739       
3740 };
3741
3742 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3743     
3744     position: '',
3745     brand: '',
3746     brand_href: false,
3747     srButton : true,
3748     autohide : false,
3749     desktopCenter : false,
3750    
3751     
3752     getAutoCreate : function(){
3753         
3754         var   cfg = {
3755             tag: this.nav || 'nav',
3756             cls: 'navbar',
3757             role: 'navigation',
3758             cn: []
3759         };
3760         
3761         var cn = cfg.cn;
3762         if (this.desktopCenter) {
3763             cn.push({cls : 'container', cn : []});
3764             cn = cn[0].cn;
3765         }
3766         
3767         if(this.srButton){
3768             cn.push({
3769                 tag: 'div',
3770                 cls: 'navbar-header',
3771                 cn: [
3772                     {
3773                         tag: 'button',
3774                         type: 'button',
3775                         cls: 'navbar-toggle',
3776                         'data-toggle': 'collapse',
3777                         cn: [
3778                             {
3779                                 tag: 'span',
3780                                 cls: 'sr-only',
3781                                 html: 'Toggle navigation'
3782                             },
3783                             {
3784                                 tag: 'span',
3785                                 cls: 'icon-bar'
3786                             },
3787                             {
3788                                 tag: 'span',
3789                                 cls: 'icon-bar'
3790                             },
3791                             {
3792                                 tag: 'span',
3793                                 cls: 'icon-bar'
3794                             }
3795                         ]
3796                     }
3797                 ]
3798             });
3799         }
3800         
3801         cn.push({
3802             tag: 'div',
3803             cls: 'collapse navbar-collapse',
3804             cn : []
3805         });
3806         
3807         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3808         
3809         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3810             cfg.cls += ' navbar-' + this.position;
3811             
3812             // tag can override this..
3813             
3814             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3815         }
3816         
3817         if (this.brand !== '') {
3818             cn[0].cn.push({
3819                 tag: 'a',
3820                 href: this.brand_href ? this.brand_href : '#',
3821                 cls: 'navbar-brand',
3822                 cn: [
3823                 this.brand
3824                 ]
3825             });
3826         }
3827         
3828         if(this.main){
3829             cfg.cls += ' main-nav';
3830         }
3831         
3832         
3833         return cfg;
3834
3835         
3836     },
3837     getHeaderChildContainer : function()
3838     {
3839         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3840             return this.el.select('.navbar-header',true).first();
3841         }
3842         
3843         return this.getChildContainer();
3844     },
3845     
3846     
3847     initEvents : function()
3848     {
3849         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3850         
3851         if (this.autohide) {
3852             
3853             var prevScroll = 0;
3854             var ft = this.el;
3855             
3856             Roo.get(document).on('scroll',function(e) {
3857                 var ns = Roo.get(document).getScroll().top;
3858                 var os = prevScroll;
3859                 prevScroll = ns;
3860                 
3861                 if(ns > os){
3862                     ft.removeClass('slideDown');
3863                     ft.addClass('slideUp');
3864                     return;
3865                 }
3866                 ft.removeClass('slideUp');
3867                 ft.addClass('slideDown');
3868                  
3869               
3870           },this);
3871         }
3872     }    
3873     
3874 });
3875
3876
3877
3878  
3879
3880  /*
3881  * - LGPL
3882  *
3883  * navbar
3884  * 
3885  */
3886
3887 /**
3888  * @class Roo.bootstrap.NavSidebar
3889  * @extends Roo.bootstrap.Navbar
3890  * Bootstrap Sidebar class
3891  * 
3892  * @constructor
3893  * Create a new Sidebar
3894  * @param {Object} config The config object
3895  */
3896
3897
3898 Roo.bootstrap.NavSidebar = function(config){
3899     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3900 };
3901
3902 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3903     
3904     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3905     
3906     getAutoCreate : function(){
3907         
3908         
3909         return  {
3910             tag: 'div',
3911             cls: 'sidebar sidebar-nav'
3912         };
3913     
3914         
3915     }
3916     
3917     
3918     
3919 });
3920
3921
3922
3923  
3924
3925  /*
3926  * - LGPL
3927  *
3928  * nav group
3929  * 
3930  */
3931
3932 /**
3933  * @class Roo.bootstrap.NavGroup
3934  * @extends Roo.bootstrap.Component
3935  * Bootstrap NavGroup class
3936  * @cfg {String} align (left|right)
3937  * @cfg {Boolean} inverse
3938  * @cfg {String} type (nav|pills|tab) default nav
3939  * @cfg {String} navId - reference Id for navbar.
3940
3941  * 
3942  * @constructor
3943  * Create a new nav group
3944  * @param {Object} config The config object
3945  */
3946
3947 Roo.bootstrap.NavGroup = function(config){
3948     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3949     this.navItems = [];
3950    
3951     Roo.bootstrap.NavGroup.register(this);
3952      this.addEvents({
3953         /**
3954              * @event changed
3955              * Fires when the active item changes
3956              * @param {Roo.bootstrap.NavGroup} this
3957              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3958              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3959          */
3960         'changed': true
3961      });
3962     
3963 };
3964
3965 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3966     
3967     align: '',
3968     inverse: false,
3969     form: false,
3970     type: 'nav',
3971     navId : '',
3972     // private
3973     
3974     navItems : false, 
3975     
3976     getAutoCreate : function()
3977     {
3978         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3979         
3980         cfg = {
3981             tag : 'ul',
3982             cls: 'nav' 
3983         };
3984         
3985         if (['tabs','pills'].indexOf(this.type)!==-1) {
3986             cfg.cls += ' nav-' + this.type
3987         } else {
3988             if (this.type!=='nav') {
3989                 Roo.log('nav type must be nav/tabs/pills')
3990             }
3991             cfg.cls += ' navbar-nav'
3992         }
3993         
3994         if (this.parent().sidebar) {
3995             cfg = {
3996                 tag: 'ul',
3997                 cls: 'dashboard-menu sidebar-menu'
3998             };
3999             
4000             return cfg;
4001         }
4002         
4003         if (this.form === true) {
4004             cfg = {
4005                 tag: 'form',
4006                 cls: 'navbar-form'
4007             };
4008             
4009             if (this.align === 'right') {
4010                 cfg.cls += ' navbar-right';
4011             } else {
4012                 cfg.cls += ' navbar-left';
4013             }
4014         }
4015         
4016         if (this.align === 'right') {
4017             cfg.cls += ' navbar-right';
4018         }
4019         
4020         if (this.inverse) {
4021             cfg.cls += ' navbar-inverse';
4022             
4023         }
4024         
4025         
4026         return cfg;
4027     },
4028     /**
4029     * sets the active Navigation item
4030     * @param {Roo.bootstrap.NavItem} the new current navitem
4031     */
4032     setActiveItem : function(item)
4033     {
4034         var prev = false;
4035         Roo.each(this.navItems, function(v){
4036             if (v == item) {
4037                 return ;
4038             }
4039             if (v.isActive()) {
4040                 v.setActive(false, true);
4041                 prev = v;
4042                 
4043             }
4044             
4045         });
4046
4047         item.setActive(true, true);
4048         this.fireEvent('changed', this, item, prev);
4049         
4050         
4051     },
4052     /**
4053     * gets the active Navigation item
4054     * @return {Roo.bootstrap.NavItem} the current navitem
4055     */
4056     getActive : function()
4057     {
4058         
4059         var prev = false;
4060         Roo.each(this.navItems, function(v){
4061             
4062             if (v.isActive()) {
4063                 prev = v;
4064                 
4065             }
4066             
4067         });
4068         return prev;
4069     },
4070     
4071     indexOfNav : function()
4072     {
4073         
4074         var prev = false;
4075         Roo.each(this.navItems, function(v,i){
4076             
4077             if (v.isActive()) {
4078                 prev = i;
4079                 
4080             }
4081             
4082         });
4083         return prev;
4084     },
4085     /**
4086     * adds a Navigation item
4087     * @param {Roo.bootstrap.NavItem} the navitem to add
4088     */
4089     addItem : function(cfg)
4090     {
4091         var cn = new Roo.bootstrap.NavItem(cfg);
4092         this.register(cn);
4093         cn.parentId = this.id;
4094         cn.onRender(this.el, null);
4095         return cn;
4096     },
4097     /**
4098     * register a Navigation item
4099     * @param {Roo.bootstrap.NavItem} the navitem to add
4100     */
4101     register : function(item)
4102     {
4103         this.navItems.push( item);
4104         item.navId = this.navId;
4105     
4106     },
4107     
4108     /**
4109     * clear all the Navigation item
4110     */
4111    
4112     clearAll : function()
4113     {
4114         this.navItems = [];
4115         this.el.dom.innerHTML = '';
4116     },
4117     
4118     getNavItem: function(tabId)
4119     {
4120         var ret = false;
4121         Roo.each(this.navItems, function(e) {
4122             if (e.tabId == tabId) {
4123                ret =  e;
4124                return false;
4125             }
4126             return true;
4127             
4128         });
4129         return ret;
4130     },
4131     
4132     setActiveNext : function()
4133     {
4134         var i = this.indexOfNav(this.getActive());
4135         if (i > this.navItems.length) {
4136             return;
4137         }
4138         this.setActiveItem(this.navItems[i+1]);
4139     },
4140     setActivePrev : function()
4141     {
4142         var i = this.indexOfNav(this.getActive());
4143         if (i  < 1) {
4144             return;
4145         }
4146         this.setActiveItem(this.navItems[i-1]);
4147     },
4148     clearWasActive : function(except) {
4149         Roo.each(this.navItems, function(e) {
4150             if (e.tabId != except.tabId && e.was_active) {
4151                e.was_active = false;
4152                return false;
4153             }
4154             return true;
4155             
4156         });
4157     },
4158     getWasActive : function ()
4159     {
4160         var r = false;
4161         Roo.each(this.navItems, function(e) {
4162             if (e.was_active) {
4163                r = e;
4164                return false;
4165             }
4166             return true;
4167             
4168         });
4169         return r;
4170     }
4171     
4172     
4173 });
4174
4175  
4176 Roo.apply(Roo.bootstrap.NavGroup, {
4177     
4178     groups: {},
4179      /**
4180     * register a Navigation Group
4181     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4182     */
4183     register : function(navgrp)
4184     {
4185         this.groups[navgrp.navId] = navgrp;
4186         
4187     },
4188     /**
4189     * fetch a Navigation Group based on the navigation ID
4190     * @param {string} the navgroup to add
4191     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4192     */
4193     get: function(navId) {
4194         if (typeof(this.groups[navId]) == 'undefined') {
4195             return false;
4196             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4197         }
4198         return this.groups[navId] ;
4199     }
4200     
4201     
4202     
4203 });
4204
4205  /*
4206  * - LGPL
4207  *
4208  * row
4209  * 
4210  */
4211
4212 /**
4213  * @class Roo.bootstrap.NavItem
4214  * @extends Roo.bootstrap.Component
4215  * Bootstrap Navbar.NavItem class
4216  * @cfg {String} href  link to
4217  * @cfg {String} html content of button
4218  * @cfg {String} badge text inside badge
4219  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4220  * @cfg {String} glyphicon name of glyphicon
4221  * @cfg {String} icon name of font awesome icon
4222  * @cfg {Boolean} active Is item active
4223  * @cfg {Boolean} disabled Is item disabled
4224  
4225  * @cfg {Boolean} preventDefault (true | false) default false
4226  * @cfg {String} tabId the tab that this item activates.
4227  * @cfg {String} tagtype (a|span) render as a href or span?
4228  * @cfg {Boolean} animateRef (true|false) link to element default false  
4229   
4230  * @constructor
4231  * Create a new Navbar Item
4232  * @param {Object} config The config object
4233  */
4234 Roo.bootstrap.NavItem = function(config){
4235     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4236     this.addEvents({
4237         // raw events
4238         /**
4239          * @event click
4240          * The raw click event for the entire grid.
4241          * @param {Roo.EventObject} e
4242          */
4243         "click" : true,
4244          /**
4245             * @event changed
4246             * Fires when the active item active state changes
4247             * @param {Roo.bootstrap.NavItem} this
4248             * @param {boolean} state the new state
4249              
4250          */
4251         'changed': true,
4252         /**
4253             * @event scrollto
4254             * Fires when scroll to element
4255             * @param {Roo.bootstrap.NavItem} this
4256             * @param {Object} options
4257             * @param {Roo.EventObject} e
4258              
4259          */
4260         'scrollto': true
4261     });
4262    
4263 };
4264
4265 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4266     
4267     href: false,
4268     html: '',
4269     badge: '',
4270     icon: false,
4271     glyphicon: false,
4272     active: false,
4273     preventDefault : false,
4274     tabId : false,
4275     tagtype : 'a',
4276     disabled : false,
4277     animateRef : false,
4278     was_active : false,
4279     
4280     getAutoCreate : function(){
4281          
4282         var cfg = {
4283             tag: 'li',
4284             cls: 'nav-item'
4285             
4286         };
4287         
4288         if (this.active) {
4289             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4290         }
4291         if (this.disabled) {
4292             cfg.cls += ' disabled';
4293         }
4294         
4295         if (this.href || this.html || this.glyphicon || this.icon) {
4296             cfg.cn = [
4297                 {
4298                     tag: this.tagtype,
4299                     href : this.href || "#",
4300                     html: this.html || ''
4301                 }
4302             ];
4303             
4304             if (this.icon) {
4305                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4306             }
4307
4308             if(this.glyphicon) {
4309                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4310             }
4311             
4312             if (this.menu) {
4313                 
4314                 cfg.cn[0].html += " <span class='caret'></span>";
4315              
4316             }
4317             
4318             if (this.badge !== '') {
4319                  
4320                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4321             }
4322         }
4323         
4324         
4325         
4326         return cfg;
4327     },
4328     initEvents: function() 
4329     {
4330         if (typeof (this.menu) != 'undefined') {
4331             this.menu.parentType = this.xtype;
4332             this.menu.triggerEl = this.el;
4333             this.menu = this.addxtype(Roo.apply({}, this.menu));
4334         }
4335         
4336         this.el.select('a',true).on('click', this.onClick, this);
4337         
4338         if(this.tagtype == 'span'){
4339             this.el.select('span',true).on('click', this.onClick, this);
4340         }
4341        
4342         // at this point parent should be available..
4343         this.parent().register(this);
4344     },
4345     
4346     onClick : function(e)
4347     {
4348         if (e.getTarget('.dropdown-menu-item')) {
4349             // did you click on a menu itemm.... - then don't trigger onclick..
4350             return;
4351         }
4352         
4353         if(
4354                 this.preventDefault || 
4355                 this.href == '#' 
4356         ){
4357             Roo.log("NavItem - prevent Default?");
4358             e.preventDefault();
4359         }
4360         
4361         if (this.disabled) {
4362             return;
4363         }
4364         
4365         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4366         if (tg && tg.transition) {
4367             Roo.log("waiting for the transitionend");
4368             return;
4369         }
4370         
4371         
4372         
4373         //Roo.log("fire event clicked");
4374         if(this.fireEvent('click', this, e) === false){
4375             return;
4376         };
4377         
4378         if(this.tagtype == 'span'){
4379             return;
4380         }
4381         
4382         //Roo.log(this.href);
4383         var ael = this.el.select('a',true).first();
4384         //Roo.log(ael);
4385         
4386         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4387             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4388             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4389                 return; // ignore... - it's a 'hash' to another page.
4390             }
4391             Roo.log("NavItem - prevent Default?");
4392             e.preventDefault();
4393             this.scrollToElement(e);
4394         }
4395         
4396         
4397         var p =  this.parent();
4398    
4399         if (['tabs','pills'].indexOf(p.type)!==-1) {
4400             if (typeof(p.setActiveItem) !== 'undefined') {
4401                 p.setActiveItem(this);
4402             }
4403         }
4404         
4405         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4406         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4407             // remove the collapsed menu expand...
4408             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4409         }
4410     },
4411     
4412     isActive: function () {
4413         return this.active
4414     },
4415     setActive : function(state, fire, is_was_active)
4416     {
4417         if (this.active && !state && this.navId) {
4418             this.was_active = true;
4419             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4420             if (nv) {
4421                 nv.clearWasActive(this);
4422             }
4423             
4424         }
4425         this.active = state;
4426         
4427         if (!state ) {
4428             this.el.removeClass('active');
4429         } else if (!this.el.hasClass('active')) {
4430             this.el.addClass('active');
4431         }
4432         if (fire) {
4433             this.fireEvent('changed', this, state);
4434         }
4435         
4436         // show a panel if it's registered and related..
4437         
4438         if (!this.navId || !this.tabId || !state || is_was_active) {
4439             return;
4440         }
4441         
4442         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4443         if (!tg) {
4444             return;
4445         }
4446         var pan = tg.getPanelByName(this.tabId);
4447         if (!pan) {
4448             return;
4449         }
4450         // if we can not flip to new panel - go back to old nav highlight..
4451         if (false == tg.showPanel(pan)) {
4452             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4453             if (nv) {
4454                 var onav = nv.getWasActive();
4455                 if (onav) {
4456                     onav.setActive(true, false, true);
4457                 }
4458             }
4459             
4460         }
4461         
4462         
4463         
4464     },
4465      // this should not be here...
4466     setDisabled : function(state)
4467     {
4468         this.disabled = state;
4469         if (!state ) {
4470             this.el.removeClass('disabled');
4471         } else if (!this.el.hasClass('disabled')) {
4472             this.el.addClass('disabled');
4473         }
4474         
4475     },
4476     
4477     /**
4478      * Fetch the element to display the tooltip on.
4479      * @return {Roo.Element} defaults to this.el
4480      */
4481     tooltipEl : function()
4482     {
4483         return this.el.select('' + this.tagtype + '', true).first();
4484     },
4485     
4486     scrollToElement : function(e)
4487     {
4488         var c = document.body;
4489         
4490         /*
4491          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4492          */
4493         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4494             c = document.documentElement;
4495         }
4496         
4497         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4498         
4499         if(!target){
4500             return;
4501         }
4502
4503         var o = target.calcOffsetsTo(c);
4504         
4505         var options = {
4506             target : target,
4507             value : o[1]
4508         };
4509         
4510         this.fireEvent('scrollto', this, options, e);
4511         
4512         Roo.get(c).scrollTo('top', options.value, true);
4513         
4514         return;
4515     }
4516 });
4517  
4518
4519  /*
4520  * - LGPL
4521  *
4522  * sidebar item
4523  *
4524  *  li
4525  *    <span> icon </span>
4526  *    <span> text </span>
4527  *    <span>badge </span>
4528  */
4529
4530 /**
4531  * @class Roo.bootstrap.NavSidebarItem
4532  * @extends Roo.bootstrap.NavItem
4533  * Bootstrap Navbar.NavSidebarItem class
4534  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4535  * {bool} open is the menu open
4536  * @constructor
4537  * Create a new Navbar Button
4538  * @param {Object} config The config object
4539  */
4540 Roo.bootstrap.NavSidebarItem = function(config){
4541     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4542     this.addEvents({
4543         // raw events
4544         /**
4545          * @event click
4546          * The raw click event for the entire grid.
4547          * @param {Roo.EventObject} e
4548          */
4549         "click" : true,
4550          /**
4551             * @event changed
4552             * Fires when the active item active state changes
4553             * @param {Roo.bootstrap.NavSidebarItem} this
4554             * @param {boolean} state the new state
4555              
4556          */
4557         'changed': true
4558     });
4559    
4560 };
4561
4562 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4563     
4564     badgeWeight : 'default',
4565     
4566     open: false,
4567     
4568     getAutoCreate : function(){
4569         
4570         
4571         var a = {
4572                 tag: 'a',
4573                 href : this.href || '#',
4574                 cls: '',
4575                 html : '',
4576                 cn : []
4577         };
4578         var cfg = {
4579             tag: 'li',
4580             cls: '',
4581             cn: [ a ]
4582         };
4583         var span = {
4584             tag: 'span',
4585             html : this.html || ''
4586         };
4587         
4588         
4589         if (this.active) {
4590             cfg.cls += ' active';
4591         }
4592         
4593         if (this.disabled) {
4594             cfg.cls += ' disabled';
4595         }
4596         if (this.open) {
4597             cfg.cls += ' open x-open';
4598         }
4599         // left icon..
4600         if (this.glyphicon || this.icon) {
4601             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4602             a.cn.push({ tag : 'i', cls : c }) ;
4603         }
4604         // html..
4605         a.cn.push(span);
4606         // then badge..
4607         if (this.badge !== '') {
4608             
4609             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4610         }
4611         // fi
4612         if (this.menu) {
4613             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4614             a.cls += 'dropdown-toggle treeview' ;
4615         }
4616         
4617         return cfg;
4618          
4619            
4620     },
4621     
4622     initEvents : function()
4623     { 
4624         if (typeof (this.menu) != 'undefined') {
4625             this.menu.parentType = this.xtype;
4626             this.menu.triggerEl = this.el;
4627             this.menu = this.addxtype(Roo.apply({}, this.menu));
4628         }
4629         
4630         this.el.on('click', this.onClick, this);
4631        
4632     
4633         if(this.badge !== ''){
4634  
4635             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4636         }
4637         
4638     },
4639     
4640     onClick : function(e)
4641     {
4642         if(this.disabled){
4643             e.preventDefault();
4644             return;
4645         }
4646         
4647         if(this.preventDefault){
4648             e.preventDefault();
4649         }
4650         
4651         this.fireEvent('click', this);
4652     },
4653     
4654     disable : function()
4655     {
4656         this.setDisabled(true);
4657     },
4658     
4659     enable : function()
4660     {
4661         this.setDisabled(false);
4662     },
4663     
4664     setDisabled : function(state)
4665     {
4666         if(this.disabled == state){
4667             return;
4668         }
4669         
4670         this.disabled = state;
4671         
4672         if (state) {
4673             this.el.addClass('disabled');
4674             return;
4675         }
4676         
4677         this.el.removeClass('disabled');
4678         
4679         return;
4680     },
4681     
4682     setActive : function(state)
4683     {
4684         if(this.active == state){
4685             return;
4686         }
4687         
4688         this.active = state;
4689         
4690         if (state) {
4691             this.el.addClass('active');
4692             return;
4693         }
4694         
4695         this.el.removeClass('active');
4696         
4697         return;
4698     },
4699     
4700     isActive: function () 
4701     {
4702         return this.active;
4703     },
4704     
4705     setBadge : function(str)
4706     {
4707         if(!this.badgeEl){
4708             return;
4709         }
4710         
4711         this.badgeEl.dom.innerHTML = str;
4712     }
4713     
4714    
4715      
4716  
4717 });
4718  
4719
4720  /*
4721  * - LGPL
4722  *
4723  * row
4724  * 
4725  */
4726
4727 /**
4728  * @class Roo.bootstrap.Row
4729  * @extends Roo.bootstrap.Component
4730  * Bootstrap Row class (contains columns...)
4731  * 
4732  * @constructor
4733  * Create a new Row
4734  * @param {Object} config The config object
4735  */
4736
4737 Roo.bootstrap.Row = function(config){
4738     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4739 };
4740
4741 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4742     
4743     getAutoCreate : function(){
4744        return {
4745             cls: 'row clearfix'
4746        };
4747     }
4748     
4749     
4750 });
4751
4752  
4753
4754  /*
4755  * - LGPL
4756  *
4757  * element
4758  * 
4759  */
4760
4761 /**
4762  * @class Roo.bootstrap.Element
4763  * @extends Roo.bootstrap.Component
4764  * Bootstrap Element class
4765  * @cfg {String} html contents of the element
4766  * @cfg {String} tag tag of the element
4767  * @cfg {String} cls class of the element
4768  * @cfg {Boolean} preventDefault (true|false) default false
4769  * @cfg {Boolean} clickable (true|false) default false
4770  * 
4771  * @constructor
4772  * Create a new Element
4773  * @param {Object} config The config object
4774  */
4775
4776 Roo.bootstrap.Element = function(config){
4777     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4778     
4779     this.addEvents({
4780         // raw events
4781         /**
4782          * @event click
4783          * When a element is chick
4784          * @param {Roo.bootstrap.Element} this
4785          * @param {Roo.EventObject} e
4786          */
4787         "click" : true
4788     });
4789 };
4790
4791 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4792     
4793     tag: 'div',
4794     cls: '',
4795     html: '',
4796     preventDefault: false, 
4797     clickable: false,
4798     
4799     getAutoCreate : function(){
4800         
4801         var cfg = {
4802             tag: this.tag,
4803             cls: this.cls,
4804             html: this.html
4805         };
4806         
4807         return cfg;
4808     },
4809     
4810     initEvents: function() 
4811     {
4812         Roo.bootstrap.Element.superclass.initEvents.call(this);
4813         
4814         if(this.clickable){
4815             this.el.on('click', this.onClick, this);
4816         }
4817         
4818     },
4819     
4820     onClick : function(e)
4821     {
4822         if(this.preventDefault){
4823             e.preventDefault();
4824         }
4825         
4826         this.fireEvent('click', this, e);
4827     },
4828     
4829     getValue : function()
4830     {
4831         return this.el.dom.innerHTML;
4832     },
4833     
4834     setValue : function(value)
4835     {
4836         this.el.dom.innerHTML = value;
4837     }
4838    
4839 });
4840
4841  
4842
4843  /*
4844  * - LGPL
4845  *
4846  * pagination
4847  * 
4848  */
4849
4850 /**
4851  * @class Roo.bootstrap.Pagination
4852  * @extends Roo.bootstrap.Component
4853  * Bootstrap Pagination class
4854  * @cfg {String} size xs | sm | md | lg
4855  * @cfg {Boolean} inverse false | true
4856  * 
4857  * @constructor
4858  * Create a new Pagination
4859  * @param {Object} config The config object
4860  */
4861
4862 Roo.bootstrap.Pagination = function(config){
4863     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4864 };
4865
4866 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4867     
4868     cls: false,
4869     size: false,
4870     inverse: false,
4871     
4872     getAutoCreate : function(){
4873         var cfg = {
4874             tag: 'ul',
4875                 cls: 'pagination'
4876         };
4877         if (this.inverse) {
4878             cfg.cls += ' inverse';
4879         }
4880         if (this.html) {
4881             cfg.html=this.html;
4882         }
4883         if (this.cls) {
4884             cfg.cls += " " + this.cls;
4885         }
4886         return cfg;
4887     }
4888    
4889 });
4890
4891  
4892
4893  /*
4894  * - LGPL
4895  *
4896  * Pagination item
4897  * 
4898  */
4899
4900
4901 /**
4902  * @class Roo.bootstrap.PaginationItem
4903  * @extends Roo.bootstrap.Component
4904  * Bootstrap PaginationItem class
4905  * @cfg {String} html text
4906  * @cfg {String} href the link
4907  * @cfg {Boolean} preventDefault (true | false) default true
4908  * @cfg {Boolean} active (true | false) default false
4909  * @cfg {Boolean} disabled default false
4910  * 
4911  * 
4912  * @constructor
4913  * Create a new PaginationItem
4914  * @param {Object} config The config object
4915  */
4916
4917
4918 Roo.bootstrap.PaginationItem = function(config){
4919     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4920     this.addEvents({
4921         // raw events
4922         /**
4923          * @event click
4924          * The raw click event for the entire grid.
4925          * @param {Roo.EventObject} e
4926          */
4927         "click" : true
4928     });
4929 };
4930
4931 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4932     
4933     href : false,
4934     html : false,
4935     preventDefault: true,
4936     active : false,
4937     cls : false,
4938     disabled: false,
4939     
4940     getAutoCreate : function(){
4941         var cfg= {
4942             tag: 'li',
4943             cn: [
4944                 {
4945                     tag : 'a',
4946                     href : this.href ? this.href : '#',
4947                     html : this.html ? this.html : ''
4948                 }
4949             ]
4950         };
4951         
4952         if(this.cls){
4953             cfg.cls = this.cls;
4954         }
4955         
4956         if(this.disabled){
4957             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4958         }
4959         
4960         if(this.active){
4961             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4962         }
4963         
4964         return cfg;
4965     },
4966     
4967     initEvents: function() {
4968         
4969         this.el.on('click', this.onClick, this);
4970         
4971     },
4972     onClick : function(e)
4973     {
4974         Roo.log('PaginationItem on click ');
4975         if(this.preventDefault){
4976             e.preventDefault();
4977         }
4978         
4979         if(this.disabled){
4980             return;
4981         }
4982         
4983         this.fireEvent('click', this, e);
4984     }
4985    
4986 });
4987
4988  
4989
4990  /*
4991  * - LGPL
4992  *
4993  * slider
4994  * 
4995  */
4996
4997
4998 /**
4999  * @class Roo.bootstrap.Slider
5000  * @extends Roo.bootstrap.Component
5001  * Bootstrap Slider class
5002  *    
5003  * @constructor
5004  * Create a new Slider
5005  * @param {Object} config The config object
5006  */
5007
5008 Roo.bootstrap.Slider = function(config){
5009     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5010 };
5011
5012 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5013     
5014     getAutoCreate : function(){
5015         
5016         var cfg = {
5017             tag: 'div',
5018             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5019             cn: [
5020                 {
5021                     tag: 'a',
5022                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5023                 }
5024             ]
5025         };
5026         
5027         return cfg;
5028     }
5029    
5030 });
5031
5032  /*
5033  * Based on:
5034  * Ext JS Library 1.1.1
5035  * Copyright(c) 2006-2007, Ext JS, LLC.
5036  *
5037  * Originally Released Under LGPL - original licence link has changed is not relivant.
5038  *
5039  * Fork - LGPL
5040  * <script type="text/javascript">
5041  */
5042  
5043
5044 /**
5045  * @class Roo.grid.ColumnModel
5046  * @extends Roo.util.Observable
5047  * This is the default implementation of a ColumnModel used by the Grid. It defines
5048  * the columns in the grid.
5049  * <br>Usage:<br>
5050  <pre><code>
5051  var colModel = new Roo.grid.ColumnModel([
5052         {header: "Ticker", width: 60, sortable: true, locked: true},
5053         {header: "Company Name", width: 150, sortable: true},
5054         {header: "Market Cap.", width: 100, sortable: true},
5055         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5056         {header: "Employees", width: 100, sortable: true, resizable: false}
5057  ]);
5058  </code></pre>
5059  * <p>
5060  
5061  * The config options listed for this class are options which may appear in each
5062  * individual column definition.
5063  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5064  * @constructor
5065  * @param {Object} config An Array of column config objects. See this class's
5066  * config objects for details.
5067 */
5068 Roo.grid.ColumnModel = function(config){
5069         /**
5070      * The config passed into the constructor
5071      */
5072     this.config = config;
5073     this.lookup = {};
5074
5075     // if no id, create one
5076     // if the column does not have a dataIndex mapping,
5077     // map it to the order it is in the config
5078     for(var i = 0, len = config.length; i < len; i++){
5079         var c = config[i];
5080         if(typeof c.dataIndex == "undefined"){
5081             c.dataIndex = i;
5082         }
5083         if(typeof c.renderer == "string"){
5084             c.renderer = Roo.util.Format[c.renderer];
5085         }
5086         if(typeof c.id == "undefined"){
5087             c.id = Roo.id();
5088         }
5089         if(c.editor && c.editor.xtype){
5090             c.editor  = Roo.factory(c.editor, Roo.grid);
5091         }
5092         if(c.editor && c.editor.isFormField){
5093             c.editor = new Roo.grid.GridEditor(c.editor);
5094         }
5095         this.lookup[c.id] = c;
5096     }
5097
5098     /**
5099      * The width of columns which have no width specified (defaults to 100)
5100      * @type Number
5101      */
5102     this.defaultWidth = 100;
5103
5104     /**
5105      * Default sortable of columns which have no sortable specified (defaults to false)
5106      * @type Boolean
5107      */
5108     this.defaultSortable = false;
5109
5110     this.addEvents({
5111         /**
5112              * @event widthchange
5113              * Fires when the width of a column changes.
5114              * @param {ColumnModel} this
5115              * @param {Number} columnIndex The column index
5116              * @param {Number} newWidth The new width
5117              */
5118             "widthchange": true,
5119         /**
5120              * @event headerchange
5121              * Fires when the text of a header changes.
5122              * @param {ColumnModel} this
5123              * @param {Number} columnIndex The column index
5124              * @param {Number} newText The new header text
5125              */
5126             "headerchange": true,
5127         /**
5128              * @event hiddenchange
5129              * Fires when a column is hidden or "unhidden".
5130              * @param {ColumnModel} this
5131              * @param {Number} columnIndex The column index
5132              * @param {Boolean} hidden true if hidden, false otherwise
5133              */
5134             "hiddenchange": true,
5135             /**
5136          * @event columnmoved
5137          * Fires when a column is moved.
5138          * @param {ColumnModel} this
5139          * @param {Number} oldIndex
5140          * @param {Number} newIndex
5141          */
5142         "columnmoved" : true,
5143         /**
5144          * @event columlockchange
5145          * Fires when a column's locked state is changed
5146          * @param {ColumnModel} this
5147          * @param {Number} colIndex
5148          * @param {Boolean} locked true if locked
5149          */
5150         "columnlockchange" : true
5151     });
5152     Roo.grid.ColumnModel.superclass.constructor.call(this);
5153 };
5154 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5155     /**
5156      * @cfg {String} header The header text to display in the Grid view.
5157      */
5158     /**
5159      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5160      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5161      * specified, the column's index is used as an index into the Record's data Array.
5162      */
5163     /**
5164      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5165      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5166      */
5167     /**
5168      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5169      * Defaults to the value of the {@link #defaultSortable} property.
5170      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5171      */
5172     /**
5173      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5174      */
5175     /**
5176      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5177      */
5178     /**
5179      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5180      */
5181     /**
5182      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5183      */
5184     /**
5185      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5186      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5187      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5188      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5189      */
5190        /**
5191      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5192      */
5193     /**
5194      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5195      */
5196     /**
5197      * @cfg {String} cursor (Optional)
5198      */
5199     /**
5200      * @cfg {String} tooltip (Optional)
5201      */
5202     /**
5203      * @cfg {Number} xs (Optional)
5204      */
5205     /**
5206      * @cfg {Number} sm (Optional)
5207      */
5208     /**
5209      * @cfg {Number} md (Optional)
5210      */
5211     /**
5212      * @cfg {Number} lg (Optional)
5213      */
5214     /**
5215      * Returns the id of the column at the specified index.
5216      * @param {Number} index The column index
5217      * @return {String} the id
5218      */
5219     getColumnId : function(index){
5220         return this.config[index].id;
5221     },
5222
5223     /**
5224      * Returns the column for a specified id.
5225      * @param {String} id The column id
5226      * @return {Object} the column
5227      */
5228     getColumnById : function(id){
5229         return this.lookup[id];
5230     },
5231
5232     
5233     /**
5234      * Returns the column for a specified dataIndex.
5235      * @param {String} dataIndex The column dataIndex
5236      * @return {Object|Boolean} the column or false if not found
5237      */
5238     getColumnByDataIndex: function(dataIndex){
5239         var index = this.findColumnIndex(dataIndex);
5240         return index > -1 ? this.config[index] : false;
5241     },
5242     
5243     /**
5244      * Returns the index for a specified column id.
5245      * @param {String} id The column id
5246      * @return {Number} the index, or -1 if not found
5247      */
5248     getIndexById : function(id){
5249         for(var i = 0, len = this.config.length; i < len; i++){
5250             if(this.config[i].id == id){
5251                 return i;
5252             }
5253         }
5254         return -1;
5255     },
5256     
5257     /**
5258      * Returns the index for a specified column dataIndex.
5259      * @param {String} dataIndex The column dataIndex
5260      * @return {Number} the index, or -1 if not found
5261      */
5262     
5263     findColumnIndex : function(dataIndex){
5264         for(var i = 0, len = this.config.length; i < len; i++){
5265             if(this.config[i].dataIndex == dataIndex){
5266                 return i;
5267             }
5268         }
5269         return -1;
5270     },
5271     
5272     
5273     moveColumn : function(oldIndex, newIndex){
5274         var c = this.config[oldIndex];
5275         this.config.splice(oldIndex, 1);
5276         this.config.splice(newIndex, 0, c);
5277         this.dataMap = null;
5278         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5279     },
5280
5281     isLocked : function(colIndex){
5282         return this.config[colIndex].locked === true;
5283     },
5284
5285     setLocked : function(colIndex, value, suppressEvent){
5286         if(this.isLocked(colIndex) == value){
5287             return;
5288         }
5289         this.config[colIndex].locked = value;
5290         if(!suppressEvent){
5291             this.fireEvent("columnlockchange", this, colIndex, value);
5292         }
5293     },
5294
5295     getTotalLockedWidth : function(){
5296         var totalWidth = 0;
5297         for(var i = 0; i < this.config.length; i++){
5298             if(this.isLocked(i) && !this.isHidden(i)){
5299                 this.totalWidth += this.getColumnWidth(i);
5300             }
5301         }
5302         return totalWidth;
5303     },
5304
5305     getLockedCount : function(){
5306         for(var i = 0, len = this.config.length; i < len; i++){
5307             if(!this.isLocked(i)){
5308                 return i;
5309             }
5310         }
5311         
5312         return this.config.length;
5313     },
5314
5315     /**
5316      * Returns the number of columns.
5317      * @return {Number}
5318      */
5319     getColumnCount : function(visibleOnly){
5320         if(visibleOnly === true){
5321             var c = 0;
5322             for(var i = 0, len = this.config.length; i < len; i++){
5323                 if(!this.isHidden(i)){
5324                     c++;
5325                 }
5326             }
5327             return c;
5328         }
5329         return this.config.length;
5330     },
5331
5332     /**
5333      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5334      * @param {Function} fn
5335      * @param {Object} scope (optional)
5336      * @return {Array} result
5337      */
5338     getColumnsBy : function(fn, scope){
5339         var r = [];
5340         for(var i = 0, len = this.config.length; i < len; i++){
5341             var c = this.config[i];
5342             if(fn.call(scope||this, c, i) === true){
5343                 r[r.length] = c;
5344             }
5345         }
5346         return r;
5347     },
5348
5349     /**
5350      * Returns true if the specified column is sortable.
5351      * @param {Number} col The column index
5352      * @return {Boolean}
5353      */
5354     isSortable : function(col){
5355         if(typeof this.config[col].sortable == "undefined"){
5356             return this.defaultSortable;
5357         }
5358         return this.config[col].sortable;
5359     },
5360
5361     /**
5362      * Returns the rendering (formatting) function defined for the column.
5363      * @param {Number} col The column index.
5364      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5365      */
5366     getRenderer : function(col){
5367         if(!this.config[col].renderer){
5368             return Roo.grid.ColumnModel.defaultRenderer;
5369         }
5370         return this.config[col].renderer;
5371     },
5372
5373     /**
5374      * Sets the rendering (formatting) function for a column.
5375      * @param {Number} col The column index
5376      * @param {Function} fn The function to use to process the cell's raw data
5377      * to return HTML markup for the grid view. The render function is called with
5378      * the following parameters:<ul>
5379      * <li>Data value.</li>
5380      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5381      * <li>css A CSS style string to apply to the table cell.</li>
5382      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5383      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5384      * <li>Row index</li>
5385      * <li>Column index</li>
5386      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5387      */
5388     setRenderer : function(col, fn){
5389         this.config[col].renderer = fn;
5390     },
5391
5392     /**
5393      * Returns the width for the specified column.
5394      * @param {Number} col The column index
5395      * @return {Number}
5396      */
5397     getColumnWidth : function(col){
5398         return this.config[col].width * 1 || this.defaultWidth;
5399     },
5400
5401     /**
5402      * Sets the width for a column.
5403      * @param {Number} col The column index
5404      * @param {Number} width The new width
5405      */
5406     setColumnWidth : function(col, width, suppressEvent){
5407         this.config[col].width = width;
5408         this.totalWidth = null;
5409         if(!suppressEvent){
5410              this.fireEvent("widthchange", this, col, width);
5411         }
5412     },
5413
5414     /**
5415      * Returns the total width of all columns.
5416      * @param {Boolean} includeHidden True to include hidden column widths
5417      * @return {Number}
5418      */
5419     getTotalWidth : function(includeHidden){
5420         if(!this.totalWidth){
5421             this.totalWidth = 0;
5422             for(var i = 0, len = this.config.length; i < len; i++){
5423                 if(includeHidden || !this.isHidden(i)){
5424                     this.totalWidth += this.getColumnWidth(i);
5425                 }
5426             }
5427         }
5428         return this.totalWidth;
5429     },
5430
5431     /**
5432      * Returns the header for the specified column.
5433      * @param {Number} col The column index
5434      * @return {String}
5435      */
5436     getColumnHeader : function(col){
5437         return this.config[col].header;
5438     },
5439
5440     /**
5441      * Sets the header for a column.
5442      * @param {Number} col The column index
5443      * @param {String} header The new header
5444      */
5445     setColumnHeader : function(col, header){
5446         this.config[col].header = header;
5447         this.fireEvent("headerchange", this, col, header);
5448     },
5449
5450     /**
5451      * Returns the tooltip for the specified column.
5452      * @param {Number} col The column index
5453      * @return {String}
5454      */
5455     getColumnTooltip : function(col){
5456             return this.config[col].tooltip;
5457     },
5458     /**
5459      * Sets the tooltip for a column.
5460      * @param {Number} col The column index
5461      * @param {String} tooltip The new tooltip
5462      */
5463     setColumnTooltip : function(col, tooltip){
5464             this.config[col].tooltip = tooltip;
5465     },
5466
5467     /**
5468      * Returns the dataIndex for the specified column.
5469      * @param {Number} col The column index
5470      * @return {Number}
5471      */
5472     getDataIndex : function(col){
5473         return this.config[col].dataIndex;
5474     },
5475
5476     /**
5477      * Sets the dataIndex for a column.
5478      * @param {Number} col The column index
5479      * @param {Number} dataIndex The new dataIndex
5480      */
5481     setDataIndex : function(col, dataIndex){
5482         this.config[col].dataIndex = dataIndex;
5483     },
5484
5485     
5486     
5487     /**
5488      * Returns true if the cell is editable.
5489      * @param {Number} colIndex The column index
5490      * @param {Number} rowIndex The row index - this is nto actually used..?
5491      * @return {Boolean}
5492      */
5493     isCellEditable : function(colIndex, rowIndex){
5494         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5495     },
5496
5497     /**
5498      * Returns the editor defined for the cell/column.
5499      * return false or null to disable editing.
5500      * @param {Number} colIndex The column index
5501      * @param {Number} rowIndex The row index
5502      * @return {Object}
5503      */
5504     getCellEditor : function(colIndex, rowIndex){
5505         return this.config[colIndex].editor;
5506     },
5507
5508     /**
5509      * Sets if a column is editable.
5510      * @param {Number} col The column index
5511      * @param {Boolean} editable True if the column is editable
5512      */
5513     setEditable : function(col, editable){
5514         this.config[col].editable = editable;
5515     },
5516
5517
5518     /**
5519      * Returns true if the column is hidden.
5520      * @param {Number} colIndex The column index
5521      * @return {Boolean}
5522      */
5523     isHidden : function(colIndex){
5524         return this.config[colIndex].hidden;
5525     },
5526
5527
5528     /**
5529      * Returns true if the column width cannot be changed
5530      */
5531     isFixed : function(colIndex){
5532         return this.config[colIndex].fixed;
5533     },
5534
5535     /**
5536      * Returns true if the column can be resized
5537      * @return {Boolean}
5538      */
5539     isResizable : function(colIndex){
5540         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5541     },
5542     /**
5543      * Sets if a column is hidden.
5544      * @param {Number} colIndex The column index
5545      * @param {Boolean} hidden True if the column is hidden
5546      */
5547     setHidden : function(colIndex, hidden){
5548         this.config[colIndex].hidden = hidden;
5549         this.totalWidth = null;
5550         this.fireEvent("hiddenchange", this, colIndex, hidden);
5551     },
5552
5553     /**
5554      * Sets the editor for a column.
5555      * @param {Number} col The column index
5556      * @param {Object} editor The editor object
5557      */
5558     setEditor : function(col, editor){
5559         this.config[col].editor = editor;
5560     }
5561 });
5562
5563 Roo.grid.ColumnModel.defaultRenderer = function(value)
5564 {
5565     if(typeof value == "object") {
5566         return value;
5567     }
5568         if(typeof value == "string" && value.length < 1){
5569             return "&#160;";
5570         }
5571     
5572         return String.format("{0}", value);
5573 };
5574
5575 // Alias for backwards compatibility
5576 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5577 /*
5578  * Based on:
5579  * Ext JS Library 1.1.1
5580  * Copyright(c) 2006-2007, Ext JS, LLC.
5581  *
5582  * Originally Released Under LGPL - original licence link has changed is not relivant.
5583  *
5584  * Fork - LGPL
5585  * <script type="text/javascript">
5586  */
5587  
5588 /**
5589  * @class Roo.LoadMask
5590  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5591  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5592  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5593  * element's UpdateManager load indicator and will be destroyed after the initial load.
5594  * @constructor
5595  * Create a new LoadMask
5596  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5597  * @param {Object} config The config object
5598  */
5599 Roo.LoadMask = function(el, config){
5600     this.el = Roo.get(el);
5601     Roo.apply(this, config);
5602     if(this.store){
5603         this.store.on('beforeload', this.onBeforeLoad, this);
5604         this.store.on('load', this.onLoad, this);
5605         this.store.on('loadexception', this.onLoadException, this);
5606         this.removeMask = false;
5607     }else{
5608         var um = this.el.getUpdateManager();
5609         um.showLoadIndicator = false; // disable the default indicator
5610         um.on('beforeupdate', this.onBeforeLoad, this);
5611         um.on('update', this.onLoad, this);
5612         um.on('failure', this.onLoad, this);
5613         this.removeMask = true;
5614     }
5615 };
5616
5617 Roo.LoadMask.prototype = {
5618     /**
5619      * @cfg {Boolean} removeMask
5620      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5621      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5622      */
5623     /**
5624      * @cfg {String} msg
5625      * The text to display in a centered loading message box (defaults to 'Loading...')
5626      */
5627     msg : 'Loading...',
5628     /**
5629      * @cfg {String} msgCls
5630      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5631      */
5632     msgCls : 'x-mask-loading',
5633
5634     /**
5635      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5636      * @type Boolean
5637      */
5638     disabled: false,
5639
5640     /**
5641      * Disables the mask to prevent it from being displayed
5642      */
5643     disable : function(){
5644        this.disabled = true;
5645     },
5646
5647     /**
5648      * Enables the mask so that it can be displayed
5649      */
5650     enable : function(){
5651         this.disabled = false;
5652     },
5653     
5654     onLoadException : function()
5655     {
5656         Roo.log(arguments);
5657         
5658         if (typeof(arguments[3]) != 'undefined') {
5659             Roo.MessageBox.alert("Error loading",arguments[3]);
5660         } 
5661         /*
5662         try {
5663             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5664                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5665             }   
5666         } catch(e) {
5667             
5668         }
5669         */
5670     
5671         
5672         
5673         this.el.unmask(this.removeMask);
5674     },
5675     // private
5676     onLoad : function()
5677     {
5678         this.el.unmask(this.removeMask);
5679     },
5680
5681     // private
5682     onBeforeLoad : function(){
5683         if(!this.disabled){
5684             this.el.mask(this.msg, this.msgCls);
5685         }
5686     },
5687
5688     // private
5689     destroy : function(){
5690         if(this.store){
5691             this.store.un('beforeload', this.onBeforeLoad, this);
5692             this.store.un('load', this.onLoad, this);
5693             this.store.un('loadexception', this.onLoadException, this);
5694         }else{
5695             var um = this.el.getUpdateManager();
5696             um.un('beforeupdate', this.onBeforeLoad, this);
5697             um.un('update', this.onLoad, this);
5698             um.un('failure', this.onLoad, this);
5699         }
5700     }
5701 };/*
5702  * - LGPL
5703  *
5704  * table
5705  * 
5706  */
5707
5708 /**
5709  * @class Roo.bootstrap.Table
5710  * @extends Roo.bootstrap.Component
5711  * Bootstrap Table class
5712  * @cfg {String} cls table class
5713  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5714  * @cfg {String} bgcolor Specifies the background color for a table
5715  * @cfg {Number} border Specifies whether the table cells should have borders or not
5716  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5717  * @cfg {Number} cellspacing Specifies the space between cells
5718  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5719  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5720  * @cfg {String} sortable Specifies that the table should be sortable
5721  * @cfg {String} summary Specifies a summary of the content of a table
5722  * @cfg {Number} width Specifies the width of a table
5723  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5724  * 
5725  * @cfg {boolean} striped Should the rows be alternative striped
5726  * @cfg {boolean} bordered Add borders to the table
5727  * @cfg {boolean} hover Add hover highlighting
5728  * @cfg {boolean} condensed Format condensed
5729  * @cfg {boolean} responsive Format condensed
5730  * @cfg {Boolean} loadMask (true|false) default false
5731  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5732  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5733  * @cfg {Boolean} rowSelection (true|false) default false
5734  * @cfg {Boolean} cellSelection (true|false) default false
5735  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5736  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5737  
5738  * 
5739  * @constructor
5740  * Create a new Table
5741  * @param {Object} config The config object
5742  */
5743
5744 Roo.bootstrap.Table = function(config){
5745     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5746     
5747   
5748     
5749     // BC...
5750     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5751     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5752     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5753     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5754     
5755     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5756     if (this.sm) {
5757         this.sm.grid = this;
5758         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5759         this.sm = this.selModel;
5760         this.sm.xmodule = this.xmodule || false;
5761     }
5762     
5763     if (this.cm && typeof(this.cm.config) == 'undefined') {
5764         this.colModel = new Roo.grid.ColumnModel(this.cm);
5765         this.cm = this.colModel;
5766         this.cm.xmodule = this.xmodule || false;
5767     }
5768     if (this.store) {
5769         this.store= Roo.factory(this.store, Roo.data);
5770         this.ds = this.store;
5771         this.ds.xmodule = this.xmodule || false;
5772          
5773     }
5774     if (this.footer && this.store) {
5775         this.footer.dataSource = this.ds;
5776         this.footer = Roo.factory(this.footer);
5777     }
5778     
5779     /** @private */
5780     this.addEvents({
5781         /**
5782          * @event cellclick
5783          * Fires when a cell is clicked
5784          * @param {Roo.bootstrap.Table} this
5785          * @param {Roo.Element} el
5786          * @param {Number} rowIndex
5787          * @param {Number} columnIndex
5788          * @param {Roo.EventObject} e
5789          */
5790         "cellclick" : true,
5791         /**
5792          * @event celldblclick
5793          * Fires when a cell is double clicked
5794          * @param {Roo.bootstrap.Table} this
5795          * @param {Roo.Element} el
5796          * @param {Number} rowIndex
5797          * @param {Number} columnIndex
5798          * @param {Roo.EventObject} e
5799          */
5800         "celldblclick" : true,
5801         /**
5802          * @event rowclick
5803          * Fires when a row is clicked
5804          * @param {Roo.bootstrap.Table} this
5805          * @param {Roo.Element} el
5806          * @param {Number} rowIndex
5807          * @param {Roo.EventObject} e
5808          */
5809         "rowclick" : true,
5810         /**
5811          * @event rowdblclick
5812          * Fires when a row is double clicked
5813          * @param {Roo.bootstrap.Table} this
5814          * @param {Roo.Element} el
5815          * @param {Number} rowIndex
5816          * @param {Roo.EventObject} e
5817          */
5818         "rowdblclick" : true,
5819         /**
5820          * @event mouseover
5821          * Fires when a mouseover occur
5822          * @param {Roo.bootstrap.Table} this
5823          * @param {Roo.Element} el
5824          * @param {Number} rowIndex
5825          * @param {Number} columnIndex
5826          * @param {Roo.EventObject} e
5827          */
5828         "mouseover" : true,
5829         /**
5830          * @event mouseout
5831          * Fires when a mouseout occur
5832          * @param {Roo.bootstrap.Table} this
5833          * @param {Roo.Element} el
5834          * @param {Number} rowIndex
5835          * @param {Number} columnIndex
5836          * @param {Roo.EventObject} e
5837          */
5838         "mouseout" : true,
5839         /**
5840          * @event rowclass
5841          * Fires when a row is rendered, so you can change add a style to it.
5842          * @param {Roo.bootstrap.Table} this
5843          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5844          */
5845         'rowclass' : true,
5846           /**
5847          * @event rowsrendered
5848          * Fires when all the  rows have been rendered
5849          * @param {Roo.bootstrap.Table} this
5850          */
5851         'rowsrendered' : true,
5852         /**
5853          * @event contextmenu
5854          * The raw contextmenu event for the entire grid.
5855          * @param {Roo.EventObject} e
5856          */
5857         "contextmenu" : true,
5858         /**
5859          * @event rowcontextmenu
5860          * Fires when a row is right clicked
5861          * @param {Roo.bootstrap.Table} this
5862          * @param {Number} rowIndex
5863          * @param {Roo.EventObject} e
5864          */
5865         "rowcontextmenu" : true,
5866         /**
5867          * @event cellcontextmenu
5868          * Fires when a cell is right clicked
5869          * @param {Roo.bootstrap.Table} this
5870          * @param {Number} rowIndex
5871          * @param {Number} cellIndex
5872          * @param {Roo.EventObject} e
5873          */
5874          "cellcontextmenu" : true,
5875          /**
5876          * @event headercontextmenu
5877          * Fires when a header is right clicked
5878          * @param {Roo.bootstrap.Table} this
5879          * @param {Number} columnIndex
5880          * @param {Roo.EventObject} e
5881          */
5882         "headercontextmenu" : true
5883     });
5884 };
5885
5886 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5887     
5888     cls: false,
5889     align: false,
5890     bgcolor: false,
5891     border: false,
5892     cellpadding: false,
5893     cellspacing: false,
5894     frame: false,
5895     rules: false,
5896     sortable: false,
5897     summary: false,
5898     width: false,
5899     striped : false,
5900     scrollBody : false,
5901     bordered: false,
5902     hover:  false,
5903     condensed : false,
5904     responsive : false,
5905     sm : false,
5906     cm : false,
5907     store : false,
5908     loadMask : false,
5909     footerShow : true,
5910     headerShow : true,
5911   
5912     rowSelection : false,
5913     cellSelection : false,
5914     layout : false,
5915     
5916     // Roo.Element - the tbody
5917     mainBody: false,
5918     // Roo.Element - thead element
5919     mainHead: false,
5920     
5921     container: false, // used by gridpanel...
5922     
5923     getAutoCreate : function()
5924     {
5925         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5926         
5927         cfg = {
5928             tag: 'table',
5929             cls : 'table',
5930             cn : []
5931         };
5932         if (this.scrollBody) {
5933             cfg.cls += ' table-body-fixed';
5934         }    
5935         if (this.striped) {
5936             cfg.cls += ' table-striped';
5937         }
5938         
5939         if (this.hover) {
5940             cfg.cls += ' table-hover';
5941         }
5942         if (this.bordered) {
5943             cfg.cls += ' table-bordered';
5944         }
5945         if (this.condensed) {
5946             cfg.cls += ' table-condensed';
5947         }
5948         if (this.responsive) {
5949             cfg.cls += ' table-responsive';
5950         }
5951         
5952         if (this.cls) {
5953             cfg.cls+=  ' ' +this.cls;
5954         }
5955         
5956         // this lot should be simplifed...
5957         
5958         if (this.align) {
5959             cfg.align=this.align;
5960         }
5961         if (this.bgcolor) {
5962             cfg.bgcolor=this.bgcolor;
5963         }
5964         if (this.border) {
5965             cfg.border=this.border;
5966         }
5967         if (this.cellpadding) {
5968             cfg.cellpadding=this.cellpadding;
5969         }
5970         if (this.cellspacing) {
5971             cfg.cellspacing=this.cellspacing;
5972         }
5973         if (this.frame) {
5974             cfg.frame=this.frame;
5975         }
5976         if (this.rules) {
5977             cfg.rules=this.rules;
5978         }
5979         if (this.sortable) {
5980             cfg.sortable=this.sortable;
5981         }
5982         if (this.summary) {
5983             cfg.summary=this.summary;
5984         }
5985         if (this.width) {
5986             cfg.width=this.width;
5987         }
5988         if (this.layout) {
5989             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5990         }
5991         
5992         if(this.store || this.cm){
5993             if(this.headerShow){
5994                 cfg.cn.push(this.renderHeader());
5995             }
5996             
5997             cfg.cn.push(this.renderBody());
5998             
5999             if(this.footerShow){
6000                 cfg.cn.push(this.renderFooter());
6001             }
6002             // where does this come from?
6003             //cfg.cls+=  ' TableGrid';
6004         }
6005         
6006         return { cn : [ cfg ] };
6007     },
6008     
6009     initEvents : function()
6010     {   
6011         if(!this.store || !this.cm){
6012             return;
6013         }
6014         if (this.selModel) {
6015             this.selModel.initEvents();
6016         }
6017         
6018         
6019         //Roo.log('initEvents with ds!!!!');
6020         
6021         this.mainBody = this.el.select('tbody', true).first();
6022         this.mainHead = this.el.select('thead', true).first();
6023         
6024         
6025         
6026         
6027         var _this = this;
6028         
6029         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6030             e.on('click', _this.sort, _this);
6031         });
6032         
6033         this.mainBody.on("click", this.onClick, this);
6034         this.mainBody.on("dblclick", this.onDblClick, this);
6035         
6036         // why is this done????? = it breaks dialogs??
6037         //this.parent().el.setStyle('position', 'relative');
6038         
6039         
6040         if (this.footer) {
6041             this.footer.parentId = this.id;
6042             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
6043         } 
6044         
6045         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6046         
6047         this.store.on('load', this.onLoad, this);
6048         this.store.on('beforeload', this.onBeforeLoad, this);
6049         this.store.on('update', this.onUpdate, this);
6050         this.store.on('add', this.onAdd, this);
6051         this.store.on("clear", this.clear, this);
6052         
6053         this.el.on("contextmenu", this.onContextMenu, this);
6054         
6055         this.mainBody.on('scroll', this.onBodyScroll, this);
6056         
6057         
6058     },
6059     
6060     onContextMenu : function(e, t)
6061     {
6062         this.processEvent("contextmenu", e);
6063     },
6064     
6065     processEvent : function(name, e)
6066     {
6067         if (name != 'touchstart' ) {
6068             this.fireEvent(name, e);    
6069         }
6070         
6071         var t = e.getTarget();
6072         
6073         var cell = Roo.get(t);
6074         
6075         if(!cell){
6076             return;
6077         }
6078         
6079         if(cell.findParent('tfoot', false, true)){
6080             return;
6081         }
6082         
6083         if(cell.findParent('thead', false, true)){
6084             
6085             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6086                 cell = Roo.get(t).findParent('th', false, true);
6087                 if (!cell) {
6088                     Roo.log("failed to find th in thead?");
6089                     Roo.log(e.getTarget());
6090                     return;
6091                 }
6092             }
6093             
6094             var cellIndex = cell.dom.cellIndex;
6095             
6096             var ename = name == 'touchstart' ? 'click' : name;
6097             this.fireEvent("header" + ename, this, cellIndex, e);
6098             
6099             return;
6100         }
6101         
6102         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6103             cell = Roo.get(t).findParent('td', false, true);
6104             if (!cell) {
6105                 Roo.log("failed to find th in tbody?");
6106                 Roo.log(e.getTarget());
6107                 return;
6108             }
6109         }
6110         
6111         var row = cell.findParent('tr', false, true);
6112         var cellIndex = cell.dom.cellIndex;
6113         var rowIndex = row.dom.rowIndex - 1;
6114         
6115         if(row !== false){
6116             
6117             this.fireEvent("row" + name, this, rowIndex, e);
6118             
6119             if(cell !== false){
6120             
6121                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6122             }
6123         }
6124         
6125     },
6126     
6127     onMouseover : function(e, el)
6128     {
6129         var cell = Roo.get(el);
6130         
6131         if(!cell){
6132             return;
6133         }
6134         
6135         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6136             cell = cell.findParent('td', false, true);
6137         }
6138         
6139         var row = cell.findParent('tr', false, true);
6140         var cellIndex = cell.dom.cellIndex;
6141         var rowIndex = row.dom.rowIndex - 1; // start from 0
6142         
6143         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6144         
6145     },
6146     
6147     onMouseout : function(e, el)
6148     {
6149         var cell = Roo.get(el);
6150         
6151         if(!cell){
6152             return;
6153         }
6154         
6155         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6156             cell = cell.findParent('td', false, true);
6157         }
6158         
6159         var row = cell.findParent('tr', false, true);
6160         var cellIndex = cell.dom.cellIndex;
6161         var rowIndex = row.dom.rowIndex - 1; // start from 0
6162         
6163         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6164         
6165     },
6166     
6167     onClick : function(e, el)
6168     {
6169         var cell = Roo.get(el);
6170         
6171         if(!cell || (!this.cellSelection && !this.rowSelection)){
6172             return;
6173         }
6174         
6175         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6176             cell = cell.findParent('td', false, true);
6177         }
6178         
6179         if(!cell || typeof(cell) == 'undefined'){
6180             return;
6181         }
6182         
6183         var row = cell.findParent('tr', false, true);
6184         
6185         if(!row || typeof(row) == 'undefined'){
6186             return;
6187         }
6188         
6189         var cellIndex = cell.dom.cellIndex;
6190         var rowIndex = this.getRowIndex(row);
6191         
6192         // why??? - should these not be based on SelectionModel?
6193         if(this.cellSelection){
6194             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6195         }
6196         
6197         if(this.rowSelection){
6198             this.fireEvent('rowclick', this, row, rowIndex, e);
6199         }
6200         
6201         
6202     },
6203         
6204     onDblClick : function(e,el)
6205     {
6206         var cell = Roo.get(el);
6207         
6208         if(!cell || (!this.cellSelection && !this.rowSelection)){
6209             return;
6210         }
6211         
6212         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6213             cell = cell.findParent('td', false, true);
6214         }
6215         
6216         if(!cell || typeof(cell) == 'undefined'){
6217             return;
6218         }
6219         
6220         var row = cell.findParent('tr', false, true);
6221         
6222         if(!row || typeof(row) == 'undefined'){
6223             return;
6224         }
6225         
6226         var cellIndex = cell.dom.cellIndex;
6227         var rowIndex = this.getRowIndex(row);
6228         
6229         if(this.cellSelection){
6230             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6231         }
6232         
6233         if(this.rowSelection){
6234             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6235         }
6236     },
6237     
6238     sort : function(e,el)
6239     {
6240         var col = Roo.get(el);
6241         
6242         if(!col.hasClass('sortable')){
6243             return;
6244         }
6245         
6246         var sort = col.attr('sort');
6247         var dir = 'ASC';
6248         
6249         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6250             dir = 'DESC';
6251         }
6252         
6253         this.store.sortInfo = {field : sort, direction : dir};
6254         
6255         if (this.footer) {
6256             Roo.log("calling footer first");
6257             this.footer.onClick('first');
6258         } else {
6259         
6260             this.store.load({ params : { start : 0 } });
6261         }
6262     },
6263     
6264     renderHeader : function()
6265     {
6266         var header = {
6267             tag: 'thead',
6268             cn : []
6269         };
6270         
6271         var cm = this.cm;
6272         this.totalWidth = 0;
6273         
6274         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6275             
6276             var config = cm.config[i];
6277             
6278             var c = {
6279                 tag: 'th',
6280                 style : '',
6281                 html: cm.getColumnHeader(i)
6282             };
6283             
6284             var hh = '';
6285             
6286             if(typeof(config.sortable) != 'undefined' && config.sortable){
6287                 c.cls = 'sortable';
6288                 c.html = '<i class="glyphicon"></i>' + c.html;
6289             }
6290             
6291             if(typeof(config.lgHeader) != 'undefined'){
6292                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6293             }
6294             
6295             if(typeof(config.mdHeader) != 'undefined'){
6296                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6297             }
6298             
6299             if(typeof(config.smHeader) != 'undefined'){
6300                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6301             }
6302             
6303             if(typeof(config.xsHeader) != 'undefined'){
6304                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6305             }
6306             
6307             if(hh.length){
6308                 c.html = hh;
6309             }
6310             
6311             if(typeof(config.tooltip) != 'undefined'){
6312                 c.tooltip = config.tooltip;
6313             }
6314             
6315             if(typeof(config.colspan) != 'undefined'){
6316                 c.colspan = config.colspan;
6317             }
6318             
6319             if(typeof(config.hidden) != 'undefined' && config.hidden){
6320                 c.style += ' display:none;';
6321             }
6322             
6323             if(typeof(config.dataIndex) != 'undefined'){
6324                 c.sort = config.dataIndex;
6325             }
6326             
6327            
6328             
6329             if(typeof(config.align) != 'undefined' && config.align.length){
6330                 c.style += ' text-align:' + config.align + ';';
6331             }
6332             
6333             if(typeof(config.width) != 'undefined'){
6334                 c.style += ' width:' + config.width + 'px;';
6335                 this.totalWidth += config.width;
6336             } else {
6337                 this.totalWidth += 100; // assume minimum of 100 per column?
6338             }
6339             
6340             if(typeof(config.cls) != 'undefined'){
6341                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6342             }
6343             
6344             ['xs','sm','md','lg'].map(function(size){
6345                 
6346                 if(typeof(config[size]) == 'undefined'){
6347                     return;
6348                 }
6349                 
6350                 if (!config[size]) { // 0 = hidden
6351                     c.cls += ' hidden-' + size;
6352                     return;
6353                 }
6354                 
6355                 c.cls += ' col-' + size + '-' + config[size];
6356
6357             });
6358             
6359             header.cn.push(c)
6360         }
6361         
6362         return header;
6363     },
6364     
6365     renderBody : function()
6366     {
6367         var body = {
6368             tag: 'tbody',
6369             cn : [
6370                 {
6371                     tag: 'tr',
6372                     cn : [
6373                         {
6374                             tag : 'td',
6375                             colspan :  this.cm.getColumnCount()
6376                         }
6377                     ]
6378                 }
6379             ]
6380         };
6381         
6382         return body;
6383     },
6384     
6385     renderFooter : function()
6386     {
6387         var footer = {
6388             tag: 'tfoot',
6389             cn : [
6390                 {
6391                     tag: 'tr',
6392                     cn : [
6393                         {
6394                             tag : 'td',
6395                             colspan :  this.cm.getColumnCount()
6396                         }
6397                     ]
6398                 }
6399             ]
6400         };
6401         
6402         return footer;
6403     },
6404     
6405     
6406     
6407     onLoad : function()
6408     {
6409 //        Roo.log('ds onload');
6410         this.clear();
6411         
6412         var _this = this;
6413         var cm = this.cm;
6414         var ds = this.store;
6415         
6416         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6417             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6418             if (_this.store.sortInfo) {
6419                     
6420                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6421                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6422                 }
6423                 
6424                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6425                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6426                 }
6427             }
6428         });
6429         
6430         var tbody =  this.mainBody;
6431               
6432         if(ds.getCount() > 0){
6433             ds.data.each(function(d,rowIndex){
6434                 var row =  this.renderRow(cm, ds, rowIndex);
6435                 
6436                 tbody.createChild(row);
6437                 
6438                 var _this = this;
6439                 
6440                 if(row.cellObjects.length){
6441                     Roo.each(row.cellObjects, function(r){
6442                         _this.renderCellObject(r);
6443                     })
6444                 }
6445                 
6446             }, this);
6447         }
6448         
6449         Roo.each(this.el.select('tbody td', true).elements, function(e){
6450             e.on('mouseover', _this.onMouseover, _this);
6451         });
6452         
6453         Roo.each(this.el.select('tbody td', true).elements, function(e){
6454             e.on('mouseout', _this.onMouseout, _this);
6455         });
6456         this.fireEvent('rowsrendered', this);
6457         //if(this.loadMask){
6458         //    this.maskEl.hide();
6459         //}
6460         
6461         this.autoSize();
6462     },
6463     
6464     
6465     onUpdate : function(ds,record)
6466     {
6467         this.refreshRow(record);
6468         this.autoSize();
6469     },
6470     
6471     onRemove : function(ds, record, index, isUpdate){
6472         if(isUpdate !== true){
6473             this.fireEvent("beforerowremoved", this, index, record);
6474         }
6475         var bt = this.mainBody.dom;
6476         
6477         var rows = this.el.select('tbody > tr', true).elements;
6478         
6479         if(typeof(rows[index]) != 'undefined'){
6480             bt.removeChild(rows[index].dom);
6481         }
6482         
6483 //        if(bt.rows[index]){
6484 //            bt.removeChild(bt.rows[index]);
6485 //        }
6486         
6487         if(isUpdate !== true){
6488             //this.stripeRows(index);
6489             //this.syncRowHeights(index, index);
6490             //this.layout();
6491             this.fireEvent("rowremoved", this, index, record);
6492         }
6493     },
6494     
6495     onAdd : function(ds, records, rowIndex)
6496     {
6497         //Roo.log('on Add called');
6498         // - note this does not handle multiple adding very well..
6499         var bt = this.mainBody.dom;
6500         for (var i =0 ; i < records.length;i++) {
6501             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6502             //Roo.log(records[i]);
6503             //Roo.log(this.store.getAt(rowIndex+i));
6504             this.insertRow(this.store, rowIndex + i, false);
6505             return;
6506         }
6507         
6508     },
6509     
6510     
6511     refreshRow : function(record){
6512         var ds = this.store, index;
6513         if(typeof record == 'number'){
6514             index = record;
6515             record = ds.getAt(index);
6516         }else{
6517             index = ds.indexOf(record);
6518         }
6519         this.insertRow(ds, index, true);
6520         this.autoSize();
6521         this.onRemove(ds, record, index+1, true);
6522         this.autoSize();
6523         //this.syncRowHeights(index, index);
6524         //this.layout();
6525         this.fireEvent("rowupdated", this, index, record);
6526     },
6527     
6528     insertRow : function(dm, rowIndex, isUpdate){
6529         
6530         if(!isUpdate){
6531             this.fireEvent("beforerowsinserted", this, rowIndex);
6532         }
6533             //var s = this.getScrollState();
6534         var row = this.renderRow(this.cm, this.store, rowIndex);
6535         // insert before rowIndex..
6536         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6537         
6538         var _this = this;
6539                 
6540         if(row.cellObjects.length){
6541             Roo.each(row.cellObjects, function(r){
6542                 _this.renderCellObject(r);
6543             })
6544         }
6545             
6546         if(!isUpdate){
6547             this.fireEvent("rowsinserted", this, rowIndex);
6548             //this.syncRowHeights(firstRow, lastRow);
6549             //this.stripeRows(firstRow);
6550             //this.layout();
6551         }
6552         
6553     },
6554     
6555     
6556     getRowDom : function(rowIndex)
6557     {
6558         var rows = this.el.select('tbody > tr', true).elements;
6559         
6560         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6561         
6562     },
6563     // returns the object tree for a tr..
6564   
6565     
6566     renderRow : function(cm, ds, rowIndex) 
6567     {
6568         
6569         var d = ds.getAt(rowIndex);
6570         
6571         var row = {
6572             tag : 'tr',
6573             cn : []
6574         };
6575             
6576         var cellObjects = [];
6577         
6578         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6579             var config = cm.config[i];
6580             
6581             var renderer = cm.getRenderer(i);
6582             var value = '';
6583             var id = false;
6584             
6585             if(typeof(renderer) !== 'undefined'){
6586                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6587             }
6588             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6589             // and are rendered into the cells after the row is rendered - using the id for the element.
6590             
6591             if(typeof(value) === 'object'){
6592                 id = Roo.id();
6593                 cellObjects.push({
6594                     container : id,
6595                     cfg : value 
6596                 })
6597             }
6598             
6599             var rowcfg = {
6600                 record: d,
6601                 rowIndex : rowIndex,
6602                 colIndex : i,
6603                 rowClass : ''
6604             };
6605
6606             this.fireEvent('rowclass', this, rowcfg);
6607             
6608             var td = {
6609                 tag: 'td',
6610                 cls : rowcfg.rowClass,
6611                 style: '',
6612                 html: (typeof(value) === 'object') ? '' : value
6613             };
6614             
6615             if (id) {
6616                 td.id = id;
6617             }
6618             
6619             if(typeof(config.colspan) != 'undefined'){
6620                 td.colspan = config.colspan;
6621             }
6622             
6623             if(typeof(config.hidden) != 'undefined' && config.hidden){
6624                 td.style += ' display:none;';
6625             }
6626             
6627             if(typeof(config.align) != 'undefined' && config.align.length){
6628                 td.style += ' text-align:' + config.align + ';';
6629             }
6630             
6631             if(typeof(config.width) != 'undefined'){
6632                 td.style += ' width:' +  config.width + 'px;';
6633             }
6634             
6635             if(typeof(config.cursor) != 'undefined'){
6636                 td.style += ' cursor:' +  config.cursor + ';';
6637             }
6638             
6639             if(typeof(config.cls) != 'undefined'){
6640                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6641             }
6642             
6643             ['xs','sm','md','lg'].map(function(size){
6644                 
6645                 if(typeof(config[size]) == 'undefined'){
6646                     return;
6647                 }
6648                 
6649                 if (!config[size]) { // 0 = hidden
6650                     td.cls += ' hidden-' + size;
6651                     return;
6652                 }
6653                 
6654                 td.cls += ' col-' + size + '-' + config[size];
6655
6656             });
6657              
6658             row.cn.push(td);
6659            
6660         }
6661         
6662         row.cellObjects = cellObjects;
6663         
6664         return row;
6665           
6666     },
6667     
6668     
6669     
6670     onBeforeLoad : function()
6671     {
6672         //Roo.log('ds onBeforeLoad');
6673         
6674         //this.clear();
6675         
6676         //if(this.loadMask){
6677         //    this.maskEl.show();
6678         //}
6679     },
6680      /**
6681      * Remove all rows
6682      */
6683     clear : function()
6684     {
6685         this.el.select('tbody', true).first().dom.innerHTML = '';
6686     },
6687     /**
6688      * Show or hide a row.
6689      * @param {Number} rowIndex to show or hide
6690      * @param {Boolean} state hide
6691      */
6692     setRowVisibility : function(rowIndex, state)
6693     {
6694         var bt = this.mainBody.dom;
6695         
6696         var rows = this.el.select('tbody > tr', true).elements;
6697         
6698         if(typeof(rows[rowIndex]) == 'undefined'){
6699             return;
6700         }
6701         rows[rowIndex].dom.style.display = state ? '' : 'none';
6702     },
6703     
6704     
6705     getSelectionModel : function(){
6706         if(!this.selModel){
6707             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6708         }
6709         return this.selModel;
6710     },
6711     /*
6712      * Render the Roo.bootstrap object from renderder
6713      */
6714     renderCellObject : function(r)
6715     {
6716         var _this = this;
6717         
6718         var t = r.cfg.render(r.container);
6719         
6720         if(r.cfg.cn){
6721             Roo.each(r.cfg.cn, function(c){
6722                 var child = {
6723                     container: t.getChildContainer(),
6724                     cfg: c
6725                 };
6726                 _this.renderCellObject(child);
6727             })
6728         }
6729     },
6730     
6731     getRowIndex : function(row)
6732     {
6733         var rowIndex = -1;
6734         
6735         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6736             if(el != row){
6737                 return;
6738             }
6739             
6740             rowIndex = index;
6741         });
6742         
6743         return rowIndex;
6744     },
6745      /**
6746      * Returns the grid's underlying element = used by panel.Grid
6747      * @return {Element} The element
6748      */
6749     getGridEl : function(){
6750         return this.el;
6751     },
6752      /**
6753      * Forces a resize - used by panel.Grid
6754      * @return {Element} The element
6755      */
6756     autoSize : function()
6757     {
6758         //var ctr = Roo.get(this.container.dom.parentElement);
6759         var ctr = Roo.get(this.el.dom);
6760         
6761         var thd = this.getGridEl().select('thead',true).first();
6762         var tbd = this.getGridEl().select('tbody', true).first();
6763         var tfd = this.getGridEl().select('tfoot', true).first();
6764         
6765         var cw = ctr.getWidth();
6766         
6767         if (tbd) {
6768             
6769             tbd.setSize(ctr.getWidth(),
6770                         ctr.getHeight() - (thd.getHeight() + (tfd ? tfd.getHeight() : 0))
6771             );
6772             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6773             cw -= barsize;
6774         }
6775         cw = Math.max(cw, this.totalWidth);
6776         this.getGridEl().select('tr',true).setWidth(cw);
6777         // resize 'expandable coloumn?
6778         
6779         return; // we doe not have a view in this design..
6780         
6781     },
6782     onBodyScroll: function()
6783     {
6784         
6785         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6786         this.mainHead.setStyle({
6787                     'position' : 'relative',
6788                     'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6789         });
6790         
6791         
6792     }
6793 });
6794
6795  
6796
6797  /*
6798  * - LGPL
6799  *
6800  * table cell
6801  * 
6802  */
6803
6804 /**
6805  * @class Roo.bootstrap.TableCell
6806  * @extends Roo.bootstrap.Component
6807  * Bootstrap TableCell class
6808  * @cfg {String} html cell contain text
6809  * @cfg {String} cls cell class
6810  * @cfg {String} tag cell tag (td|th) default td
6811  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6812  * @cfg {String} align Aligns the content in a cell
6813  * @cfg {String} axis Categorizes cells
6814  * @cfg {String} bgcolor Specifies the background color of a cell
6815  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6816  * @cfg {Number} colspan Specifies the number of columns a cell should span
6817  * @cfg {String} headers Specifies one or more header cells a cell is related to
6818  * @cfg {Number} height Sets the height of a cell
6819  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6820  * @cfg {Number} rowspan Sets the number of rows a cell should span
6821  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6822  * @cfg {String} valign Vertical aligns the content in a cell
6823  * @cfg {Number} width Specifies the width of a cell
6824  * 
6825  * @constructor
6826  * Create a new TableCell
6827  * @param {Object} config The config object
6828  */
6829
6830 Roo.bootstrap.TableCell = function(config){
6831     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6832 };
6833
6834 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6835     
6836     html: false,
6837     cls: false,
6838     tag: false,
6839     abbr: false,
6840     align: false,
6841     axis: false,
6842     bgcolor: false,
6843     charoff: false,
6844     colspan: false,
6845     headers: false,
6846     height: false,
6847     nowrap: false,
6848     rowspan: false,
6849     scope: false,
6850     valign: false,
6851     width: false,
6852     
6853     
6854     getAutoCreate : function(){
6855         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6856         
6857         cfg = {
6858             tag: 'td'
6859         };
6860         
6861         if(this.tag){
6862             cfg.tag = this.tag;
6863         }
6864         
6865         if (this.html) {
6866             cfg.html=this.html
6867         }
6868         if (this.cls) {
6869             cfg.cls=this.cls
6870         }
6871         if (this.abbr) {
6872             cfg.abbr=this.abbr
6873         }
6874         if (this.align) {
6875             cfg.align=this.align
6876         }
6877         if (this.axis) {
6878             cfg.axis=this.axis
6879         }
6880         if (this.bgcolor) {
6881             cfg.bgcolor=this.bgcolor
6882         }
6883         if (this.charoff) {
6884             cfg.charoff=this.charoff
6885         }
6886         if (this.colspan) {
6887             cfg.colspan=this.colspan
6888         }
6889         if (this.headers) {
6890             cfg.headers=this.headers
6891         }
6892         if (this.height) {
6893             cfg.height=this.height
6894         }
6895         if (this.nowrap) {
6896             cfg.nowrap=this.nowrap
6897         }
6898         if (this.rowspan) {
6899             cfg.rowspan=this.rowspan
6900         }
6901         if (this.scope) {
6902             cfg.scope=this.scope
6903         }
6904         if (this.valign) {
6905             cfg.valign=this.valign
6906         }
6907         if (this.width) {
6908             cfg.width=this.width
6909         }
6910         
6911         
6912         return cfg;
6913     }
6914    
6915 });
6916
6917  
6918
6919  /*
6920  * - LGPL
6921  *
6922  * table row
6923  * 
6924  */
6925
6926 /**
6927  * @class Roo.bootstrap.TableRow
6928  * @extends Roo.bootstrap.Component
6929  * Bootstrap TableRow class
6930  * @cfg {String} cls row class
6931  * @cfg {String} align Aligns the content in a table row
6932  * @cfg {String} bgcolor Specifies a background color for a table row
6933  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6934  * @cfg {String} valign Vertical aligns the content in a table row
6935  * 
6936  * @constructor
6937  * Create a new TableRow
6938  * @param {Object} config The config object
6939  */
6940
6941 Roo.bootstrap.TableRow = function(config){
6942     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6943 };
6944
6945 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6946     
6947     cls: false,
6948     align: false,
6949     bgcolor: false,
6950     charoff: false,
6951     valign: false,
6952     
6953     getAutoCreate : function(){
6954         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6955         
6956         cfg = {
6957             tag: 'tr'
6958         };
6959             
6960         if(this.cls){
6961             cfg.cls = this.cls;
6962         }
6963         if(this.align){
6964             cfg.align = this.align;
6965         }
6966         if(this.bgcolor){
6967             cfg.bgcolor = this.bgcolor;
6968         }
6969         if(this.charoff){
6970             cfg.charoff = this.charoff;
6971         }
6972         if(this.valign){
6973             cfg.valign = this.valign;
6974         }
6975         
6976         return cfg;
6977     }
6978    
6979 });
6980
6981  
6982
6983  /*
6984  * - LGPL
6985  *
6986  * table body
6987  * 
6988  */
6989
6990 /**
6991  * @class Roo.bootstrap.TableBody
6992  * @extends Roo.bootstrap.Component
6993  * Bootstrap TableBody class
6994  * @cfg {String} cls element class
6995  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6996  * @cfg {String} align Aligns the content inside the element
6997  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6998  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6999  * 
7000  * @constructor
7001  * Create a new TableBody
7002  * @param {Object} config The config object
7003  */
7004
7005 Roo.bootstrap.TableBody = function(config){
7006     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7007 };
7008
7009 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7010     
7011     cls: false,
7012     tag: false,
7013     align: false,
7014     charoff: false,
7015     valign: false,
7016     
7017     getAutoCreate : function(){
7018         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7019         
7020         cfg = {
7021             tag: 'tbody'
7022         };
7023             
7024         if (this.cls) {
7025             cfg.cls=this.cls
7026         }
7027         if(this.tag){
7028             cfg.tag = this.tag;
7029         }
7030         
7031         if(this.align){
7032             cfg.align = this.align;
7033         }
7034         if(this.charoff){
7035             cfg.charoff = this.charoff;
7036         }
7037         if(this.valign){
7038             cfg.valign = this.valign;
7039         }
7040         
7041         return cfg;
7042     }
7043     
7044     
7045 //    initEvents : function()
7046 //    {
7047 //        
7048 //        if(!this.store){
7049 //            return;
7050 //        }
7051 //        
7052 //        this.store = Roo.factory(this.store, Roo.data);
7053 //        this.store.on('load', this.onLoad, this);
7054 //        
7055 //        this.store.load();
7056 //        
7057 //    },
7058 //    
7059 //    onLoad: function () 
7060 //    {   
7061 //        this.fireEvent('load', this);
7062 //    }
7063 //    
7064 //   
7065 });
7066
7067  
7068
7069  /*
7070  * Based on:
7071  * Ext JS Library 1.1.1
7072  * Copyright(c) 2006-2007, Ext JS, LLC.
7073  *
7074  * Originally Released Under LGPL - original licence link has changed is not relivant.
7075  *
7076  * Fork - LGPL
7077  * <script type="text/javascript">
7078  */
7079
7080 // as we use this in bootstrap.
7081 Roo.namespace('Roo.form');
7082  /**
7083  * @class Roo.form.Action
7084  * Internal Class used to handle form actions
7085  * @constructor
7086  * @param {Roo.form.BasicForm} el The form element or its id
7087  * @param {Object} config Configuration options
7088  */
7089
7090  
7091  
7092 // define the action interface
7093 Roo.form.Action = function(form, options){
7094     this.form = form;
7095     this.options = options || {};
7096 };
7097 /**
7098  * Client Validation Failed
7099  * @const 
7100  */
7101 Roo.form.Action.CLIENT_INVALID = 'client';
7102 /**
7103  * Server Validation Failed
7104  * @const 
7105  */
7106 Roo.form.Action.SERVER_INVALID = 'server';
7107  /**
7108  * Connect to Server Failed
7109  * @const 
7110  */
7111 Roo.form.Action.CONNECT_FAILURE = 'connect';
7112 /**
7113  * Reading Data from Server Failed
7114  * @const 
7115  */
7116 Roo.form.Action.LOAD_FAILURE = 'load';
7117
7118 Roo.form.Action.prototype = {
7119     type : 'default',
7120     failureType : undefined,
7121     response : undefined,
7122     result : undefined,
7123
7124     // interface method
7125     run : function(options){
7126
7127     },
7128
7129     // interface method
7130     success : function(response){
7131
7132     },
7133
7134     // interface method
7135     handleResponse : function(response){
7136
7137     },
7138
7139     // default connection failure
7140     failure : function(response){
7141         
7142         this.response = response;
7143         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7144         this.form.afterAction(this, false);
7145     },
7146
7147     processResponse : function(response){
7148         this.response = response;
7149         if(!response.responseText){
7150             return true;
7151         }
7152         this.result = this.handleResponse(response);
7153         return this.result;
7154     },
7155
7156     // utility functions used internally
7157     getUrl : function(appendParams){
7158         var url = this.options.url || this.form.url || this.form.el.dom.action;
7159         if(appendParams){
7160             var p = this.getParams();
7161             if(p){
7162                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7163             }
7164         }
7165         return url;
7166     },
7167
7168     getMethod : function(){
7169         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7170     },
7171
7172     getParams : function(){
7173         var bp = this.form.baseParams;
7174         var p = this.options.params;
7175         if(p){
7176             if(typeof p == "object"){
7177                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7178             }else if(typeof p == 'string' && bp){
7179                 p += '&' + Roo.urlEncode(bp);
7180             }
7181         }else if(bp){
7182             p = Roo.urlEncode(bp);
7183         }
7184         return p;
7185     },
7186
7187     createCallback : function(){
7188         return {
7189             success: this.success,
7190             failure: this.failure,
7191             scope: this,
7192             timeout: (this.form.timeout*1000),
7193             upload: this.form.fileUpload ? this.success : undefined
7194         };
7195     }
7196 };
7197
7198 Roo.form.Action.Submit = function(form, options){
7199     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7200 };
7201
7202 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7203     type : 'submit',
7204
7205     haveProgress : false,
7206     uploadComplete : false,
7207     
7208     // uploadProgress indicator.
7209     uploadProgress : function()
7210     {
7211         if (!this.form.progressUrl) {
7212             return;
7213         }
7214         
7215         if (!this.haveProgress) {
7216             Roo.MessageBox.progress("Uploading", "Uploading");
7217         }
7218         if (this.uploadComplete) {
7219            Roo.MessageBox.hide();
7220            return;
7221         }
7222         
7223         this.haveProgress = true;
7224    
7225         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7226         
7227         var c = new Roo.data.Connection();
7228         c.request({
7229             url : this.form.progressUrl,
7230             params: {
7231                 id : uid
7232             },
7233             method: 'GET',
7234             success : function(req){
7235                //console.log(data);
7236                 var rdata = false;
7237                 var edata;
7238                 try  {
7239                    rdata = Roo.decode(req.responseText)
7240                 } catch (e) {
7241                     Roo.log("Invalid data from server..");
7242                     Roo.log(edata);
7243                     return;
7244                 }
7245                 if (!rdata || !rdata.success) {
7246                     Roo.log(rdata);
7247                     Roo.MessageBox.alert(Roo.encode(rdata));
7248                     return;
7249                 }
7250                 var data = rdata.data;
7251                 
7252                 if (this.uploadComplete) {
7253                    Roo.MessageBox.hide();
7254                    return;
7255                 }
7256                    
7257                 if (data){
7258                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7259                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7260                     );
7261                 }
7262                 this.uploadProgress.defer(2000,this);
7263             },
7264        
7265             failure: function(data) {
7266                 Roo.log('progress url failed ');
7267                 Roo.log(data);
7268             },
7269             scope : this
7270         });
7271            
7272     },
7273     
7274     
7275     run : function()
7276     {
7277         // run get Values on the form, so it syncs any secondary forms.
7278         this.form.getValues();
7279         
7280         var o = this.options;
7281         var method = this.getMethod();
7282         var isPost = method == 'POST';
7283         if(o.clientValidation === false || this.form.isValid()){
7284             
7285             if (this.form.progressUrl) {
7286                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7287                     (new Date() * 1) + '' + Math.random());
7288                     
7289             } 
7290             
7291             
7292             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7293                 form:this.form.el.dom,
7294                 url:this.getUrl(!isPost),
7295                 method: method,
7296                 params:isPost ? this.getParams() : null,
7297                 isUpload: this.form.fileUpload
7298             }));
7299             
7300             this.uploadProgress();
7301
7302         }else if (o.clientValidation !== false){ // client validation failed
7303             this.failureType = Roo.form.Action.CLIENT_INVALID;
7304             this.form.afterAction(this, false);
7305         }
7306     },
7307
7308     success : function(response)
7309     {
7310         this.uploadComplete= true;
7311         if (this.haveProgress) {
7312             Roo.MessageBox.hide();
7313         }
7314         
7315         
7316         var result = this.processResponse(response);
7317         if(result === true || result.success){
7318             this.form.afterAction(this, true);
7319             return;
7320         }
7321         if(result.errors){
7322             this.form.markInvalid(result.errors);
7323             this.failureType = Roo.form.Action.SERVER_INVALID;
7324         }
7325         this.form.afterAction(this, false);
7326     },
7327     failure : function(response)
7328     {
7329         this.uploadComplete= true;
7330         if (this.haveProgress) {
7331             Roo.MessageBox.hide();
7332         }
7333         
7334         this.response = response;
7335         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7336         this.form.afterAction(this, false);
7337     },
7338     
7339     handleResponse : function(response){
7340         if(this.form.errorReader){
7341             var rs = this.form.errorReader.read(response);
7342             var errors = [];
7343             if(rs.records){
7344                 for(var i = 0, len = rs.records.length; i < len; i++) {
7345                     var r = rs.records[i];
7346                     errors[i] = r.data;
7347                 }
7348             }
7349             if(errors.length < 1){
7350                 errors = null;
7351             }
7352             return {
7353                 success : rs.success,
7354                 errors : errors
7355             };
7356         }
7357         var ret = false;
7358         try {
7359             ret = Roo.decode(response.responseText);
7360         } catch (e) {
7361             ret = {
7362                 success: false,
7363                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7364                 errors : []
7365             };
7366         }
7367         return ret;
7368         
7369     }
7370 });
7371
7372
7373 Roo.form.Action.Load = function(form, options){
7374     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7375     this.reader = this.form.reader;
7376 };
7377
7378 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7379     type : 'load',
7380
7381     run : function(){
7382         
7383         Roo.Ajax.request(Roo.apply(
7384                 this.createCallback(), {
7385                     method:this.getMethod(),
7386                     url:this.getUrl(false),
7387                     params:this.getParams()
7388         }));
7389     },
7390
7391     success : function(response){
7392         
7393         var result = this.processResponse(response);
7394         if(result === true || !result.success || !result.data){
7395             this.failureType = Roo.form.Action.LOAD_FAILURE;
7396             this.form.afterAction(this, false);
7397             return;
7398         }
7399         this.form.clearInvalid();
7400         this.form.setValues(result.data);
7401         this.form.afterAction(this, true);
7402     },
7403
7404     handleResponse : function(response){
7405         if(this.form.reader){
7406             var rs = this.form.reader.read(response);
7407             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7408             return {
7409                 success : rs.success,
7410                 data : data
7411             };
7412         }
7413         return Roo.decode(response.responseText);
7414     }
7415 });
7416
7417 Roo.form.Action.ACTION_TYPES = {
7418     'load' : Roo.form.Action.Load,
7419     'submit' : Roo.form.Action.Submit
7420 };/*
7421  * - LGPL
7422  *
7423  * form
7424  *
7425  */
7426
7427 /**
7428  * @class Roo.bootstrap.Form
7429  * @extends Roo.bootstrap.Component
7430  * Bootstrap Form class
7431  * @cfg {String} method  GET | POST (default POST)
7432  * @cfg {String} labelAlign top | left (default top)
7433  * @cfg {String} align left  | right - for navbars
7434  * @cfg {Boolean} loadMask load mask when submit (default true)
7435
7436  *
7437  * @constructor
7438  * Create a new Form
7439  * @param {Object} config The config object
7440  */
7441
7442
7443 Roo.bootstrap.Form = function(config){
7444     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7445     this.addEvents({
7446         /**
7447          * @event clientvalidation
7448          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7449          * @param {Form} this
7450          * @param {Boolean} valid true if the form has passed client-side validation
7451          */
7452         clientvalidation: true,
7453         /**
7454          * @event beforeaction
7455          * Fires before any action is performed. Return false to cancel the action.
7456          * @param {Form} this
7457          * @param {Action} action The action to be performed
7458          */
7459         beforeaction: true,
7460         /**
7461          * @event actionfailed
7462          * Fires when an action fails.
7463          * @param {Form} this
7464          * @param {Action} action The action that failed
7465          */
7466         actionfailed : true,
7467         /**
7468          * @event actioncomplete
7469          * Fires when an action is completed.
7470          * @param {Form} this
7471          * @param {Action} action The action that completed
7472          */
7473         actioncomplete : true
7474     });
7475
7476 };
7477
7478 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7479
7480      /**
7481      * @cfg {String} method
7482      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7483      */
7484     method : 'POST',
7485     /**
7486      * @cfg {String} url
7487      * The URL to use for form actions if one isn't supplied in the action options.
7488      */
7489     /**
7490      * @cfg {Boolean} fileUpload
7491      * Set to true if this form is a file upload.
7492      */
7493
7494     /**
7495      * @cfg {Object} baseParams
7496      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7497      */
7498
7499     /**
7500      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7501      */
7502     timeout: 30,
7503     /**
7504      * @cfg {Sting} align (left|right) for navbar forms
7505      */
7506     align : 'left',
7507
7508     // private
7509     activeAction : null,
7510
7511     /**
7512      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7513      * element by passing it or its id or mask the form itself by passing in true.
7514      * @type Mixed
7515      */
7516     waitMsgTarget : false,
7517
7518     loadMask : true,
7519     
7520     /**
7521      * @cfg {Boolean} errPopover (true|false) default false
7522      */
7523     errPopover : false,
7524
7525     getAutoCreate : function(){
7526
7527         var cfg = {
7528             tag: 'form',
7529             method : this.method || 'POST',
7530             id : this.id || Roo.id(),
7531             cls : ''
7532         };
7533         if (this.parent().xtype.match(/^Nav/)) {
7534             cfg.cls = 'navbar-form navbar-' + this.align;
7535
7536         }
7537
7538         if (this.labelAlign == 'left' ) {
7539             cfg.cls += ' form-horizontal';
7540         }
7541
7542
7543         return cfg;
7544     },
7545     initEvents : function()
7546     {
7547         this.el.on('submit', this.onSubmit, this);
7548         // this was added as random key presses on the form where triggering form submit.
7549         this.el.on('keypress', function(e) {
7550             if (e.getCharCode() != 13) {
7551                 return true;
7552             }
7553             // we might need to allow it for textareas.. and some other items.
7554             // check e.getTarget().
7555
7556             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7557                 return true;
7558             }
7559
7560             Roo.log("keypress blocked");
7561
7562             e.preventDefault();
7563             return false;
7564         });
7565         
7566         this.errTooltip = new Roo.bootstrap.Tooltip({
7567             cls : 'roo-form-error-popover'
7568         });
7569         
7570         this.errTooltip.render(this.el);
7571         
7572         this.errTooltip.alignment = {
7573             'left' : ['r-l', [-2,0], 'right'],
7574             'right' : ['l-r', [2,0], 'left'],
7575             'bottom' : ['tl-bl', [0,2], 'top'],
7576             'top' : [ 'bl-tl', [0,-2], 'bottom']
7577         };
7578         
7579     },
7580     // private
7581     onSubmit : function(e){
7582         e.stopEvent();
7583     },
7584
7585      /**
7586      * Returns true if client-side validation on the form is successful.
7587      * @return Boolean
7588      */
7589     isValid : function(){
7590         var items = this.getItems();
7591         var valid = true;
7592         var target = false;
7593         
7594         items.each(function(f){
7595             
7596             if(f.validate()){
7597                 return;
7598             }
7599             
7600             valid = false;
7601
7602             if(!target){
7603                 target = f;
7604             }
7605            
7606         });
7607         
7608         if(this.errPopover && !valid){
7609             this.showErrPopover(target);
7610         }
7611         
7612         return valid;
7613     },
7614     
7615     showErrPopover : function(target)
7616     {
7617         if(!this.errPopover){
7618             return;
7619         }
7620         
7621         /*
7622          * Mask the element
7623          */
7624         var oIndex = target.el.getStyle('z-index');
7625         
7626         target.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
7627         
7628         target.el.addClass('roo-invalid-outline');
7629         
7630         target.inputEl().focus();
7631         
7632         /*
7633          * Place the popover
7634          */
7635         this.errTooltip.bindEl = target.el;
7636         
7637         this.errTooltip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
7638         
7639         var tip = target.blankText;
7640         
7641         if(target.getValue() !== '' && target.regexText.length){
7642             tip = target.regexText;
7643         }
7644         
7645         this.errTooltip.show(tip);
7646         
7647         var _this = this;
7648         
7649         var fadeout = function(){
7650             
7651             target.inputEl().un('blur', fadeout);
7652             target.inputEl().un('keyup', fadeout);
7653             
7654             target.el.setStyle('z-index', oIndex);
7655         
7656             target.el.removeClass('roo-invalid-outline');
7657             
7658             _this.errTooltip.hide();
7659             
7660             if(!intervalFadeOut){
7661                 return;
7662             }
7663             
7664             window.clearInterval(intervalFadeOut);
7665             intervalFadeOut = false;
7666                 
7667         }
7668         
7669         target.inputEl().on('blur', fadeout, target);
7670         target.inputEl().on('keyup', fadeout, target);
7671         
7672         if(intervalFadeOut){
7673             window.clearInterval(intervalFadeOut);
7674             intervalFadeOut = false;
7675         }
7676         
7677         var intervalFadeOut =  window.setInterval(function() {
7678             fadeout();
7679         }, 10000);
7680           
7681     },
7682     
7683     /**
7684      * Returns true if any fields in this form have changed since their original load.
7685      * @return Boolean
7686      */
7687     isDirty : function(){
7688         var dirty = false;
7689         var items = this.getItems();
7690         items.each(function(f){
7691            if(f.isDirty()){
7692                dirty = true;
7693                return false;
7694            }
7695            return true;
7696         });
7697         return dirty;
7698     },
7699      /**
7700      * Performs a predefined action (submit or load) or custom actions you define on this form.
7701      * @param {String} actionName The name of the action type
7702      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7703      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7704      * accept other config options):
7705      * <pre>
7706 Property          Type             Description
7707 ----------------  ---------------  ----------------------------------------------------------------------------------
7708 url               String           The url for the action (defaults to the form's url)
7709 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7710 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7711 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7712                                    validate the form on the client (defaults to false)
7713      * </pre>
7714      * @return {BasicForm} this
7715      */
7716     doAction : function(action, options){
7717         if(typeof action == 'string'){
7718             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7719         }
7720         if(this.fireEvent('beforeaction', this, action) !== false){
7721             this.beforeAction(action);
7722             action.run.defer(100, action);
7723         }
7724         return this;
7725     },
7726
7727     // private
7728     beforeAction : function(action){
7729         var o = action.options;
7730
7731         if(this.loadMask){
7732             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7733         }
7734         // not really supported yet.. ??
7735
7736         //if(this.waitMsgTarget === true){
7737         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7738         //}else if(this.waitMsgTarget){
7739         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7740         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7741         //}else {
7742         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7743        // }
7744
7745     },
7746
7747     // private
7748     afterAction : function(action, success){
7749         this.activeAction = null;
7750         var o = action.options;
7751
7752         //if(this.waitMsgTarget === true){
7753             this.el.unmask();
7754         //}else if(this.waitMsgTarget){
7755         //    this.waitMsgTarget.unmask();
7756         //}else{
7757         //    Roo.MessageBox.updateProgress(1);
7758         //    Roo.MessageBox.hide();
7759        // }
7760         //
7761         if(success){
7762             if(o.reset){
7763                 this.reset();
7764             }
7765             Roo.callback(o.success, o.scope, [this, action]);
7766             this.fireEvent('actioncomplete', this, action);
7767
7768         }else{
7769
7770             // failure condition..
7771             // we have a scenario where updates need confirming.
7772             // eg. if a locking scenario exists..
7773             // we look for { errors : { needs_confirm : true }} in the response.
7774             if (
7775                 (typeof(action.result) != 'undefined')  &&
7776                 (typeof(action.result.errors) != 'undefined')  &&
7777                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7778            ){
7779                 var _t = this;
7780                 Roo.log("not supported yet");
7781                  /*
7782
7783                 Roo.MessageBox.confirm(
7784                     "Change requires confirmation",
7785                     action.result.errorMsg,
7786                     function(r) {
7787                         if (r != 'yes') {
7788                             return;
7789                         }
7790                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7791                     }
7792
7793                 );
7794                 */
7795
7796
7797                 return;
7798             }
7799
7800             Roo.callback(o.failure, o.scope, [this, action]);
7801             // show an error message if no failed handler is set..
7802             if (!this.hasListener('actionfailed')) {
7803                 Roo.log("need to add dialog support");
7804                 /*
7805                 Roo.MessageBox.alert("Error",
7806                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7807                         action.result.errorMsg :
7808                         "Saving Failed, please check your entries or try again"
7809                 );
7810                 */
7811             }
7812
7813             this.fireEvent('actionfailed', this, action);
7814         }
7815
7816     },
7817     /**
7818      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7819      * @param {String} id The value to search for
7820      * @return Field
7821      */
7822     findField : function(id){
7823         var items = this.getItems();
7824         var field = items.get(id);
7825         if(!field){
7826              items.each(function(f){
7827                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7828                     field = f;
7829                     return false;
7830                 }
7831                 return true;
7832             });
7833         }
7834         return field || null;
7835     },
7836      /**
7837      * Mark fields in this form invalid in bulk.
7838      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7839      * @return {BasicForm} this
7840      */
7841     markInvalid : function(errors){
7842         if(errors instanceof Array){
7843             for(var i = 0, len = errors.length; i < len; i++){
7844                 var fieldError = errors[i];
7845                 var f = this.findField(fieldError.id);
7846                 if(f){
7847                     f.markInvalid(fieldError.msg);
7848                 }
7849             }
7850         }else{
7851             var field, id;
7852             for(id in errors){
7853                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7854                     field.markInvalid(errors[id]);
7855                 }
7856             }
7857         }
7858         //Roo.each(this.childForms || [], function (f) {
7859         //    f.markInvalid(errors);
7860         //});
7861
7862         return this;
7863     },
7864
7865     /**
7866      * Set values for fields in this form in bulk.
7867      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7868      * @return {BasicForm} this
7869      */
7870     setValues : function(values){
7871         if(values instanceof Array){ // array of objects
7872             for(var i = 0, len = values.length; i < len; i++){
7873                 var v = values[i];
7874                 var f = this.findField(v.id);
7875                 if(f){
7876                     f.setValue(v.value);
7877                     if(this.trackResetOnLoad){
7878                         f.originalValue = f.getValue();
7879                     }
7880                 }
7881             }
7882         }else{ // object hash
7883             var field, id;
7884             for(id in values){
7885                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7886
7887                     if (field.setFromData &&
7888                         field.valueField &&
7889                         field.displayField &&
7890                         // combos' with local stores can
7891                         // be queried via setValue()
7892                         // to set their value..
7893                         (field.store && !field.store.isLocal)
7894                         ) {
7895                         // it's a combo
7896                         var sd = { };
7897                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7898                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7899                         field.setFromData(sd);
7900
7901                     } else {
7902                         field.setValue(values[id]);
7903                     }
7904
7905
7906                     if(this.trackResetOnLoad){
7907                         field.originalValue = field.getValue();
7908                     }
7909                 }
7910             }
7911         }
7912
7913         //Roo.each(this.childForms || [], function (f) {
7914         //    f.setValues(values);
7915         //});
7916
7917         return this;
7918     },
7919
7920     /**
7921      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7922      * they are returned as an array.
7923      * @param {Boolean} asString
7924      * @return {Object}
7925      */
7926     getValues : function(asString){
7927         //if (this.childForms) {
7928             // copy values from the child forms
7929         //    Roo.each(this.childForms, function (f) {
7930         //        this.setValues(f.getValues());
7931         //    }, this);
7932         //}
7933
7934
7935
7936         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7937         if(asString === true){
7938             return fs;
7939         }
7940         return Roo.urlDecode(fs);
7941     },
7942
7943     /**
7944      * Returns the fields in this form as an object with key/value pairs.
7945      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7946      * @return {Object}
7947      */
7948     getFieldValues : function(with_hidden)
7949     {
7950         var items = this.getItems();
7951         var ret = {};
7952         items.each(function(f){
7953             if (!f.getName()) {
7954                 return;
7955             }
7956             var v = f.getValue();
7957             if (f.inputType =='radio') {
7958                 if (typeof(ret[f.getName()]) == 'undefined') {
7959                     ret[f.getName()] = ''; // empty..
7960                 }
7961
7962                 if (!f.el.dom.checked) {
7963                     return;
7964
7965                 }
7966                 v = f.el.dom.value;
7967
7968             }
7969
7970             // not sure if this supported any more..
7971             if ((typeof(v) == 'object') && f.getRawValue) {
7972                 v = f.getRawValue() ; // dates..
7973             }
7974             // combo boxes where name != hiddenName...
7975             if (f.name != f.getName()) {
7976                 ret[f.name] = f.getRawValue();
7977             }
7978             ret[f.getName()] = v;
7979         });
7980
7981         return ret;
7982     },
7983
7984     /**
7985      * Clears all invalid messages in this form.
7986      * @return {BasicForm} this
7987      */
7988     clearInvalid : function(){
7989         var items = this.getItems();
7990
7991         items.each(function(f){
7992            f.clearInvalid();
7993         });
7994
7995
7996
7997         return this;
7998     },
7999
8000     /**
8001      * Resets this form.
8002      * @return {BasicForm} this
8003      */
8004     reset : function(){
8005         var items = this.getItems();
8006         items.each(function(f){
8007             f.reset();
8008         });
8009
8010         Roo.each(this.childForms || [], function (f) {
8011             f.reset();
8012         });
8013
8014
8015         return this;
8016     },
8017     getItems : function()
8018     {
8019         var r=new Roo.util.MixedCollection(false, function(o){
8020             return o.id || (o.id = Roo.id());
8021         });
8022         var iter = function(el) {
8023             if (el.inputEl) {
8024                 r.add(el);
8025             }
8026             if (!el.items) {
8027                 return;
8028             }
8029             Roo.each(el.items,function(e) {
8030                 iter(e);
8031             });
8032
8033
8034         };
8035
8036         iter(this);
8037         return r;
8038
8039
8040
8041
8042     }
8043
8044 });
8045 /*
8046  * Based on:
8047  * Ext JS Library 1.1.1
8048  * Copyright(c) 2006-2007, Ext JS, LLC.
8049  *
8050  * Originally Released Under LGPL - original licence link has changed is not relivant.
8051  *
8052  * Fork - LGPL
8053  * <script type="text/javascript">
8054  */
8055 /**
8056  * @class Roo.form.VTypes
8057  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8058  * @singleton
8059  */
8060 Roo.form.VTypes = function(){
8061     // closure these in so they are only created once.
8062     var alpha = /^[a-zA-Z_]+$/;
8063     var alphanum = /^[a-zA-Z0-9_]+$/;
8064     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8065     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8066
8067     // All these messages and functions are configurable
8068     return {
8069         /**
8070          * The function used to validate email addresses
8071          * @param {String} value The email address
8072          */
8073         'email' : function(v){
8074             return email.test(v);
8075         },
8076         /**
8077          * The error text to display when the email validation function returns false
8078          * @type String
8079          */
8080         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8081         /**
8082          * The keystroke filter mask to be applied on email input
8083          * @type RegExp
8084          */
8085         'emailMask' : /[a-z0-9_\.\-@]/i,
8086
8087         /**
8088          * The function used to validate URLs
8089          * @param {String} value The URL
8090          */
8091         'url' : function(v){
8092             return url.test(v);
8093         },
8094         /**
8095          * The error text to display when the url validation function returns false
8096          * @type String
8097          */
8098         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8099         
8100         /**
8101          * The function used to validate alpha values
8102          * @param {String} value The value
8103          */
8104         'alpha' : function(v){
8105             return alpha.test(v);
8106         },
8107         /**
8108          * The error text to display when the alpha validation function returns false
8109          * @type String
8110          */
8111         'alphaText' : 'This field should only contain letters and _',
8112         /**
8113          * The keystroke filter mask to be applied on alpha input
8114          * @type RegExp
8115          */
8116         'alphaMask' : /[a-z_]/i,
8117
8118         /**
8119          * The function used to validate alphanumeric values
8120          * @param {String} value The value
8121          */
8122         'alphanum' : function(v){
8123             return alphanum.test(v);
8124         },
8125         /**
8126          * The error text to display when the alphanumeric validation function returns false
8127          * @type String
8128          */
8129         'alphanumText' : 'This field should only contain letters, numbers and _',
8130         /**
8131          * The keystroke filter mask to be applied on alphanumeric input
8132          * @type RegExp
8133          */
8134         'alphanumMask' : /[a-z0-9_]/i
8135     };
8136 }();/*
8137  * - LGPL
8138  *
8139  * Input
8140  * 
8141  */
8142
8143 /**
8144  * @class Roo.bootstrap.Input
8145  * @extends Roo.bootstrap.Component
8146  * Bootstrap Input class
8147  * @cfg {Boolean} disabled is it disabled
8148  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8149  * @cfg {String} name name of the input
8150  * @cfg {string} fieldLabel - the label associated
8151  * @cfg {string} placeholder - placeholder to put in text.
8152  * @cfg {string}  before - input group add on before
8153  * @cfg {string} after - input group add on after
8154  * @cfg {string} size - (lg|sm) or leave empty..
8155  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8156  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8157  * @cfg {Number} md colspan out of 12 for computer-sized screens
8158  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8159  * @cfg {string} value default value of the input
8160  * @cfg {Number} labelWidth set the width of label (0-12)
8161  * @cfg {String} labelAlign (top|left)
8162  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8163  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8164  * @cfg {String} indicatorpos (left|right) default left
8165
8166  * @cfg {String} align (left|center|right) Default left
8167  * @cfg {Boolean} forceFeedback (true|false) Default false
8168  * 
8169  * 
8170  * 
8171  * 
8172  * @constructor
8173  * Create a new Input
8174  * @param {Object} config The config object
8175  */
8176
8177 Roo.bootstrap.Input = function(config){
8178     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8179    
8180         this.addEvents({
8181             /**
8182              * @event focus
8183              * Fires when this field receives input focus.
8184              * @param {Roo.form.Field} this
8185              */
8186             focus : true,
8187             /**
8188              * @event blur
8189              * Fires when this field loses input focus.
8190              * @param {Roo.form.Field} this
8191              */
8192             blur : true,
8193             /**
8194              * @event specialkey
8195              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8196              * {@link Roo.EventObject#getKey} to determine which key was pressed.
8197              * @param {Roo.form.Field} this
8198              * @param {Roo.EventObject} e The event object
8199              */
8200             specialkey : true,
8201             /**
8202              * @event change
8203              * Fires just before the field blurs if the field value has changed.
8204              * @param {Roo.form.Field} this
8205              * @param {Mixed} newValue The new value
8206              * @param {Mixed} oldValue The original value
8207              */
8208             change : true,
8209             /**
8210              * @event invalid
8211              * Fires after the field has been marked as invalid.
8212              * @param {Roo.form.Field} this
8213              * @param {String} msg The validation message
8214              */
8215             invalid : true,
8216             /**
8217              * @event valid
8218              * Fires after the field has been validated with no errors.
8219              * @param {Roo.form.Field} this
8220              */
8221             valid : true,
8222              /**
8223              * @event keyup
8224              * Fires after the key up
8225              * @param {Roo.form.Field} this
8226              * @param {Roo.EventObject}  e The event Object
8227              */
8228             keyup : true
8229         });
8230 };
8231
8232 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8233      /**
8234      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8235       automatic validation (defaults to "keyup").
8236      */
8237     validationEvent : "keyup",
8238      /**
8239      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8240      */
8241     validateOnBlur : true,
8242     /**
8243      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8244      */
8245     validationDelay : 250,
8246      /**
8247      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8248      */
8249     focusClass : "x-form-focus",  // not needed???
8250     
8251        
8252     /**
8253      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8254      */
8255     invalidClass : "has-warning",
8256     
8257     /**
8258      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8259      */
8260     validClass : "has-success",
8261     
8262     /**
8263      * @cfg {Boolean} hasFeedback (true|false) default true
8264      */
8265     hasFeedback : true,
8266     
8267     /**
8268      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8269      */
8270     invalidFeedbackClass : "glyphicon-warning-sign",
8271     
8272     /**
8273      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8274      */
8275     validFeedbackClass : "glyphicon-ok",
8276     
8277     /**
8278      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8279      */
8280     selectOnFocus : false,
8281     
8282      /**
8283      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8284      */
8285     maskRe : null,
8286        /**
8287      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8288      */
8289     vtype : null,
8290     
8291       /**
8292      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8293      */
8294     disableKeyFilter : false,
8295     
8296        /**
8297      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8298      */
8299     disabled : false,
8300      /**
8301      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8302      */
8303     allowBlank : true,
8304     /**
8305      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8306      */
8307     blankText : "This field is required",
8308     
8309      /**
8310      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8311      */
8312     minLength : 0,
8313     /**
8314      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8315      */
8316     maxLength : Number.MAX_VALUE,
8317     /**
8318      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8319      */
8320     minLengthText : "The minimum length for this field is {0}",
8321     /**
8322      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8323      */
8324     maxLengthText : "The maximum length for this field is {0}",
8325   
8326     
8327     /**
8328      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8329      * If available, this function will be called only after the basic validators all return true, and will be passed the
8330      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8331      */
8332     validator : null,
8333     /**
8334      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8335      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8336      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8337      */
8338     regex : null,
8339     /**
8340      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8341      */
8342     regexText : "",
8343     
8344     autocomplete: false,
8345     
8346     
8347     fieldLabel : '',
8348     inputType : 'text',
8349     
8350     name : false,
8351     placeholder: false,
8352     before : false,
8353     after : false,
8354     size : false,
8355     hasFocus : false,
8356     preventMark: false,
8357     isFormField : true,
8358     value : '',
8359     labelWidth : 2,
8360     labelAlign : false,
8361     readOnly : false,
8362     align : false,
8363     formatedValue : false,
8364     forceFeedback : false,
8365     
8366     indicatorpos : 'left',
8367     
8368     parentLabelAlign : function()
8369     {
8370         var parent = this;
8371         while (parent.parent()) {
8372             parent = parent.parent();
8373             if (typeof(parent.labelAlign) !='undefined') {
8374                 return parent.labelAlign;
8375             }
8376         }
8377         return 'left';
8378         
8379     },
8380     
8381     getAutoCreate : function()
8382     {
8383         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8384         
8385         var id = Roo.id();
8386         
8387         var cfg = {};
8388         
8389         if(this.inputType != 'hidden'){
8390             cfg.cls = 'form-group' //input-group
8391         }
8392         
8393         var input =  {
8394             tag: 'input',
8395             id : id,
8396             type : this.inputType,
8397             value : this.value,
8398             cls : 'form-control',
8399             placeholder : this.placeholder || '',
8400             autocomplete : this.autocomplete || 'new-password'
8401         };
8402         
8403         if(this.align){
8404             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8405         }
8406         
8407         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8408             input.maxLength = this.maxLength;
8409         }
8410         
8411         if (this.disabled) {
8412             input.disabled=true;
8413         }
8414         
8415         if (this.readOnly) {
8416             input.readonly=true;
8417         }
8418         
8419         if (this.name) {
8420             input.name = this.name;
8421         }
8422         
8423         if (this.size) {
8424             input.cls += ' input-' + this.size;
8425         }
8426         
8427         var settings=this;
8428         ['xs','sm','md','lg'].map(function(size){
8429             if (settings[size]) {
8430                 cfg.cls += ' col-' + size + '-' + settings[size];
8431             }
8432         });
8433         
8434         var inputblock = input;
8435         
8436         var feedback = {
8437             tag: 'span',
8438             cls: 'glyphicon form-control-feedback'
8439         };
8440             
8441         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8442             
8443             inputblock = {
8444                 cls : 'has-feedback',
8445                 cn :  [
8446                     input,
8447                     feedback
8448                 ] 
8449             };  
8450         }
8451         
8452         if (this.before || this.after) {
8453             
8454             inputblock = {
8455                 cls : 'input-group',
8456                 cn :  [] 
8457             };
8458             
8459             if (this.before && typeof(this.before) == 'string') {
8460                 
8461                 inputblock.cn.push({
8462                     tag :'span',
8463                     cls : 'roo-input-before input-group-addon',
8464                     html : this.before
8465                 });
8466             }
8467             if (this.before && typeof(this.before) == 'object') {
8468                 this.before = Roo.factory(this.before);
8469                 
8470                 inputblock.cn.push({
8471                     tag :'span',
8472                     cls : 'roo-input-before input-group-' +
8473                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8474                 });
8475             }
8476             
8477             inputblock.cn.push(input);
8478             
8479             if (this.after && typeof(this.after) == 'string') {
8480                 inputblock.cn.push({
8481                     tag :'span',
8482                     cls : 'roo-input-after input-group-addon',
8483                     html : this.after
8484                 });
8485             }
8486             if (this.after && typeof(this.after) == 'object') {
8487                 this.after = Roo.factory(this.after);
8488                 
8489                 inputblock.cn.push({
8490                     tag :'span',
8491                     cls : 'roo-input-after input-group-' +
8492                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8493                 });
8494             }
8495             
8496             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8497                 inputblock.cls += ' has-feedback';
8498                 inputblock.cn.push(feedback);
8499             }
8500         };
8501         
8502         if (align ==='left' && this.fieldLabel.length) {
8503             
8504             cfg.cn = [
8505                 {
8506                     tag : 'i',
8507                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8508                     tooltip : 'This field is required'
8509                 },
8510                 {
8511                     tag: 'label',
8512                     'for' :  id,
8513                     cls : 'control-label col-sm-' + this.labelWidth,
8514                     html : this.fieldLabel
8515
8516                 },
8517                 {
8518                     cls : "col-sm-" + (12 - this.labelWidth), 
8519                     cn: [
8520                         inputblock
8521                     ]
8522                 }
8523
8524             ];
8525             
8526             if(this.indicatorpos == 'right'){
8527                 cfg.cn = [
8528                     {
8529                         tag: 'label',
8530                         'for' :  id,
8531                         cls : 'control-label col-sm-' + this.labelWidth,
8532                         html : this.fieldLabel
8533
8534                     },
8535                     {
8536                         tag : 'i',
8537                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8538                         tooltip : 'This field is required'
8539                     },
8540                     {
8541                         cls : "col-sm-" + (12 - this.labelWidth), 
8542                         cn: [
8543                             inputblock
8544                         ]
8545                     }
8546
8547                 ];
8548             }
8549             
8550         } else if ( this.fieldLabel.length) {
8551                 
8552             cfg.cn = [
8553                 {
8554                     tag : 'i',
8555                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8556                     tooltip : 'This field is required'
8557                 },
8558                 {
8559                     tag: 'label',
8560                    //cls : 'input-group-addon',
8561                     html : this.fieldLabel
8562
8563                 },
8564
8565                inputblock
8566
8567            ];
8568            
8569            if(this.indicatorpos == 'right'){
8570                 
8571                 cfg.cn = [
8572                     {
8573                         tag: 'label',
8574                        //cls : 'input-group-addon',
8575                         html : this.fieldLabel
8576
8577                     },
8578                     {
8579                         tag : 'i',
8580                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8581                         tooltip : 'This field is required'
8582                     },
8583
8584                    inputblock
8585
8586                ];
8587
8588             }
8589
8590         } else {
8591             
8592             cfg.cn = [
8593
8594                     inputblock
8595
8596             ];
8597                 
8598                 
8599         };
8600         
8601         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8602            cfg.cls += ' navbar-form';
8603         }
8604         
8605         if (this.parentType === 'NavGroup') {
8606            cfg.cls += ' navbar-form';
8607            cfg.tag = 'li';
8608         }
8609         
8610         return cfg;
8611         
8612     },
8613     /**
8614      * return the real input element.
8615      */
8616     inputEl: function ()
8617     {
8618         return this.el.select('input.form-control',true).first();
8619     },
8620     
8621     tooltipEl : function()
8622     {
8623         return this.inputEl();
8624     },
8625     
8626     indicatorEl : function()
8627     {
8628         var indicator = this.el.select('i.roo-required-indicator',true).first();
8629         
8630         if(!indicator){
8631             return false;
8632         }
8633         
8634         return indicator;
8635         
8636     },
8637     
8638     setDisabled : function(v)
8639     {
8640         var i  = this.inputEl().dom;
8641         if (!v) {
8642             i.removeAttribute('disabled');
8643             return;
8644             
8645         }
8646         i.setAttribute('disabled','true');
8647     },
8648     initEvents : function()
8649     {
8650           
8651         this.inputEl().on("keydown" , this.fireKey,  this);
8652         this.inputEl().on("focus", this.onFocus,  this);
8653         this.inputEl().on("blur", this.onBlur,  this);
8654         
8655         this.inputEl().relayEvent('keyup', this);
8656         
8657         this.indicator = this.indicatorEl();
8658         
8659         if(this.indicator){
8660             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8661             this.indicator.hide();
8662         }
8663  
8664         // reference to original value for reset
8665         this.originalValue = this.getValue();
8666         //Roo.form.TextField.superclass.initEvents.call(this);
8667         if(this.validationEvent == 'keyup'){
8668             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8669             this.inputEl().on('keyup', this.filterValidation, this);
8670         }
8671         else if(this.validationEvent !== false){
8672             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8673         }
8674         
8675         if(this.selectOnFocus){
8676             this.on("focus", this.preFocus, this);
8677             
8678         }
8679         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8680             this.inputEl().on("keypress", this.filterKeys, this);
8681         } else {
8682             this.inputEl().relayEvent('keypress', this);
8683         }
8684        /* if(this.grow){
8685             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8686             this.el.on("click", this.autoSize,  this);
8687         }
8688         */
8689         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8690             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8691         }
8692         
8693         if (typeof(this.before) == 'object') {
8694             this.before.render(this.el.select('.roo-input-before',true).first());
8695         }
8696         if (typeof(this.after) == 'object') {
8697             this.after.render(this.el.select('.roo-input-after',true).first());
8698         }
8699         
8700         
8701     },
8702     filterValidation : function(e){
8703         if(!e.isNavKeyPress()){
8704             this.validationTask.delay(this.validationDelay);
8705         }
8706     },
8707      /**
8708      * Validates the field value
8709      * @return {Boolean} True if the value is valid, else false
8710      */
8711     validate : function(){
8712         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8713         if(this.disabled || this.validateValue(this.getRawValue())){
8714             this.markValid();
8715             return true;
8716         }
8717         
8718         this.markInvalid();
8719         return false;
8720     },
8721     
8722     
8723     /**
8724      * Validates a value according to the field's validation rules and marks the field as invalid
8725      * if the validation fails
8726      * @param {Mixed} value The value to validate
8727      * @return {Boolean} True if the value is valid, else false
8728      */
8729     validateValue : function(value){
8730         if(value.length < 1)  { // if it's blank
8731             if(this.allowBlank){
8732                 return true;
8733             }
8734             return false;
8735         }
8736         
8737         if(value.length < this.minLength){
8738             return false;
8739         }
8740         if(value.length > this.maxLength){
8741             return false;
8742         }
8743         if(this.vtype){
8744             var vt = Roo.form.VTypes;
8745             if(!vt[this.vtype](value, this)){
8746                 return false;
8747             }
8748         }
8749         if(typeof this.validator == "function"){
8750             var msg = this.validator(value);
8751             if(msg !== true){
8752                 return false;
8753             }
8754         }
8755         
8756         if(this.regex && !this.regex.test(value)){
8757             return false;
8758         }
8759         
8760         return true;
8761     },
8762
8763     
8764     
8765      // private
8766     fireKey : function(e){
8767         //Roo.log('field ' + e.getKey());
8768         if(e.isNavKeyPress()){
8769             this.fireEvent("specialkey", this, e);
8770         }
8771     },
8772     focus : function (selectText){
8773         if(this.rendered){
8774             this.inputEl().focus();
8775             if(selectText === true){
8776                 this.inputEl().dom.select();
8777             }
8778         }
8779         return this;
8780     } ,
8781     
8782     onFocus : function(){
8783         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8784            // this.el.addClass(this.focusClass);
8785         }
8786         if(!this.hasFocus){
8787             this.hasFocus = true;
8788             this.startValue = this.getValue();
8789             this.fireEvent("focus", this);
8790         }
8791     },
8792     
8793     beforeBlur : Roo.emptyFn,
8794
8795     
8796     // private
8797     onBlur : function(){
8798         this.beforeBlur();
8799         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8800             //this.el.removeClass(this.focusClass);
8801         }
8802         this.hasFocus = false;
8803         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8804             this.validate();
8805         }
8806         var v = this.getValue();
8807         if(String(v) !== String(this.startValue)){
8808             this.fireEvent('change', this, v, this.startValue);
8809         }
8810         this.fireEvent("blur", this);
8811     },
8812     
8813     /**
8814      * Resets the current field value to the originally loaded value and clears any validation messages
8815      */
8816     reset : function(){
8817         this.setValue(this.originalValue);
8818         this.validate();
8819     },
8820      /**
8821      * Returns the name of the field
8822      * @return {Mixed} name The name field
8823      */
8824     getName: function(){
8825         return this.name;
8826     },
8827      /**
8828      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8829      * @return {Mixed} value The field value
8830      */
8831     getValue : function(){
8832         
8833         var v = this.inputEl().getValue();
8834         
8835         return v;
8836     },
8837     /**
8838      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8839      * @return {Mixed} value The field value
8840      */
8841     getRawValue : function(){
8842         var v = this.inputEl().getValue();
8843         
8844         return v;
8845     },
8846     
8847     /**
8848      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8849      * @param {Mixed} value The value to set
8850      */
8851     setRawValue : function(v){
8852         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8853     },
8854     
8855     selectText : function(start, end){
8856         var v = this.getRawValue();
8857         if(v.length > 0){
8858             start = start === undefined ? 0 : start;
8859             end = end === undefined ? v.length : end;
8860             var d = this.inputEl().dom;
8861             if(d.setSelectionRange){
8862                 d.setSelectionRange(start, end);
8863             }else if(d.createTextRange){
8864                 var range = d.createTextRange();
8865                 range.moveStart("character", start);
8866                 range.moveEnd("character", v.length-end);
8867                 range.select();
8868             }
8869         }
8870     },
8871     
8872     /**
8873      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8874      * @param {Mixed} value The value to set
8875      */
8876     setValue : function(v){
8877         this.value = v;
8878         if(this.rendered){
8879             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8880             this.validate();
8881         }
8882     },
8883     
8884     /*
8885     processValue : function(value){
8886         if(this.stripCharsRe){
8887             var newValue = value.replace(this.stripCharsRe, '');
8888             if(newValue !== value){
8889                 this.setRawValue(newValue);
8890                 return newValue;
8891             }
8892         }
8893         return value;
8894     },
8895   */
8896     preFocus : function(){
8897         
8898         if(this.selectOnFocus){
8899             this.inputEl().dom.select();
8900         }
8901     },
8902     filterKeys : function(e){
8903         var k = e.getKey();
8904         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8905             return;
8906         }
8907         var c = e.getCharCode(), cc = String.fromCharCode(c);
8908         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8909             return;
8910         }
8911         if(!this.maskRe.test(cc)){
8912             e.stopEvent();
8913         }
8914     },
8915      /**
8916      * Clear any invalid styles/messages for this field
8917      */
8918     clearInvalid : function(){
8919         
8920         if(!this.el || this.preventMark){ // not rendered
8921             return;
8922         }
8923         
8924         if(this.indicator){
8925             this.indicator.hide();
8926         }
8927         
8928         this.el.removeClass(this.invalidClass);
8929         
8930         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8931             
8932             var feedback = this.el.select('.form-control-feedback', true).first();
8933             
8934             if(feedback){
8935                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8936             }
8937             
8938         }
8939         
8940         this.fireEvent('valid', this);
8941     },
8942     
8943      /**
8944      * Mark this field as valid
8945      */
8946     markValid : function()
8947     {
8948         if(!this.el  || this.preventMark){ // not rendered
8949             return;
8950         }
8951         
8952         this.el.removeClass([this.invalidClass, this.validClass]);
8953         
8954         var feedback = this.el.select('.form-control-feedback', true).first();
8955             
8956         if(feedback){
8957             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8958         }
8959
8960         if(this.disabled){
8961             return;
8962         }
8963         
8964         if(this.allowBlank && !this.getRawValue().length){
8965             return;
8966         }
8967         
8968         if(this.indicator){
8969             this.indicator.hide();
8970         }
8971         
8972         this.el.addClass(this.validClass);
8973         
8974         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8975             
8976             var feedback = this.el.select('.form-control-feedback', true).first();
8977             
8978             if(feedback){
8979                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8980                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8981             }
8982             
8983         }
8984         
8985         this.fireEvent('valid', this);
8986     },
8987     
8988      /**
8989      * Mark this field as invalid
8990      * @param {String} msg The validation message
8991      */
8992     markInvalid : function(msg)
8993     {
8994         if(!this.el  || this.preventMark){ // not rendered
8995             return;
8996         }
8997         
8998         this.el.removeClass([this.invalidClass, this.validClass]);
8999         
9000         var feedback = this.el.select('.form-control-feedback', true).first();
9001             
9002         if(feedback){
9003             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9004         }
9005
9006         if(this.disabled){
9007             return;
9008         }
9009         
9010         if(this.allowBlank && !this.getRawValue().length){
9011             return;
9012         }
9013         
9014         if(this.indicator){
9015             this.indicator.show();
9016         }
9017         
9018         this.el.addClass(this.invalidClass);
9019         
9020         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9021             
9022             var feedback = this.el.select('.form-control-feedback', true).first();
9023             
9024             if(feedback){
9025                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9026                 
9027                 if(this.getValue().length || this.forceFeedback){
9028                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9029                 }
9030                 
9031             }
9032             
9033         }
9034         
9035         this.fireEvent('invalid', this, msg);
9036     },
9037     // private
9038     SafariOnKeyDown : function(event)
9039     {
9040         // this is a workaround for a password hang bug on chrome/ webkit.
9041         if (this.inputEl().dom.type != 'password') {
9042             return;
9043         }
9044         
9045         var isSelectAll = false;
9046         
9047         if(this.inputEl().dom.selectionEnd > 0){
9048             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9049         }
9050         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9051             event.preventDefault();
9052             this.setValue('');
9053             return;
9054         }
9055         
9056         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9057             
9058             event.preventDefault();
9059             // this is very hacky as keydown always get's upper case.
9060             //
9061             var cc = String.fromCharCode(event.getCharCode());
9062             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9063             
9064         }
9065     },
9066     adjustWidth : function(tag, w){
9067         tag = tag.toLowerCase();
9068         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9069             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9070                 if(tag == 'input'){
9071                     return w + 2;
9072                 }
9073                 if(tag == 'textarea'){
9074                     return w-2;
9075                 }
9076             }else if(Roo.isOpera){
9077                 if(tag == 'input'){
9078                     return w + 2;
9079                 }
9080                 if(tag == 'textarea'){
9081                     return w-2;
9082                 }
9083             }
9084         }
9085         return w;
9086     }
9087     
9088 });
9089
9090  
9091 /*
9092  * - LGPL
9093  *
9094  * Input
9095  * 
9096  */
9097
9098 /**
9099  * @class Roo.bootstrap.TextArea
9100  * @extends Roo.bootstrap.Input
9101  * Bootstrap TextArea class
9102  * @cfg {Number} cols Specifies the visible width of a text area
9103  * @cfg {Number} rows Specifies the visible number of lines in a text area
9104  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9105  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9106  * @cfg {string} html text
9107  * 
9108  * @constructor
9109  * Create a new TextArea
9110  * @param {Object} config The config object
9111  */
9112
9113 Roo.bootstrap.TextArea = function(config){
9114     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9115    
9116 };
9117
9118 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9119      
9120     cols : false,
9121     rows : 5,
9122     readOnly : false,
9123     warp : 'soft',
9124     resize : false,
9125     value: false,
9126     html: false,
9127     
9128     getAutoCreate : function(){
9129         
9130         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9131         
9132         var id = Roo.id();
9133         
9134         var cfg = {};
9135         
9136         var input =  {
9137             tag: 'textarea',
9138             id : id,
9139             warp : this.warp,
9140             rows : this.rows,
9141             value : this.value || '',
9142             html: this.html || '',
9143             cls : 'form-control',
9144             placeholder : this.placeholder || '' 
9145             
9146         };
9147         
9148         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9149             input.maxLength = this.maxLength;
9150         }
9151         
9152         if(this.resize){
9153             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9154         }
9155         
9156         if(this.cols){
9157             input.cols = this.cols;
9158         }
9159         
9160         if (this.readOnly) {
9161             input.readonly = true;
9162         }
9163         
9164         if (this.name) {
9165             input.name = this.name;
9166         }
9167         
9168         if (this.size) {
9169             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9170         }
9171         
9172         var settings=this;
9173         ['xs','sm','md','lg'].map(function(size){
9174             if (settings[size]) {
9175                 cfg.cls += ' col-' + size + '-' + settings[size];
9176             }
9177         });
9178         
9179         var inputblock = input;
9180         
9181         if(this.hasFeedback && !this.allowBlank){
9182             
9183             var feedback = {
9184                 tag: 'span',
9185                 cls: 'glyphicon form-control-feedback'
9186             };
9187
9188             inputblock = {
9189                 cls : 'has-feedback',
9190                 cn :  [
9191                     input,
9192                     feedback
9193                 ] 
9194             };  
9195         }
9196         
9197         
9198         if (this.before || this.after) {
9199             
9200             inputblock = {
9201                 cls : 'input-group',
9202                 cn :  [] 
9203             };
9204             if (this.before) {
9205                 inputblock.cn.push({
9206                     tag :'span',
9207                     cls : 'input-group-addon',
9208                     html : this.before
9209                 });
9210             }
9211             
9212             inputblock.cn.push(input);
9213             
9214             if(this.hasFeedback && !this.allowBlank){
9215                 inputblock.cls += ' has-feedback';
9216                 inputblock.cn.push(feedback);
9217             }
9218             
9219             if (this.after) {
9220                 inputblock.cn.push({
9221                     tag :'span',
9222                     cls : 'input-group-addon',
9223                     html : this.after
9224                 });
9225             }
9226             
9227         }
9228         
9229         if (align ==='left' && this.fieldLabel.length) {
9230 //                Roo.log("left and has label");
9231                 cfg.cn = [
9232                     
9233                     {
9234                         tag: 'label',
9235                         'for' :  id,
9236                         cls : 'control-label col-sm-' + this.labelWidth,
9237                         html : this.fieldLabel
9238                         
9239                     },
9240                     {
9241                         cls : "col-sm-" + (12 - this.labelWidth), 
9242                         cn: [
9243                             inputblock
9244                         ]
9245                     }
9246                     
9247                 ];
9248         } else if ( this.fieldLabel.length) {
9249 //                Roo.log(" label");
9250                  cfg.cn = [
9251                    
9252                     {
9253                         tag: 'label',
9254                         //cls : 'input-group-addon',
9255                         html : this.fieldLabel
9256                         
9257                     },
9258                     
9259                     inputblock
9260                     
9261                 ];
9262
9263         } else {
9264             
9265 //                   Roo.log(" no label && no align");
9266                 cfg.cn = [
9267                     
9268                         inputblock
9269                     
9270                 ];
9271                 
9272                 
9273         }
9274         
9275         if (this.disabled) {
9276             input.disabled=true;
9277         }
9278         
9279         return cfg;
9280         
9281     },
9282     /**
9283      * return the real textarea element.
9284      */
9285     inputEl: function ()
9286     {
9287         return this.el.select('textarea.form-control',true).first();
9288     },
9289     
9290     /**
9291      * Clear any invalid styles/messages for this field
9292      */
9293     clearInvalid : function()
9294     {
9295         
9296         if(!this.el || this.preventMark){ // not rendered
9297             return;
9298         }
9299         
9300         var label = this.el.select('label', true).first();
9301         var icon = this.el.select('i.fa-star', true).first();
9302         
9303         if(label && icon){
9304             icon.remove();
9305         }
9306         
9307         this.el.removeClass(this.invalidClass);
9308         
9309         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9310             
9311             var feedback = this.el.select('.form-control-feedback', true).first();
9312             
9313             if(feedback){
9314                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9315             }
9316             
9317         }
9318         
9319         this.fireEvent('valid', this);
9320     },
9321     
9322      /**
9323      * Mark this field as valid
9324      */
9325     markValid : function()
9326     {
9327         if(!this.el  || this.preventMark){ // not rendered
9328             return;
9329         }
9330         
9331         this.el.removeClass([this.invalidClass, this.validClass]);
9332         
9333         var feedback = this.el.select('.form-control-feedback', true).first();
9334             
9335         if(feedback){
9336             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9337         }
9338
9339         if(this.disabled || this.allowBlank){
9340             return;
9341         }
9342         
9343         var label = this.el.select('label', true).first();
9344         var icon = this.el.select('i.fa-star', true).first();
9345         
9346         if(label && icon){
9347             icon.remove();
9348         }
9349         
9350         this.el.addClass(this.validClass);
9351         
9352         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9353             
9354             var feedback = this.el.select('.form-control-feedback', true).first();
9355             
9356             if(feedback){
9357                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9358                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9359             }
9360             
9361         }
9362         
9363         this.fireEvent('valid', this);
9364     },
9365     
9366      /**
9367      * Mark this field as invalid
9368      * @param {String} msg The validation message
9369      */
9370     markInvalid : function(msg)
9371     {
9372         if(!this.el  || this.preventMark){ // not rendered
9373             return;
9374         }
9375         
9376         this.el.removeClass([this.invalidClass, this.validClass]);
9377         
9378         var feedback = this.el.select('.form-control-feedback', true).first();
9379             
9380         if(feedback){
9381             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9382         }
9383
9384         if(this.disabled || this.allowBlank){
9385             return;
9386         }
9387         
9388         var label = this.el.select('label', true).first();
9389         var icon = this.el.select('i.fa-star', true).first();
9390         
9391         if(!this.getValue().length && label && !icon){
9392             this.el.createChild({
9393                 tag : 'i',
9394                 cls : 'text-danger fa fa-lg fa-star',
9395                 tooltip : 'This field is required',
9396                 style : 'margin-right:5px;'
9397             }, label, true);
9398         }
9399
9400         this.el.addClass(this.invalidClass);
9401         
9402         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9403             
9404             var feedback = this.el.select('.form-control-feedback', true).first();
9405             
9406             if(feedback){
9407                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9408                 
9409                 if(this.getValue().length || this.forceFeedback){
9410                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9411                 }
9412                 
9413             }
9414             
9415         }
9416         
9417         this.fireEvent('invalid', this, msg);
9418     }
9419 });
9420
9421  
9422 /*
9423  * - LGPL
9424  *
9425  * trigger field - base class for combo..
9426  * 
9427  */
9428  
9429 /**
9430  * @class Roo.bootstrap.TriggerField
9431  * @extends Roo.bootstrap.Input
9432  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9433  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9434  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9435  * for which you can provide a custom implementation.  For example:
9436  * <pre><code>
9437 var trigger = new Roo.bootstrap.TriggerField();
9438 trigger.onTriggerClick = myTriggerFn;
9439 trigger.applyTo('my-field');
9440 </code></pre>
9441  *
9442  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9443  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9444  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9445  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9446  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9447
9448  * @constructor
9449  * Create a new TriggerField.
9450  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9451  * to the base TextField)
9452  */
9453 Roo.bootstrap.TriggerField = function(config){
9454     this.mimicing = false;
9455     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9456 };
9457
9458 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9459     /**
9460      * @cfg {String} triggerClass A CSS class to apply to the trigger
9461      */
9462      /**
9463      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9464      */
9465     hideTrigger:false,
9466
9467     /**
9468      * @cfg {Boolean} removable (true|false) special filter default false
9469      */
9470     removable : false,
9471     
9472     /** @cfg {Boolean} grow @hide */
9473     /** @cfg {Number} growMin @hide */
9474     /** @cfg {Number} growMax @hide */
9475
9476     /**
9477      * @hide 
9478      * @method
9479      */
9480     autoSize: Roo.emptyFn,
9481     // private
9482     monitorTab : true,
9483     // private
9484     deferHeight : true,
9485
9486     
9487     actionMode : 'wrap',
9488     
9489     caret : false,
9490     
9491     
9492     getAutoCreate : function(){
9493        
9494         var align = this.labelAlign || this.parentLabelAlign();
9495         
9496         var id = Roo.id();
9497         
9498         var cfg = {
9499             cls: 'form-group' //input-group
9500         };
9501         
9502         
9503         var input =  {
9504             tag: 'input',
9505             id : id,
9506             type : this.inputType,
9507             cls : 'form-control',
9508             autocomplete: 'new-password',
9509             placeholder : this.placeholder || '' 
9510             
9511         };
9512         if (this.name) {
9513             input.name = this.name;
9514         }
9515         if (this.size) {
9516             input.cls += ' input-' + this.size;
9517         }
9518         
9519         if (this.disabled) {
9520             input.disabled=true;
9521         }
9522         
9523         var inputblock = input;
9524         
9525         if(this.hasFeedback && !this.allowBlank){
9526             
9527             var feedback = {
9528                 tag: 'span',
9529                 cls: 'glyphicon form-control-feedback'
9530             };
9531             
9532             if(this.removable && !this.editable && !this.tickable){
9533                 inputblock = {
9534                     cls : 'has-feedback',
9535                     cn :  [
9536                         inputblock,
9537                         {
9538                             tag: 'button',
9539                             html : 'x',
9540                             cls : 'roo-combo-removable-btn close'
9541                         },
9542                         feedback
9543                     ] 
9544                 };
9545             } else {
9546                 inputblock = {
9547                     cls : 'has-feedback',
9548                     cn :  [
9549                         inputblock,
9550                         feedback
9551                     ] 
9552                 };
9553             }
9554
9555         } else {
9556             if(this.removable && !this.editable && !this.tickable){
9557                 inputblock = {
9558                     cls : 'roo-removable',
9559                     cn :  [
9560                         inputblock,
9561                         {
9562                             tag: 'button',
9563                             html : 'x',
9564                             cls : 'roo-combo-removable-btn close'
9565                         }
9566                     ] 
9567                 };
9568             }
9569         }
9570         
9571         if (this.before || this.after) {
9572             
9573             inputblock = {
9574                 cls : 'input-group',
9575                 cn :  [] 
9576             };
9577             if (this.before) {
9578                 inputblock.cn.push({
9579                     tag :'span',
9580                     cls : 'input-group-addon',
9581                     html : this.before
9582                 });
9583             }
9584             
9585             inputblock.cn.push(input);
9586             
9587             if(this.hasFeedback && !this.allowBlank){
9588                 inputblock.cls += ' has-feedback';
9589                 inputblock.cn.push(feedback);
9590             }
9591             
9592             if (this.after) {
9593                 inputblock.cn.push({
9594                     tag :'span',
9595                     cls : 'input-group-addon',
9596                     html : this.after
9597                 });
9598             }
9599             
9600         };
9601         
9602         var box = {
9603             tag: 'div',
9604             cn: [
9605                 {
9606                     tag: 'input',
9607                     type : 'hidden',
9608                     cls: 'form-hidden-field'
9609                 },
9610                 inputblock
9611             ]
9612             
9613         };
9614         
9615         if(this.multiple){
9616             box = {
9617                 tag: 'div',
9618                 cn: [
9619                     {
9620                         tag: 'input',
9621                         type : 'hidden',
9622                         cls: 'form-hidden-field'
9623                     },
9624                     {
9625                         tag: 'ul',
9626                         cls: 'roo-select2-choices',
9627                         cn:[
9628                             {
9629                                 tag: 'li',
9630                                 cls: 'roo-select2-search-field',
9631                                 cn: [
9632
9633                                     inputblock
9634                                 ]
9635                             }
9636                         ]
9637                     }
9638                 ]
9639             }
9640         };
9641         
9642         var combobox = {
9643             cls: 'roo-select2-container input-group',
9644             cn: [
9645                 box
9646 //                {
9647 //                    tag: 'ul',
9648 //                    cls: 'typeahead typeahead-long dropdown-menu',
9649 //                    style: 'display:none'
9650 //                }
9651             ]
9652         };
9653         
9654         if(!this.multiple && this.showToggleBtn){
9655             
9656             var caret = {
9657                         tag: 'span',
9658                         cls: 'caret'
9659              };
9660             if (this.caret != false) {
9661                 caret = {
9662                      tag: 'i',
9663                      cls: 'fa fa-' + this.caret
9664                 };
9665                 
9666             }
9667             
9668             combobox.cn.push({
9669                 tag :'span',
9670                 cls : 'input-group-addon btn dropdown-toggle',
9671                 cn : [
9672                     caret,
9673                     {
9674                         tag: 'span',
9675                         cls: 'combobox-clear',
9676                         cn  : [
9677                             {
9678                                 tag : 'i',
9679                                 cls: 'icon-remove'
9680                             }
9681                         ]
9682                     }
9683                 ]
9684
9685             })
9686         }
9687         
9688         if(this.multiple){
9689             combobox.cls += ' roo-select2-container-multi';
9690         }
9691         
9692         if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
9693             
9694 //                Roo.log("left and has label");
9695             cfg.cn = [
9696                 {
9697                     tag : 'i',
9698                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9699                     tooltip : 'This field is required'
9700                 },
9701                 {
9702                     tag: 'label',
9703                     'for' :  id,
9704                     cls : 'control-label col-sm-' + this.labelWidth,
9705                     html : this.fieldLabel
9706
9707                 },
9708                 {
9709                     cls : "col-sm-" + (12 - this.labelWidth), 
9710                     cn: [
9711                         combobox
9712                     ]
9713                 }
9714
9715             ];
9716             
9717             if(this.indicatorpos == 'right'){
9718                 cfg.cn = [
9719                     {
9720                         tag: 'label',
9721                         'for' :  id,
9722                         cls : 'control-label col-sm-' + this.labelWidth,
9723                         html : this.fieldLabel
9724
9725                     },
9726                     {
9727                         tag : 'i',
9728                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9729                         tooltip : 'This field is required'
9730                     },
9731                     {
9732                         cls : "col-sm-" + (12 - this.labelWidth), 
9733                         cn: [
9734                             combobox
9735                         ]
9736                     }
9737
9738                 ];
9739             }
9740             
9741         } else if ( this.fieldLabel.length) {
9742 //                Roo.log(" label");
9743             cfg.cn = [
9744                 {
9745                    tag : 'i',
9746                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9747                    tooltip : 'This field is required'
9748                },
9749                {
9750                    tag: 'label',
9751                    //cls : 'input-group-addon',
9752                    html : this.fieldLabel
9753
9754                },
9755
9756                combobox
9757
9758             ];
9759             
9760             if(this.indicatorpos == 'right'){
9761                 
9762                 cfg.cn = [
9763                     {
9764                        tag: 'label',
9765                        //cls : 'input-group-addon',
9766                        html : this.fieldLabel
9767
9768                     },
9769                     {
9770                        tag : 'i',
9771                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9772                        tooltip : 'This field is required'
9773                     },
9774                     
9775                     combobox
9776
9777                 ];
9778
9779             }
9780
9781         } else {
9782             
9783 //                Roo.log(" no label && no align");
9784                 cfg = combobox
9785                      
9786                 
9787         }
9788          
9789         var settings=this;
9790         ['xs','sm','md','lg'].map(function(size){
9791             if (settings[size]) {
9792                 cfg.cls += ' col-' + size + '-' + settings[size];
9793             }
9794         });
9795         
9796         return cfg;
9797         
9798     },
9799     
9800     
9801     
9802     // private
9803     onResize : function(w, h){
9804 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9805 //        if(typeof w == 'number'){
9806 //            var x = w - this.trigger.getWidth();
9807 //            this.inputEl().setWidth(this.adjustWidth('input', x));
9808 //            this.trigger.setStyle('left', x+'px');
9809 //        }
9810     },
9811
9812     // private
9813     adjustSize : Roo.BoxComponent.prototype.adjustSize,
9814
9815     // private
9816     getResizeEl : function(){
9817         return this.inputEl();
9818     },
9819
9820     // private
9821     getPositionEl : function(){
9822         return this.inputEl();
9823     },
9824
9825     // private
9826     alignErrorIcon : function(){
9827         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9828     },
9829
9830     // private
9831     initEvents : function(){
9832         
9833         this.createList();
9834         
9835         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9836         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9837         if(!this.multiple && this.showToggleBtn){
9838             this.trigger = this.el.select('span.dropdown-toggle',true).first();
9839             if(this.hideTrigger){
9840                 this.trigger.setDisplayed(false);
9841             }
9842             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9843         }
9844         
9845         if(this.multiple){
9846             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9847         }
9848         
9849         if(this.removable && !this.editable && !this.tickable){
9850             var close = this.closeTriggerEl();
9851             
9852             if(close){
9853                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9854                 close.on('click', this.removeBtnClick, this, close);
9855             }
9856         }
9857         
9858         //this.trigger.addClassOnOver('x-form-trigger-over');
9859         //this.trigger.addClassOnClick('x-form-trigger-click');
9860         
9861         //if(!this.width){
9862         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9863         //}
9864     },
9865     
9866     closeTriggerEl : function()
9867     {
9868         var close = this.el.select('.roo-combo-removable-btn', true).first();
9869         return close ? close : false;
9870     },
9871     
9872     removeBtnClick : function(e, h, el)
9873     {
9874         e.preventDefault();
9875         
9876         if(this.fireEvent("remove", this) !== false){
9877             this.reset();
9878             this.fireEvent("afterremove", this)
9879         }
9880     },
9881     
9882     createList : function()
9883     {
9884         this.list = Roo.get(document.body).createChild({
9885             tag: 'ul',
9886             cls: 'typeahead typeahead-long dropdown-menu',
9887             style: 'display:none'
9888         });
9889         
9890         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9891         
9892     },
9893
9894     // private
9895     initTrigger : function(){
9896        
9897     },
9898
9899     // private
9900     onDestroy : function(){
9901         if(this.trigger){
9902             this.trigger.removeAllListeners();
9903           //  this.trigger.remove();
9904         }
9905         //if(this.wrap){
9906         //    this.wrap.remove();
9907         //}
9908         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9909     },
9910
9911     // private
9912     onFocus : function(){
9913         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9914         /*
9915         if(!this.mimicing){
9916             this.wrap.addClass('x-trigger-wrap-focus');
9917             this.mimicing = true;
9918             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9919             if(this.monitorTab){
9920                 this.el.on("keydown", this.checkTab, this);
9921             }
9922         }
9923         */
9924     },
9925
9926     // private
9927     checkTab : function(e){
9928         if(e.getKey() == e.TAB){
9929             this.triggerBlur();
9930         }
9931     },
9932
9933     // private
9934     onBlur : function(){
9935         // do nothing
9936     },
9937
9938     // private
9939     mimicBlur : function(e, t){
9940         /*
9941         if(!this.wrap.contains(t) && this.validateBlur()){
9942             this.triggerBlur();
9943         }
9944         */
9945     },
9946
9947     // private
9948     triggerBlur : function(){
9949         this.mimicing = false;
9950         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9951         if(this.monitorTab){
9952             this.el.un("keydown", this.checkTab, this);
9953         }
9954         //this.wrap.removeClass('x-trigger-wrap-focus');
9955         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9956     },
9957
9958     // private
9959     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9960     validateBlur : function(e, t){
9961         return true;
9962     },
9963
9964     // private
9965     onDisable : function(){
9966         this.inputEl().dom.disabled = true;
9967         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9968         //if(this.wrap){
9969         //    this.wrap.addClass('x-item-disabled');
9970         //}
9971     },
9972
9973     // private
9974     onEnable : function(){
9975         this.inputEl().dom.disabled = false;
9976         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9977         //if(this.wrap){
9978         //    this.el.removeClass('x-item-disabled');
9979         //}
9980     },
9981
9982     // private
9983     onShow : function(){
9984         var ae = this.getActionEl();
9985         
9986         if(ae){
9987             ae.dom.style.display = '';
9988             ae.dom.style.visibility = 'visible';
9989         }
9990     },
9991
9992     // private
9993     
9994     onHide : function(){
9995         var ae = this.getActionEl();
9996         ae.dom.style.display = 'none';
9997     },
9998
9999     /**
10000      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10001      * by an implementing function.
10002      * @method
10003      * @param {EventObject} e
10004      */
10005     onTriggerClick : Roo.emptyFn
10006 });
10007  /*
10008  * Based on:
10009  * Ext JS Library 1.1.1
10010  * Copyright(c) 2006-2007, Ext JS, LLC.
10011  *
10012  * Originally Released Under LGPL - original licence link has changed is not relivant.
10013  *
10014  * Fork - LGPL
10015  * <script type="text/javascript">
10016  */
10017
10018
10019 /**
10020  * @class Roo.data.SortTypes
10021  * @singleton
10022  * Defines the default sorting (casting?) comparison functions used when sorting data.
10023  */
10024 Roo.data.SortTypes = {
10025     /**
10026      * Default sort that does nothing
10027      * @param {Mixed} s The value being converted
10028      * @return {Mixed} The comparison value
10029      */
10030     none : function(s){
10031         return s;
10032     },
10033     
10034     /**
10035      * The regular expression used to strip tags
10036      * @type {RegExp}
10037      * @property
10038      */
10039     stripTagsRE : /<\/?[^>]+>/gi,
10040     
10041     /**
10042      * Strips all HTML tags to sort on text only
10043      * @param {Mixed} s The value being converted
10044      * @return {String} The comparison value
10045      */
10046     asText : function(s){
10047         return String(s).replace(this.stripTagsRE, "");
10048     },
10049     
10050     /**
10051      * Strips all HTML tags to sort on text only - Case insensitive
10052      * @param {Mixed} s The value being converted
10053      * @return {String} The comparison value
10054      */
10055     asUCText : function(s){
10056         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10057     },
10058     
10059     /**
10060      * Case insensitive string
10061      * @param {Mixed} s The value being converted
10062      * @return {String} The comparison value
10063      */
10064     asUCString : function(s) {
10065         return String(s).toUpperCase();
10066     },
10067     
10068     /**
10069      * Date sorting
10070      * @param {Mixed} s The value being converted
10071      * @return {Number} The comparison value
10072      */
10073     asDate : function(s) {
10074         if(!s){
10075             return 0;
10076         }
10077         if(s instanceof Date){
10078             return s.getTime();
10079         }
10080         return Date.parse(String(s));
10081     },
10082     
10083     /**
10084      * Float sorting
10085      * @param {Mixed} s The value being converted
10086      * @return {Float} The comparison value
10087      */
10088     asFloat : function(s) {
10089         var val = parseFloat(String(s).replace(/,/g, ""));
10090         if(isNaN(val)) {
10091             val = 0;
10092         }
10093         return val;
10094     },
10095     
10096     /**
10097      * Integer sorting
10098      * @param {Mixed} s The value being converted
10099      * @return {Number} The comparison value
10100      */
10101     asInt : function(s) {
10102         var val = parseInt(String(s).replace(/,/g, ""));
10103         if(isNaN(val)) {
10104             val = 0;
10105         }
10106         return val;
10107     }
10108 };/*
10109  * Based on:
10110  * Ext JS Library 1.1.1
10111  * Copyright(c) 2006-2007, Ext JS, LLC.
10112  *
10113  * Originally Released Under LGPL - original licence link has changed is not relivant.
10114  *
10115  * Fork - LGPL
10116  * <script type="text/javascript">
10117  */
10118
10119 /**
10120 * @class Roo.data.Record
10121  * Instances of this class encapsulate both record <em>definition</em> information, and record
10122  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10123  * to access Records cached in an {@link Roo.data.Store} object.<br>
10124  * <p>
10125  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10126  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10127  * objects.<br>
10128  * <p>
10129  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10130  * @constructor
10131  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10132  * {@link #create}. The parameters are the same.
10133  * @param {Array} data An associative Array of data values keyed by the field name.
10134  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10135  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10136  * not specified an integer id is generated.
10137  */
10138 Roo.data.Record = function(data, id){
10139     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10140     this.data = data;
10141 };
10142
10143 /**
10144  * Generate a constructor for a specific record layout.
10145  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10146  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10147  * Each field definition object may contain the following properties: <ul>
10148  * <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,
10149  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10150  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10151  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10152  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10153  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10154  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10155  * this may be omitted.</p></li>
10156  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10157  * <ul><li>auto (Default, implies no conversion)</li>
10158  * <li>string</li>
10159  * <li>int</li>
10160  * <li>float</li>
10161  * <li>boolean</li>
10162  * <li>date</li></ul></p></li>
10163  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10164  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10165  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10166  * by the Reader into an object that will be stored in the Record. It is passed the
10167  * following parameters:<ul>
10168  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10169  * </ul></p></li>
10170  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10171  * </ul>
10172  * <br>usage:<br><pre><code>
10173 var TopicRecord = Roo.data.Record.create(
10174     {name: 'title', mapping: 'topic_title'},
10175     {name: 'author', mapping: 'username'},
10176     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10177     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10178     {name: 'lastPoster', mapping: 'user2'},
10179     {name: 'excerpt', mapping: 'post_text'}
10180 );
10181
10182 var myNewRecord = new TopicRecord({
10183     title: 'Do my job please',
10184     author: 'noobie',
10185     totalPosts: 1,
10186     lastPost: new Date(),
10187     lastPoster: 'Animal',
10188     excerpt: 'No way dude!'
10189 });
10190 myStore.add(myNewRecord);
10191 </code></pre>
10192  * @method create
10193  * @static
10194  */
10195 Roo.data.Record.create = function(o){
10196     var f = function(){
10197         f.superclass.constructor.apply(this, arguments);
10198     };
10199     Roo.extend(f, Roo.data.Record);
10200     var p = f.prototype;
10201     p.fields = new Roo.util.MixedCollection(false, function(field){
10202         return field.name;
10203     });
10204     for(var i = 0, len = o.length; i < len; i++){
10205         p.fields.add(new Roo.data.Field(o[i]));
10206     }
10207     f.getField = function(name){
10208         return p.fields.get(name);  
10209     };
10210     return f;
10211 };
10212
10213 Roo.data.Record.AUTO_ID = 1000;
10214 Roo.data.Record.EDIT = 'edit';
10215 Roo.data.Record.REJECT = 'reject';
10216 Roo.data.Record.COMMIT = 'commit';
10217
10218 Roo.data.Record.prototype = {
10219     /**
10220      * Readonly flag - true if this record has been modified.
10221      * @type Boolean
10222      */
10223     dirty : false,
10224     editing : false,
10225     error: null,
10226     modified: null,
10227
10228     // private
10229     join : function(store){
10230         this.store = store;
10231     },
10232
10233     /**
10234      * Set the named field to the specified value.
10235      * @param {String} name The name of the field to set.
10236      * @param {Object} value The value to set the field to.
10237      */
10238     set : function(name, value){
10239         if(this.data[name] == value){
10240             return;
10241         }
10242         this.dirty = true;
10243         if(!this.modified){
10244             this.modified = {};
10245         }
10246         if(typeof this.modified[name] == 'undefined'){
10247             this.modified[name] = this.data[name];
10248         }
10249         this.data[name] = value;
10250         if(!this.editing && this.store){
10251             this.store.afterEdit(this);
10252         }       
10253     },
10254
10255     /**
10256      * Get the value of the named field.
10257      * @param {String} name The name of the field to get the value of.
10258      * @return {Object} The value of the field.
10259      */
10260     get : function(name){
10261         return this.data[name]; 
10262     },
10263
10264     // private
10265     beginEdit : function(){
10266         this.editing = true;
10267         this.modified = {}; 
10268     },
10269
10270     // private
10271     cancelEdit : function(){
10272         this.editing = false;
10273         delete this.modified;
10274     },
10275
10276     // private
10277     endEdit : function(){
10278         this.editing = false;
10279         if(this.dirty && this.store){
10280             this.store.afterEdit(this);
10281         }
10282     },
10283
10284     /**
10285      * Usually called by the {@link Roo.data.Store} which owns the Record.
10286      * Rejects all changes made to the Record since either creation, or the last commit operation.
10287      * Modified fields are reverted to their original values.
10288      * <p>
10289      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10290      * of reject operations.
10291      */
10292     reject : function(){
10293         var m = this.modified;
10294         for(var n in m){
10295             if(typeof m[n] != "function"){
10296                 this.data[n] = m[n];
10297             }
10298         }
10299         this.dirty = false;
10300         delete this.modified;
10301         this.editing = false;
10302         if(this.store){
10303             this.store.afterReject(this);
10304         }
10305     },
10306
10307     /**
10308      * Usually called by the {@link Roo.data.Store} which owns the Record.
10309      * Commits all changes made to the Record since either creation, or the last commit operation.
10310      * <p>
10311      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10312      * of commit operations.
10313      */
10314     commit : function(){
10315         this.dirty = false;
10316         delete this.modified;
10317         this.editing = false;
10318         if(this.store){
10319             this.store.afterCommit(this);
10320         }
10321     },
10322
10323     // private
10324     hasError : function(){
10325         return this.error != null;
10326     },
10327
10328     // private
10329     clearError : function(){
10330         this.error = null;
10331     },
10332
10333     /**
10334      * Creates a copy of this record.
10335      * @param {String} id (optional) A new record id if you don't want to use this record's id
10336      * @return {Record}
10337      */
10338     copy : function(newId) {
10339         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10340     }
10341 };/*
10342  * Based on:
10343  * Ext JS Library 1.1.1
10344  * Copyright(c) 2006-2007, Ext JS, LLC.
10345  *
10346  * Originally Released Under LGPL - original licence link has changed is not relivant.
10347  *
10348  * Fork - LGPL
10349  * <script type="text/javascript">
10350  */
10351
10352
10353
10354 /**
10355  * @class Roo.data.Store
10356  * @extends Roo.util.Observable
10357  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10358  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10359  * <p>
10360  * 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
10361  * has no knowledge of the format of the data returned by the Proxy.<br>
10362  * <p>
10363  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10364  * instances from the data object. These records are cached and made available through accessor functions.
10365  * @constructor
10366  * Creates a new Store.
10367  * @param {Object} config A config object containing the objects needed for the Store to access data,
10368  * and read the data into Records.
10369  */
10370 Roo.data.Store = function(config){
10371     this.data = new Roo.util.MixedCollection(false);
10372     this.data.getKey = function(o){
10373         return o.id;
10374     };
10375     this.baseParams = {};
10376     // private
10377     this.paramNames = {
10378         "start" : "start",
10379         "limit" : "limit",
10380         "sort" : "sort",
10381         "dir" : "dir",
10382         "multisort" : "_multisort"
10383     };
10384
10385     if(config && config.data){
10386         this.inlineData = config.data;
10387         delete config.data;
10388     }
10389
10390     Roo.apply(this, config);
10391     
10392     if(this.reader){ // reader passed
10393         this.reader = Roo.factory(this.reader, Roo.data);
10394         this.reader.xmodule = this.xmodule || false;
10395         if(!this.recordType){
10396             this.recordType = this.reader.recordType;
10397         }
10398         if(this.reader.onMetaChange){
10399             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10400         }
10401     }
10402
10403     if(this.recordType){
10404         this.fields = this.recordType.prototype.fields;
10405     }
10406     this.modified = [];
10407
10408     this.addEvents({
10409         /**
10410          * @event datachanged
10411          * Fires when the data cache has changed, and a widget which is using this Store
10412          * as a Record cache should refresh its view.
10413          * @param {Store} this
10414          */
10415         datachanged : true,
10416         /**
10417          * @event metachange
10418          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10419          * @param {Store} this
10420          * @param {Object} meta The JSON metadata
10421          */
10422         metachange : true,
10423         /**
10424          * @event add
10425          * Fires when Records have been added to the Store
10426          * @param {Store} this
10427          * @param {Roo.data.Record[]} records The array of Records added
10428          * @param {Number} index The index at which the record(s) were added
10429          */
10430         add : true,
10431         /**
10432          * @event remove
10433          * Fires when a Record has been removed from the Store
10434          * @param {Store} this
10435          * @param {Roo.data.Record} record The Record that was removed
10436          * @param {Number} index The index at which the record was removed
10437          */
10438         remove : true,
10439         /**
10440          * @event update
10441          * Fires when a Record has been updated
10442          * @param {Store} this
10443          * @param {Roo.data.Record} record The Record that was updated
10444          * @param {String} operation The update operation being performed.  Value may be one of:
10445          * <pre><code>
10446  Roo.data.Record.EDIT
10447  Roo.data.Record.REJECT
10448  Roo.data.Record.COMMIT
10449          * </code></pre>
10450          */
10451         update : true,
10452         /**
10453          * @event clear
10454          * Fires when the data cache has been cleared.
10455          * @param {Store} this
10456          */
10457         clear : true,
10458         /**
10459          * @event beforeload
10460          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10461          * the load action will be canceled.
10462          * @param {Store} this
10463          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10464          */
10465         beforeload : true,
10466         /**
10467          * @event beforeloadadd
10468          * Fires after a new set of Records has been loaded.
10469          * @param {Store} this
10470          * @param {Roo.data.Record[]} records The Records that were loaded
10471          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10472          */
10473         beforeloadadd : true,
10474         /**
10475          * @event load
10476          * Fires after a new set of Records has been loaded, before they are added to the store.
10477          * @param {Store} this
10478          * @param {Roo.data.Record[]} records The Records that were loaded
10479          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10480          * @params {Object} return from reader
10481          */
10482         load : true,
10483         /**
10484          * @event loadexception
10485          * Fires if an exception occurs in the Proxy during loading.
10486          * Called with the signature of the Proxy's "loadexception" event.
10487          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10488          * 
10489          * @param {Proxy} 
10490          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10491          * @param {Object} load options 
10492          * @param {Object} jsonData from your request (normally this contains the Exception)
10493          */
10494         loadexception : true
10495     });
10496     
10497     if(this.proxy){
10498         this.proxy = Roo.factory(this.proxy, Roo.data);
10499         this.proxy.xmodule = this.xmodule || false;
10500         this.relayEvents(this.proxy,  ["loadexception"]);
10501     }
10502     this.sortToggle = {};
10503     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10504
10505     Roo.data.Store.superclass.constructor.call(this);
10506
10507     if(this.inlineData){
10508         this.loadData(this.inlineData);
10509         delete this.inlineData;
10510     }
10511 };
10512
10513 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10514      /**
10515     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10516     * without a remote query - used by combo/forms at present.
10517     */
10518     
10519     /**
10520     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10521     */
10522     /**
10523     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10524     */
10525     /**
10526     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10527     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10528     */
10529     /**
10530     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10531     * on any HTTP request
10532     */
10533     /**
10534     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10535     */
10536     /**
10537     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10538     */
10539     multiSort: false,
10540     /**
10541     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10542     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10543     */
10544     remoteSort : false,
10545
10546     /**
10547     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10548      * loaded or when a record is removed. (defaults to false).
10549     */
10550     pruneModifiedRecords : false,
10551
10552     // private
10553     lastOptions : null,
10554
10555     /**
10556      * Add Records to the Store and fires the add event.
10557      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10558      */
10559     add : function(records){
10560         records = [].concat(records);
10561         for(var i = 0, len = records.length; i < len; i++){
10562             records[i].join(this);
10563         }
10564         var index = this.data.length;
10565         this.data.addAll(records);
10566         this.fireEvent("add", this, records, index);
10567     },
10568
10569     /**
10570      * Remove a Record from the Store and fires the remove event.
10571      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10572      */
10573     remove : function(record){
10574         var index = this.data.indexOf(record);
10575         this.data.removeAt(index);
10576         if(this.pruneModifiedRecords){
10577             this.modified.remove(record);
10578         }
10579         this.fireEvent("remove", this, record, index);
10580     },
10581
10582     /**
10583      * Remove all Records from the Store and fires the clear event.
10584      */
10585     removeAll : function(){
10586         this.data.clear();
10587         if(this.pruneModifiedRecords){
10588             this.modified = [];
10589         }
10590         this.fireEvent("clear", this);
10591     },
10592
10593     /**
10594      * Inserts Records to the Store at the given index and fires the add event.
10595      * @param {Number} index The start index at which to insert the passed Records.
10596      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10597      */
10598     insert : function(index, records){
10599         records = [].concat(records);
10600         for(var i = 0, len = records.length; i < len; i++){
10601             this.data.insert(index, records[i]);
10602             records[i].join(this);
10603         }
10604         this.fireEvent("add", this, records, index);
10605     },
10606
10607     /**
10608      * Get the index within the cache of the passed Record.
10609      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10610      * @return {Number} The index of the passed Record. Returns -1 if not found.
10611      */
10612     indexOf : function(record){
10613         return this.data.indexOf(record);
10614     },
10615
10616     /**
10617      * Get the index within the cache of the Record with the passed id.
10618      * @param {String} id The id of the Record to find.
10619      * @return {Number} The index of the Record. Returns -1 if not found.
10620      */
10621     indexOfId : function(id){
10622         return this.data.indexOfKey(id);
10623     },
10624
10625     /**
10626      * Get the Record with the specified id.
10627      * @param {String} id The id of the Record to find.
10628      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10629      */
10630     getById : function(id){
10631         return this.data.key(id);
10632     },
10633
10634     /**
10635      * Get the Record at the specified index.
10636      * @param {Number} index The index of the Record to find.
10637      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10638      */
10639     getAt : function(index){
10640         return this.data.itemAt(index);
10641     },
10642
10643     /**
10644      * Returns a range of Records between specified indices.
10645      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10646      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10647      * @return {Roo.data.Record[]} An array of Records
10648      */
10649     getRange : function(start, end){
10650         return this.data.getRange(start, end);
10651     },
10652
10653     // private
10654     storeOptions : function(o){
10655         o = Roo.apply({}, o);
10656         delete o.callback;
10657         delete o.scope;
10658         this.lastOptions = o;
10659     },
10660
10661     /**
10662      * Loads the Record cache from the configured Proxy using the configured Reader.
10663      * <p>
10664      * If using remote paging, then the first load call must specify the <em>start</em>
10665      * and <em>limit</em> properties in the options.params property to establish the initial
10666      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10667      * <p>
10668      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10669      * and this call will return before the new data has been loaded. Perform any post-processing
10670      * in a callback function, or in a "load" event handler.</strong>
10671      * <p>
10672      * @param {Object} options An object containing properties which control loading options:<ul>
10673      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10674      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10675      * passed the following arguments:<ul>
10676      * <li>r : Roo.data.Record[]</li>
10677      * <li>options: Options object from the load call</li>
10678      * <li>success: Boolean success indicator</li></ul></li>
10679      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10680      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10681      * </ul>
10682      */
10683     load : function(options){
10684         options = options || {};
10685         if(this.fireEvent("beforeload", this, options) !== false){
10686             this.storeOptions(options);
10687             var p = Roo.apply(options.params || {}, this.baseParams);
10688             // if meta was not loaded from remote source.. try requesting it.
10689             if (!this.reader.metaFromRemote) {
10690                 p._requestMeta = 1;
10691             }
10692             if(this.sortInfo && this.remoteSort){
10693                 var pn = this.paramNames;
10694                 p[pn["sort"]] = this.sortInfo.field;
10695                 p[pn["dir"]] = this.sortInfo.direction;
10696             }
10697             if (this.multiSort) {
10698                 var pn = this.paramNames;
10699                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10700             }
10701             
10702             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10703         }
10704     },
10705
10706     /**
10707      * Reloads the Record cache from the configured Proxy using the configured Reader and
10708      * the options from the last load operation performed.
10709      * @param {Object} options (optional) An object containing properties which may override the options
10710      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10711      * the most recently used options are reused).
10712      */
10713     reload : function(options){
10714         this.load(Roo.applyIf(options||{}, this.lastOptions));
10715     },
10716
10717     // private
10718     // Called as a callback by the Reader during a load operation.
10719     loadRecords : function(o, options, success){
10720         if(!o || success === false){
10721             if(success !== false){
10722                 this.fireEvent("load", this, [], options, o);
10723             }
10724             if(options.callback){
10725                 options.callback.call(options.scope || this, [], options, false);
10726             }
10727             return;
10728         }
10729         // if data returned failure - throw an exception.
10730         if (o.success === false) {
10731             // show a message if no listener is registered.
10732             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10733                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10734             }
10735             // loadmask wil be hooked into this..
10736             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10737             return;
10738         }
10739         var r = o.records, t = o.totalRecords || r.length;
10740         
10741         this.fireEvent("beforeloadadd", this, r, options, o);
10742         
10743         if(!options || options.add !== true){
10744             if(this.pruneModifiedRecords){
10745                 this.modified = [];
10746             }
10747             for(var i = 0, len = r.length; i < len; i++){
10748                 r[i].join(this);
10749             }
10750             if(this.snapshot){
10751                 this.data = this.snapshot;
10752                 delete this.snapshot;
10753             }
10754             this.data.clear();
10755             this.data.addAll(r);
10756             this.totalLength = t;
10757             this.applySort();
10758             this.fireEvent("datachanged", this);
10759         }else{
10760             this.totalLength = Math.max(t, this.data.length+r.length);
10761             this.add(r);
10762         }
10763         this.fireEvent("load", this, r, options, o);
10764         if(options.callback){
10765             options.callback.call(options.scope || this, r, options, true);
10766         }
10767     },
10768
10769
10770     /**
10771      * Loads data from a passed data block. A Reader which understands the format of the data
10772      * must have been configured in the constructor.
10773      * @param {Object} data The data block from which to read the Records.  The format of the data expected
10774      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10775      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10776      */
10777     loadData : function(o, append){
10778         var r = this.reader.readRecords(o);
10779         this.loadRecords(r, {add: append}, true);
10780     },
10781
10782     /**
10783      * Gets the number of cached records.
10784      * <p>
10785      * <em>If using paging, this may not be the total size of the dataset. If the data object
10786      * used by the Reader contains the dataset size, then the getTotalCount() function returns
10787      * the data set size</em>
10788      */
10789     getCount : function(){
10790         return this.data.length || 0;
10791     },
10792
10793     /**
10794      * Gets the total number of records in the dataset as returned by the server.
10795      * <p>
10796      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10797      * the dataset size</em>
10798      */
10799     getTotalCount : function(){
10800         return this.totalLength || 0;
10801     },
10802
10803     /**
10804      * Returns the sort state of the Store as an object with two properties:
10805      * <pre><code>
10806  field {String} The name of the field by which the Records are sorted
10807  direction {String} The sort order, "ASC" or "DESC"
10808      * </code></pre>
10809      */
10810     getSortState : function(){
10811         return this.sortInfo;
10812     },
10813
10814     // private
10815     applySort : function(){
10816         if(this.sortInfo && !this.remoteSort){
10817             var s = this.sortInfo, f = s.field;
10818             var st = this.fields.get(f).sortType;
10819             var fn = function(r1, r2){
10820                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10821                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10822             };
10823             this.data.sort(s.direction, fn);
10824             if(this.snapshot && this.snapshot != this.data){
10825                 this.snapshot.sort(s.direction, fn);
10826             }
10827         }
10828     },
10829
10830     /**
10831      * Sets the default sort column and order to be used by the next load operation.
10832      * @param {String} fieldName The name of the field to sort by.
10833      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10834      */
10835     setDefaultSort : function(field, dir){
10836         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10837     },
10838
10839     /**
10840      * Sort the Records.
10841      * If remote sorting is used, the sort is performed on the server, and the cache is
10842      * reloaded. If local sorting is used, the cache is sorted internally.
10843      * @param {String} fieldName The name of the field to sort by.
10844      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10845      */
10846     sort : function(fieldName, dir){
10847         var f = this.fields.get(fieldName);
10848         if(!dir){
10849             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10850             
10851             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10852                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10853             }else{
10854                 dir = f.sortDir;
10855             }
10856         }
10857         this.sortToggle[f.name] = dir;
10858         this.sortInfo = {field: f.name, direction: dir};
10859         if(!this.remoteSort){
10860             this.applySort();
10861             this.fireEvent("datachanged", this);
10862         }else{
10863             this.load(this.lastOptions);
10864         }
10865     },
10866
10867     /**
10868      * Calls the specified function for each of the Records in the cache.
10869      * @param {Function} fn The function to call. The Record is passed as the first parameter.
10870      * Returning <em>false</em> aborts and exits the iteration.
10871      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10872      */
10873     each : function(fn, scope){
10874         this.data.each(fn, scope);
10875     },
10876
10877     /**
10878      * Gets all records modified since the last commit.  Modified records are persisted across load operations
10879      * (e.g., during paging).
10880      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10881      */
10882     getModifiedRecords : function(){
10883         return this.modified;
10884     },
10885
10886     // private
10887     createFilterFn : function(property, value, anyMatch){
10888         if(!value.exec){ // not a regex
10889             value = String(value);
10890             if(value.length == 0){
10891                 return false;
10892             }
10893             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10894         }
10895         return function(r){
10896             return value.test(r.data[property]);
10897         };
10898     },
10899
10900     /**
10901      * Sums the value of <i>property</i> for each record between start and end and returns the result.
10902      * @param {String} property A field on your records
10903      * @param {Number} start The record index to start at (defaults to 0)
10904      * @param {Number} end The last record index to include (defaults to length - 1)
10905      * @return {Number} The sum
10906      */
10907     sum : function(property, start, end){
10908         var rs = this.data.items, v = 0;
10909         start = start || 0;
10910         end = (end || end === 0) ? end : rs.length-1;
10911
10912         for(var i = start; i <= end; i++){
10913             v += (rs[i].data[property] || 0);
10914         }
10915         return v;
10916     },
10917
10918     /**
10919      * Filter the records by a specified property.
10920      * @param {String} field A field on your records
10921      * @param {String/RegExp} value Either a string that the field
10922      * should start with or a RegExp to test against the field
10923      * @param {Boolean} anyMatch True to match any part not just the beginning
10924      */
10925     filter : function(property, value, anyMatch){
10926         var fn = this.createFilterFn(property, value, anyMatch);
10927         return fn ? this.filterBy(fn) : this.clearFilter();
10928     },
10929
10930     /**
10931      * Filter by a function. The specified function will be called with each
10932      * record in this data source. If the function returns true the record is included,
10933      * otherwise it is filtered.
10934      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10935      * @param {Object} scope (optional) The scope of the function (defaults to this)
10936      */
10937     filterBy : function(fn, scope){
10938         this.snapshot = this.snapshot || this.data;
10939         this.data = this.queryBy(fn, scope||this);
10940         this.fireEvent("datachanged", this);
10941     },
10942
10943     /**
10944      * Query the records by a specified property.
10945      * @param {String} field A field on your records
10946      * @param {String/RegExp} value Either a string that the field
10947      * should start with or a RegExp to test against the field
10948      * @param {Boolean} anyMatch True to match any part not just the beginning
10949      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10950      */
10951     query : function(property, value, anyMatch){
10952         var fn = this.createFilterFn(property, value, anyMatch);
10953         return fn ? this.queryBy(fn) : this.data.clone();
10954     },
10955
10956     /**
10957      * Query by a function. The specified function will be called with each
10958      * record in this data source. If the function returns true the record is included
10959      * in the results.
10960      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10961      * @param {Object} scope (optional) The scope of the function (defaults to this)
10962       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10963      **/
10964     queryBy : function(fn, scope){
10965         var data = this.snapshot || this.data;
10966         return data.filterBy(fn, scope||this);
10967     },
10968
10969     /**
10970      * Collects unique values for a particular dataIndex from this store.
10971      * @param {String} dataIndex The property to collect
10972      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10973      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10974      * @return {Array} An array of the unique values
10975      **/
10976     collect : function(dataIndex, allowNull, bypassFilter){
10977         var d = (bypassFilter === true && this.snapshot) ?
10978                 this.snapshot.items : this.data.items;
10979         var v, sv, r = [], l = {};
10980         for(var i = 0, len = d.length; i < len; i++){
10981             v = d[i].data[dataIndex];
10982             sv = String(v);
10983             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10984                 l[sv] = true;
10985                 r[r.length] = v;
10986             }
10987         }
10988         return r;
10989     },
10990
10991     /**
10992      * Revert to a view of the Record cache with no filtering applied.
10993      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10994      */
10995     clearFilter : function(suppressEvent){
10996         if(this.snapshot && this.snapshot != this.data){
10997             this.data = this.snapshot;
10998             delete this.snapshot;
10999             if(suppressEvent !== true){
11000                 this.fireEvent("datachanged", this);
11001             }
11002         }
11003     },
11004
11005     // private
11006     afterEdit : function(record){
11007         if(this.modified.indexOf(record) == -1){
11008             this.modified.push(record);
11009         }
11010         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11011     },
11012     
11013     // private
11014     afterReject : function(record){
11015         this.modified.remove(record);
11016         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11017     },
11018
11019     // private
11020     afterCommit : function(record){
11021         this.modified.remove(record);
11022         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11023     },
11024
11025     /**
11026      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11027      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11028      */
11029     commitChanges : function(){
11030         var m = this.modified.slice(0);
11031         this.modified = [];
11032         for(var i = 0, len = m.length; i < len; i++){
11033             m[i].commit();
11034         }
11035     },
11036
11037     /**
11038      * Cancel outstanding changes on all changed records.
11039      */
11040     rejectChanges : function(){
11041         var m = this.modified.slice(0);
11042         this.modified = [];
11043         for(var i = 0, len = m.length; i < len; i++){
11044             m[i].reject();
11045         }
11046     },
11047
11048     onMetaChange : function(meta, rtype, o){
11049         this.recordType = rtype;
11050         this.fields = rtype.prototype.fields;
11051         delete this.snapshot;
11052         this.sortInfo = meta.sortInfo || this.sortInfo;
11053         this.modified = [];
11054         this.fireEvent('metachange', this, this.reader.meta);
11055     },
11056     
11057     moveIndex : function(data, type)
11058     {
11059         var index = this.indexOf(data);
11060         
11061         var newIndex = index + type;
11062         
11063         this.remove(data);
11064         
11065         this.insert(newIndex, data);
11066         
11067     }
11068 });/*
11069  * Based on:
11070  * Ext JS Library 1.1.1
11071  * Copyright(c) 2006-2007, Ext JS, LLC.
11072  *
11073  * Originally Released Under LGPL - original licence link has changed is not relivant.
11074  *
11075  * Fork - LGPL
11076  * <script type="text/javascript">
11077  */
11078
11079 /**
11080  * @class Roo.data.SimpleStore
11081  * @extends Roo.data.Store
11082  * Small helper class to make creating Stores from Array data easier.
11083  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11084  * @cfg {Array} fields An array of field definition objects, or field name strings.
11085  * @cfg {Array} data The multi-dimensional array of data
11086  * @constructor
11087  * @param {Object} config
11088  */
11089 Roo.data.SimpleStore = function(config){
11090     Roo.data.SimpleStore.superclass.constructor.call(this, {
11091         isLocal : true,
11092         reader: new Roo.data.ArrayReader({
11093                 id: config.id
11094             },
11095             Roo.data.Record.create(config.fields)
11096         ),
11097         proxy : new Roo.data.MemoryProxy(config.data)
11098     });
11099     this.load();
11100 };
11101 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11102  * Based on:
11103  * Ext JS Library 1.1.1
11104  * Copyright(c) 2006-2007, Ext JS, LLC.
11105  *
11106  * Originally Released Under LGPL - original licence link has changed is not relivant.
11107  *
11108  * Fork - LGPL
11109  * <script type="text/javascript">
11110  */
11111
11112 /**
11113 /**
11114  * @extends Roo.data.Store
11115  * @class Roo.data.JsonStore
11116  * Small helper class to make creating Stores for JSON data easier. <br/>
11117 <pre><code>
11118 var store = new Roo.data.JsonStore({
11119     url: 'get-images.php',
11120     root: 'images',
11121     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11122 });
11123 </code></pre>
11124  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11125  * JsonReader and HttpProxy (unless inline data is provided).</b>
11126  * @cfg {Array} fields An array of field definition objects, or field name strings.
11127  * @constructor
11128  * @param {Object} config
11129  */
11130 Roo.data.JsonStore = function(c){
11131     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11132         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11133         reader: new Roo.data.JsonReader(c, c.fields)
11134     }));
11135 };
11136 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11137  * Based on:
11138  * Ext JS Library 1.1.1
11139  * Copyright(c) 2006-2007, Ext JS, LLC.
11140  *
11141  * Originally Released Under LGPL - original licence link has changed is not relivant.
11142  *
11143  * Fork - LGPL
11144  * <script type="text/javascript">
11145  */
11146
11147  
11148 Roo.data.Field = function(config){
11149     if(typeof config == "string"){
11150         config = {name: config};
11151     }
11152     Roo.apply(this, config);
11153     
11154     if(!this.type){
11155         this.type = "auto";
11156     }
11157     
11158     var st = Roo.data.SortTypes;
11159     // named sortTypes are supported, here we look them up
11160     if(typeof this.sortType == "string"){
11161         this.sortType = st[this.sortType];
11162     }
11163     
11164     // set default sortType for strings and dates
11165     if(!this.sortType){
11166         switch(this.type){
11167             case "string":
11168                 this.sortType = st.asUCString;
11169                 break;
11170             case "date":
11171                 this.sortType = st.asDate;
11172                 break;
11173             default:
11174                 this.sortType = st.none;
11175         }
11176     }
11177
11178     // define once
11179     var stripRe = /[\$,%]/g;
11180
11181     // prebuilt conversion function for this field, instead of
11182     // switching every time we're reading a value
11183     if(!this.convert){
11184         var cv, dateFormat = this.dateFormat;
11185         switch(this.type){
11186             case "":
11187             case "auto":
11188             case undefined:
11189                 cv = function(v){ return v; };
11190                 break;
11191             case "string":
11192                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11193                 break;
11194             case "int":
11195                 cv = function(v){
11196                     return v !== undefined && v !== null && v !== '' ?
11197                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11198                     };
11199                 break;
11200             case "float":
11201                 cv = function(v){
11202                     return v !== undefined && v !== null && v !== '' ?
11203                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11204                     };
11205                 break;
11206             case "bool":
11207             case "boolean":
11208                 cv = function(v){ return v === true || v === "true" || v == 1; };
11209                 break;
11210             case "date":
11211                 cv = function(v){
11212                     if(!v){
11213                         return '';
11214                     }
11215                     if(v instanceof Date){
11216                         return v;
11217                     }
11218                     if(dateFormat){
11219                         if(dateFormat == "timestamp"){
11220                             return new Date(v*1000);
11221                         }
11222                         return Date.parseDate(v, dateFormat);
11223                     }
11224                     var parsed = Date.parse(v);
11225                     return parsed ? new Date(parsed) : null;
11226                 };
11227              break;
11228             
11229         }
11230         this.convert = cv;
11231     }
11232 };
11233
11234 Roo.data.Field.prototype = {
11235     dateFormat: null,
11236     defaultValue: "",
11237     mapping: null,
11238     sortType : null,
11239     sortDir : "ASC"
11240 };/*
11241  * Based on:
11242  * Ext JS Library 1.1.1
11243  * Copyright(c) 2006-2007, Ext JS, LLC.
11244  *
11245  * Originally Released Under LGPL - original licence link has changed is not relivant.
11246  *
11247  * Fork - LGPL
11248  * <script type="text/javascript">
11249  */
11250  
11251 // Base class for reading structured data from a data source.  This class is intended to be
11252 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11253
11254 /**
11255  * @class Roo.data.DataReader
11256  * Base class for reading structured data from a data source.  This class is intended to be
11257  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11258  */
11259
11260 Roo.data.DataReader = function(meta, recordType){
11261     
11262     this.meta = meta;
11263     
11264     this.recordType = recordType instanceof Array ? 
11265         Roo.data.Record.create(recordType) : recordType;
11266 };
11267
11268 Roo.data.DataReader.prototype = {
11269      /**
11270      * Create an empty record
11271      * @param {Object} data (optional) - overlay some values
11272      * @return {Roo.data.Record} record created.
11273      */
11274     newRow :  function(d) {
11275         var da =  {};
11276         this.recordType.prototype.fields.each(function(c) {
11277             switch( c.type) {
11278                 case 'int' : da[c.name] = 0; break;
11279                 case 'date' : da[c.name] = new Date(); break;
11280                 case 'float' : da[c.name] = 0.0; break;
11281                 case 'boolean' : da[c.name] = false; break;
11282                 default : da[c.name] = ""; break;
11283             }
11284             
11285         });
11286         return new this.recordType(Roo.apply(da, d));
11287     }
11288     
11289 };/*
11290  * Based on:
11291  * Ext JS Library 1.1.1
11292  * Copyright(c) 2006-2007, Ext JS, LLC.
11293  *
11294  * Originally Released Under LGPL - original licence link has changed is not relivant.
11295  *
11296  * Fork - LGPL
11297  * <script type="text/javascript">
11298  */
11299
11300 /**
11301  * @class Roo.data.DataProxy
11302  * @extends Roo.data.Observable
11303  * This class is an abstract base class for implementations which provide retrieval of
11304  * unformatted data objects.<br>
11305  * <p>
11306  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11307  * (of the appropriate type which knows how to parse the data object) to provide a block of
11308  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11309  * <p>
11310  * Custom implementations must implement the load method as described in
11311  * {@link Roo.data.HttpProxy#load}.
11312  */
11313 Roo.data.DataProxy = function(){
11314     this.addEvents({
11315         /**
11316          * @event beforeload
11317          * Fires before a network request is made to retrieve a data object.
11318          * @param {Object} This DataProxy object.
11319          * @param {Object} params The params parameter to the load function.
11320          */
11321         beforeload : true,
11322         /**
11323          * @event load
11324          * Fires before the load method's callback is called.
11325          * @param {Object} This DataProxy object.
11326          * @param {Object} o The data object.
11327          * @param {Object} arg The callback argument object passed to the load function.
11328          */
11329         load : true,
11330         /**
11331          * @event loadexception
11332          * Fires if an Exception occurs during data retrieval.
11333          * @param {Object} This DataProxy object.
11334          * @param {Object} o The data object.
11335          * @param {Object} arg The callback argument object passed to the load function.
11336          * @param {Object} e The Exception.
11337          */
11338         loadexception : true
11339     });
11340     Roo.data.DataProxy.superclass.constructor.call(this);
11341 };
11342
11343 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11344
11345     /**
11346      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11347      */
11348 /*
11349  * Based on:
11350  * Ext JS Library 1.1.1
11351  * Copyright(c) 2006-2007, Ext JS, LLC.
11352  *
11353  * Originally Released Under LGPL - original licence link has changed is not relivant.
11354  *
11355  * Fork - LGPL
11356  * <script type="text/javascript">
11357  */
11358 /**
11359  * @class Roo.data.MemoryProxy
11360  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11361  * to the Reader when its load method is called.
11362  * @constructor
11363  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11364  */
11365 Roo.data.MemoryProxy = function(data){
11366     if (data.data) {
11367         data = data.data;
11368     }
11369     Roo.data.MemoryProxy.superclass.constructor.call(this);
11370     this.data = data;
11371 };
11372
11373 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11374     
11375     /**
11376      * Load data from the requested source (in this case an in-memory
11377      * data object passed to the constructor), read the data object into
11378      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11379      * process that block using the passed callback.
11380      * @param {Object} params This parameter is not used by the MemoryProxy class.
11381      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11382      * object into a block of Roo.data.Records.
11383      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11384      * The function must be passed <ul>
11385      * <li>The Record block object</li>
11386      * <li>The "arg" argument from the load function</li>
11387      * <li>A boolean success indicator</li>
11388      * </ul>
11389      * @param {Object} scope The scope in which to call the callback
11390      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11391      */
11392     load : function(params, reader, callback, scope, arg){
11393         params = params || {};
11394         var result;
11395         try {
11396             result = reader.readRecords(this.data);
11397         }catch(e){
11398             this.fireEvent("loadexception", this, arg, null, e);
11399             callback.call(scope, null, arg, false);
11400             return;
11401         }
11402         callback.call(scope, result, arg, true);
11403     },
11404     
11405     // private
11406     update : function(params, records){
11407         
11408     }
11409 });/*
11410  * Based on:
11411  * Ext JS Library 1.1.1
11412  * Copyright(c) 2006-2007, Ext JS, LLC.
11413  *
11414  * Originally Released Under LGPL - original licence link has changed is not relivant.
11415  *
11416  * Fork - LGPL
11417  * <script type="text/javascript">
11418  */
11419 /**
11420  * @class Roo.data.HttpProxy
11421  * @extends Roo.data.DataProxy
11422  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11423  * configured to reference a certain URL.<br><br>
11424  * <p>
11425  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11426  * from which the running page was served.<br><br>
11427  * <p>
11428  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11429  * <p>
11430  * Be aware that to enable the browser to parse an XML document, the server must set
11431  * the Content-Type header in the HTTP response to "text/xml".
11432  * @constructor
11433  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11434  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11435  * will be used to make the request.
11436  */
11437 Roo.data.HttpProxy = function(conn){
11438     Roo.data.HttpProxy.superclass.constructor.call(this);
11439     // is conn a conn config or a real conn?
11440     this.conn = conn;
11441     this.useAjax = !conn || !conn.events;
11442   
11443 };
11444
11445 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11446     // thse are take from connection...
11447     
11448     /**
11449      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11450      */
11451     /**
11452      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11453      * extra parameters to each request made by this object. (defaults to undefined)
11454      */
11455     /**
11456      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11457      *  to each request made by this object. (defaults to undefined)
11458      */
11459     /**
11460      * @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)
11461      */
11462     /**
11463      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11464      */
11465      /**
11466      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11467      * @type Boolean
11468      */
11469   
11470
11471     /**
11472      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11473      * @type Boolean
11474      */
11475     /**
11476      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11477      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11478      * a finer-grained basis than the DataProxy events.
11479      */
11480     getConnection : function(){
11481         return this.useAjax ? Roo.Ajax : this.conn;
11482     },
11483
11484     /**
11485      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11486      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11487      * process that block using the passed callback.
11488      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11489      * for the request to the remote server.
11490      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11491      * object into a block of Roo.data.Records.
11492      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11493      * The function must be passed <ul>
11494      * <li>The Record block object</li>
11495      * <li>The "arg" argument from the load function</li>
11496      * <li>A boolean success indicator</li>
11497      * </ul>
11498      * @param {Object} scope The scope in which to call the callback
11499      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11500      */
11501     load : function(params, reader, callback, scope, arg){
11502         if(this.fireEvent("beforeload", this, params) !== false){
11503             var  o = {
11504                 params : params || {},
11505                 request: {
11506                     callback : callback,
11507                     scope : scope,
11508                     arg : arg
11509                 },
11510                 reader: reader,
11511                 callback : this.loadResponse,
11512                 scope: this
11513             };
11514             if(this.useAjax){
11515                 Roo.applyIf(o, this.conn);
11516                 if(this.activeRequest){
11517                     Roo.Ajax.abort(this.activeRequest);
11518                 }
11519                 this.activeRequest = Roo.Ajax.request(o);
11520             }else{
11521                 this.conn.request(o);
11522             }
11523         }else{
11524             callback.call(scope||this, null, arg, false);
11525         }
11526     },
11527
11528     // private
11529     loadResponse : function(o, success, response){
11530         delete this.activeRequest;
11531         if(!success){
11532             this.fireEvent("loadexception", this, o, response);
11533             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11534             return;
11535         }
11536         var result;
11537         try {
11538             result = o.reader.read(response);
11539         }catch(e){
11540             this.fireEvent("loadexception", this, o, response, e);
11541             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11542             return;
11543         }
11544         
11545         this.fireEvent("load", this, o, o.request.arg);
11546         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11547     },
11548
11549     // private
11550     update : function(dataSet){
11551
11552     },
11553
11554     // private
11555     updateResponse : function(dataSet){
11556
11557     }
11558 });/*
11559  * Based on:
11560  * Ext JS Library 1.1.1
11561  * Copyright(c) 2006-2007, Ext JS, LLC.
11562  *
11563  * Originally Released Under LGPL - original licence link has changed is not relivant.
11564  *
11565  * Fork - LGPL
11566  * <script type="text/javascript">
11567  */
11568
11569 /**
11570  * @class Roo.data.ScriptTagProxy
11571  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11572  * other than the originating domain of the running page.<br><br>
11573  * <p>
11574  * <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
11575  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11576  * <p>
11577  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11578  * source code that is used as the source inside a &lt;script> tag.<br><br>
11579  * <p>
11580  * In order for the browser to process the returned data, the server must wrap the data object
11581  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11582  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11583  * depending on whether the callback name was passed:
11584  * <p>
11585  * <pre><code>
11586 boolean scriptTag = false;
11587 String cb = request.getParameter("callback");
11588 if (cb != null) {
11589     scriptTag = true;
11590     response.setContentType("text/javascript");
11591 } else {
11592     response.setContentType("application/x-json");
11593 }
11594 Writer out = response.getWriter();
11595 if (scriptTag) {
11596     out.write(cb + "(");
11597 }
11598 out.print(dataBlock.toJsonString());
11599 if (scriptTag) {
11600     out.write(");");
11601 }
11602 </pre></code>
11603  *
11604  * @constructor
11605  * @param {Object} config A configuration object.
11606  */
11607 Roo.data.ScriptTagProxy = function(config){
11608     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11609     Roo.apply(this, config);
11610     this.head = document.getElementsByTagName("head")[0];
11611 };
11612
11613 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11614
11615 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11616     /**
11617      * @cfg {String} url The URL from which to request the data object.
11618      */
11619     /**
11620      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11621      */
11622     timeout : 30000,
11623     /**
11624      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11625      * the server the name of the callback function set up by the load call to process the returned data object.
11626      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11627      * javascript output which calls this named function passing the data object as its only parameter.
11628      */
11629     callbackParam : "callback",
11630     /**
11631      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11632      * name to the request.
11633      */
11634     nocache : true,
11635
11636     /**
11637      * Load data from the configured URL, read the data object into
11638      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11639      * process that block using the passed callback.
11640      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11641      * for the request to the remote server.
11642      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11643      * object into a block of Roo.data.Records.
11644      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11645      * The function must be passed <ul>
11646      * <li>The Record block object</li>
11647      * <li>The "arg" argument from the load function</li>
11648      * <li>A boolean success indicator</li>
11649      * </ul>
11650      * @param {Object} scope The scope in which to call the callback
11651      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11652      */
11653     load : function(params, reader, callback, scope, arg){
11654         if(this.fireEvent("beforeload", this, params) !== false){
11655
11656             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11657
11658             var url = this.url;
11659             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11660             if(this.nocache){
11661                 url += "&_dc=" + (new Date().getTime());
11662             }
11663             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11664             var trans = {
11665                 id : transId,
11666                 cb : "stcCallback"+transId,
11667                 scriptId : "stcScript"+transId,
11668                 params : params,
11669                 arg : arg,
11670                 url : url,
11671                 callback : callback,
11672                 scope : scope,
11673                 reader : reader
11674             };
11675             var conn = this;
11676
11677             window[trans.cb] = function(o){
11678                 conn.handleResponse(o, trans);
11679             };
11680
11681             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11682
11683             if(this.autoAbort !== false){
11684                 this.abort();
11685             }
11686
11687             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11688
11689             var script = document.createElement("script");
11690             script.setAttribute("src", url);
11691             script.setAttribute("type", "text/javascript");
11692             script.setAttribute("id", trans.scriptId);
11693             this.head.appendChild(script);
11694
11695             this.trans = trans;
11696         }else{
11697             callback.call(scope||this, null, arg, false);
11698         }
11699     },
11700
11701     // private
11702     isLoading : function(){
11703         return this.trans ? true : false;
11704     },
11705
11706     /**
11707      * Abort the current server request.
11708      */
11709     abort : function(){
11710         if(this.isLoading()){
11711             this.destroyTrans(this.trans);
11712         }
11713     },
11714
11715     // private
11716     destroyTrans : function(trans, isLoaded){
11717         this.head.removeChild(document.getElementById(trans.scriptId));
11718         clearTimeout(trans.timeoutId);
11719         if(isLoaded){
11720             window[trans.cb] = undefined;
11721             try{
11722                 delete window[trans.cb];
11723             }catch(e){}
11724         }else{
11725             // if hasn't been loaded, wait for load to remove it to prevent script error
11726             window[trans.cb] = function(){
11727                 window[trans.cb] = undefined;
11728                 try{
11729                     delete window[trans.cb];
11730                 }catch(e){}
11731             };
11732         }
11733     },
11734
11735     // private
11736     handleResponse : function(o, trans){
11737         this.trans = false;
11738         this.destroyTrans(trans, true);
11739         var result;
11740         try {
11741             result = trans.reader.readRecords(o);
11742         }catch(e){
11743             this.fireEvent("loadexception", this, o, trans.arg, e);
11744             trans.callback.call(trans.scope||window, null, trans.arg, false);
11745             return;
11746         }
11747         this.fireEvent("load", this, o, trans.arg);
11748         trans.callback.call(trans.scope||window, result, trans.arg, true);
11749     },
11750
11751     // private
11752     handleFailure : function(trans){
11753         this.trans = false;
11754         this.destroyTrans(trans, false);
11755         this.fireEvent("loadexception", this, null, trans.arg);
11756         trans.callback.call(trans.scope||window, null, trans.arg, false);
11757     }
11758 });/*
11759  * Based on:
11760  * Ext JS Library 1.1.1
11761  * Copyright(c) 2006-2007, Ext JS, LLC.
11762  *
11763  * Originally Released Under LGPL - original licence link has changed is not relivant.
11764  *
11765  * Fork - LGPL
11766  * <script type="text/javascript">
11767  */
11768
11769 /**
11770  * @class Roo.data.JsonReader
11771  * @extends Roo.data.DataReader
11772  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11773  * based on mappings in a provided Roo.data.Record constructor.
11774  * 
11775  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11776  * in the reply previously. 
11777  * 
11778  * <p>
11779  * Example code:
11780  * <pre><code>
11781 var RecordDef = Roo.data.Record.create([
11782     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
11783     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
11784 ]);
11785 var myReader = new Roo.data.JsonReader({
11786     totalProperty: "results",    // The property which contains the total dataset size (optional)
11787     root: "rows",                // The property which contains an Array of row objects
11788     id: "id"                     // The property within each row object that provides an ID for the record (optional)
11789 }, RecordDef);
11790 </code></pre>
11791  * <p>
11792  * This would consume a JSON file like this:
11793  * <pre><code>
11794 { 'results': 2, 'rows': [
11795     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11796     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11797 }
11798 </code></pre>
11799  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11800  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11801  * paged from the remote server.
11802  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11803  * @cfg {String} root name of the property which contains the Array of row objects.
11804  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11805  * @cfg {Array} fields Array of field definition objects
11806  * @constructor
11807  * Create a new JsonReader
11808  * @param {Object} meta Metadata configuration options
11809  * @param {Object} recordType Either an Array of field definition objects,
11810  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11811  */
11812 Roo.data.JsonReader = function(meta, recordType){
11813     
11814     meta = meta || {};
11815     // set some defaults:
11816     Roo.applyIf(meta, {
11817         totalProperty: 'total',
11818         successProperty : 'success',
11819         root : 'data',
11820         id : 'id'
11821     });
11822     
11823     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11824 };
11825 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11826     
11827     /**
11828      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
11829      * Used by Store query builder to append _requestMeta to params.
11830      * 
11831      */
11832     metaFromRemote : false,
11833     /**
11834      * This method is only used by a DataProxy which has retrieved data from a remote server.
11835      * @param {Object} response The XHR object which contains the JSON data in its responseText.
11836      * @return {Object} data A data block which is used by an Roo.data.Store object as
11837      * a cache of Roo.data.Records.
11838      */
11839     read : function(response){
11840         var json = response.responseText;
11841        
11842         var o = /* eval:var:o */ eval("("+json+")");
11843         if(!o) {
11844             throw {message: "JsonReader.read: Json object not found"};
11845         }
11846         
11847         if(o.metaData){
11848             
11849             delete this.ef;
11850             this.metaFromRemote = true;
11851             this.meta = o.metaData;
11852             this.recordType = Roo.data.Record.create(o.metaData.fields);
11853             this.onMetaChange(this.meta, this.recordType, o);
11854         }
11855         return this.readRecords(o);
11856     },
11857
11858     // private function a store will implement
11859     onMetaChange : function(meta, recordType, o){
11860
11861     },
11862
11863     /**
11864          * @ignore
11865          */
11866     simpleAccess: function(obj, subsc) {
11867         return obj[subsc];
11868     },
11869
11870         /**
11871          * @ignore
11872          */
11873     getJsonAccessor: function(){
11874         var re = /[\[\.]/;
11875         return function(expr) {
11876             try {
11877                 return(re.test(expr))
11878                     ? new Function("obj", "return obj." + expr)
11879                     : function(obj){
11880                         return obj[expr];
11881                     };
11882             } catch(e){}
11883             return Roo.emptyFn;
11884         };
11885     }(),
11886
11887     /**
11888      * Create a data block containing Roo.data.Records from an XML document.
11889      * @param {Object} o An object which contains an Array of row objects in the property specified
11890      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11891      * which contains the total size of the dataset.
11892      * @return {Object} data A data block which is used by an Roo.data.Store object as
11893      * a cache of Roo.data.Records.
11894      */
11895     readRecords : function(o){
11896         /**
11897          * After any data loads, the raw JSON data is available for further custom processing.
11898          * @type Object
11899          */
11900         this.o = o;
11901         var s = this.meta, Record = this.recordType,
11902             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11903
11904 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
11905         if (!this.ef) {
11906             if(s.totalProperty) {
11907                     this.getTotal = this.getJsonAccessor(s.totalProperty);
11908                 }
11909                 if(s.successProperty) {
11910                     this.getSuccess = this.getJsonAccessor(s.successProperty);
11911                 }
11912                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11913                 if (s.id) {
11914                         var g = this.getJsonAccessor(s.id);
11915                         this.getId = function(rec) {
11916                                 var r = g(rec);  
11917                                 return (r === undefined || r === "") ? null : r;
11918                         };
11919                 } else {
11920                         this.getId = function(){return null;};
11921                 }
11922             this.ef = [];
11923             for(var jj = 0; jj < fl; jj++){
11924                 f = fi[jj];
11925                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11926                 this.ef[jj] = this.getJsonAccessor(map);
11927             }
11928         }
11929
11930         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11931         if(s.totalProperty){
11932             var vt = parseInt(this.getTotal(o), 10);
11933             if(!isNaN(vt)){
11934                 totalRecords = vt;
11935             }
11936         }
11937         if(s.successProperty){
11938             var vs = this.getSuccess(o);
11939             if(vs === false || vs === 'false'){
11940                 success = false;
11941             }
11942         }
11943         var records = [];
11944         for(var i = 0; i < c; i++){
11945                 var n = root[i];
11946             var values = {};
11947             var id = this.getId(n);
11948             for(var j = 0; j < fl; j++){
11949                 f = fi[j];
11950             var v = this.ef[j](n);
11951             if (!f.convert) {
11952                 Roo.log('missing convert for ' + f.name);
11953                 Roo.log(f);
11954                 continue;
11955             }
11956             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11957             }
11958             var record = new Record(values, id);
11959             record.json = n;
11960             records[i] = record;
11961         }
11962         return {
11963             raw : o,
11964             success : success,
11965             records : records,
11966             totalRecords : totalRecords
11967         };
11968     }
11969 });/*
11970  * Based on:
11971  * Ext JS Library 1.1.1
11972  * Copyright(c) 2006-2007, Ext JS, LLC.
11973  *
11974  * Originally Released Under LGPL - original licence link has changed is not relivant.
11975  *
11976  * Fork - LGPL
11977  * <script type="text/javascript">
11978  */
11979
11980 /**
11981  * @class Roo.data.ArrayReader
11982  * @extends Roo.data.DataReader
11983  * Data reader class to create an Array of Roo.data.Record objects from an Array.
11984  * Each element of that Array represents a row of data fields. The
11985  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11986  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11987  * <p>
11988  * Example code:.
11989  * <pre><code>
11990 var RecordDef = Roo.data.Record.create([
11991     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11992     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11993 ]);
11994 var myReader = new Roo.data.ArrayReader({
11995     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11996 }, RecordDef);
11997 </code></pre>
11998  * <p>
11999  * This would consume an Array like this:
12000  * <pre><code>
12001 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12002   </code></pre>
12003  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12004  * @constructor
12005  * Create a new JsonReader
12006  * @param {Object} meta Metadata configuration options.
12007  * @param {Object} recordType Either an Array of field definition objects
12008  * as specified to {@link Roo.data.Record#create},
12009  * or an {@link Roo.data.Record} object
12010  * created using {@link Roo.data.Record#create}.
12011  */
12012 Roo.data.ArrayReader = function(meta, recordType){
12013     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12014 };
12015
12016 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12017     /**
12018      * Create a data block containing Roo.data.Records from an XML document.
12019      * @param {Object} o An Array of row objects which represents the dataset.
12020      * @return {Object} data A data block which is used by an Roo.data.Store object as
12021      * a cache of Roo.data.Records.
12022      */
12023     readRecords : function(o){
12024         var sid = this.meta ? this.meta.id : null;
12025         var recordType = this.recordType, fields = recordType.prototype.fields;
12026         var records = [];
12027         var root = o;
12028             for(var i = 0; i < root.length; i++){
12029                     var n = root[i];
12030                 var values = {};
12031                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12032                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12033                 var f = fields.items[j];
12034                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12035                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12036                 v = f.convert(v);
12037                 values[f.name] = v;
12038             }
12039                 var record = new recordType(values, id);
12040                 record.json = n;
12041                 records[records.length] = record;
12042             }
12043             return {
12044                 records : records,
12045                 totalRecords : records.length
12046             };
12047     }
12048 });/*
12049  * - LGPL
12050  * * 
12051  */
12052
12053 /**
12054  * @class Roo.bootstrap.ComboBox
12055  * @extends Roo.bootstrap.TriggerField
12056  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12057  * @cfg {Boolean} append (true|false) default false
12058  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12059  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12060  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12061  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12062  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12063  * @cfg {Boolean} animate default true
12064  * @cfg {Boolean} emptyResultText only for touch device
12065  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12066  * @constructor
12067  * Create a new ComboBox.
12068  * @param {Object} config Configuration options
12069  */
12070 Roo.bootstrap.ComboBox = function(config){
12071     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12072     this.addEvents({
12073         /**
12074          * @event expand
12075          * Fires when the dropdown list is expanded
12076              * @param {Roo.bootstrap.ComboBox} combo This combo box
12077              */
12078         'expand' : true,
12079         /**
12080          * @event collapse
12081          * Fires when the dropdown list is collapsed
12082              * @param {Roo.bootstrap.ComboBox} combo This combo box
12083              */
12084         'collapse' : true,
12085         /**
12086          * @event beforeselect
12087          * Fires before a list item is selected. Return false to cancel the selection.
12088              * @param {Roo.bootstrap.ComboBox} combo This combo box
12089              * @param {Roo.data.Record} record The data record returned from the underlying store
12090              * @param {Number} index The index of the selected item in the dropdown list
12091              */
12092         'beforeselect' : true,
12093         /**
12094          * @event select
12095          * Fires when a list item is selected
12096              * @param {Roo.bootstrap.ComboBox} combo This combo box
12097              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12098              * @param {Number} index The index of the selected item in the dropdown list
12099              */
12100         'select' : true,
12101         /**
12102          * @event beforequery
12103          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12104          * The event object passed has these properties:
12105              * @param {Roo.bootstrap.ComboBox} combo This combo box
12106              * @param {String} query The query
12107              * @param {Boolean} forceAll true to force "all" query
12108              * @param {Boolean} cancel true to cancel the query
12109              * @param {Object} e The query event object
12110              */
12111         'beforequery': true,
12112          /**
12113          * @event add
12114          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12115              * @param {Roo.bootstrap.ComboBox} combo This combo box
12116              */
12117         'add' : true,
12118         /**
12119          * @event edit
12120          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12121              * @param {Roo.bootstrap.ComboBox} combo This combo box
12122              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12123              */
12124         'edit' : true,
12125         /**
12126          * @event remove
12127          * Fires when the remove value from the combobox array
12128              * @param {Roo.bootstrap.ComboBox} combo This combo box
12129              */
12130         'remove' : true,
12131         /**
12132          * @event afterremove
12133          * Fires when the remove value from the combobox array
12134              * @param {Roo.bootstrap.ComboBox} combo This combo box
12135              */
12136         'afterremove' : true,
12137         /**
12138          * @event specialfilter
12139          * Fires when specialfilter
12140             * @param {Roo.bootstrap.ComboBox} combo This combo box
12141             */
12142         'specialfilter' : true,
12143         /**
12144          * @event tick
12145          * Fires when tick the element
12146             * @param {Roo.bootstrap.ComboBox} combo This combo box
12147             */
12148         'tick' : true,
12149         /**
12150          * @event touchviewdisplay
12151          * Fires when touch view require special display (default is using displayField)
12152             * @param {Roo.bootstrap.ComboBox} combo This combo box
12153             * @param {Object} cfg set html .
12154             */
12155         'touchviewdisplay' : true
12156         
12157     });
12158     
12159     this.item = [];
12160     this.tickItems = [];
12161     
12162     this.selectedIndex = -1;
12163     if(this.mode == 'local'){
12164         if(config.queryDelay === undefined){
12165             this.queryDelay = 10;
12166         }
12167         if(config.minChars === undefined){
12168             this.minChars = 0;
12169         }
12170     }
12171 };
12172
12173 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12174      
12175     /**
12176      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12177      * rendering into an Roo.Editor, defaults to false)
12178      */
12179     /**
12180      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12181      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12182      */
12183     /**
12184      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12185      */
12186     /**
12187      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12188      * the dropdown list (defaults to undefined, with no header element)
12189      */
12190
12191      /**
12192      * @cfg {String/Roo.Template} tpl The template to use to render the output
12193      */
12194      
12195      /**
12196      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12197      */
12198     listWidth: undefined,
12199     /**
12200      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12201      * mode = 'remote' or 'text' if mode = 'local')
12202      */
12203     displayField: undefined,
12204     
12205     /**
12206      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12207      * mode = 'remote' or 'value' if mode = 'local'). 
12208      * Note: use of a valueField requires the user make a selection
12209      * in order for a value to be mapped.
12210      */
12211     valueField: undefined,
12212     /**
12213      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12214      */
12215     modalTitle : '',
12216     
12217     /**
12218      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12219      * field's data value (defaults to the underlying DOM element's name)
12220      */
12221     hiddenName: undefined,
12222     /**
12223      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12224      */
12225     listClass: '',
12226     /**
12227      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12228      */
12229     selectedClass: 'active',
12230     
12231     /**
12232      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12233      */
12234     shadow:'sides',
12235     /**
12236      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12237      * anchor positions (defaults to 'tl-bl')
12238      */
12239     listAlign: 'tl-bl?',
12240     /**
12241      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12242      */
12243     maxHeight: 300,
12244     /**
12245      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12246      * query specified by the allQuery config option (defaults to 'query')
12247      */
12248     triggerAction: 'query',
12249     /**
12250      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12251      * (defaults to 4, does not apply if editable = false)
12252      */
12253     minChars : 4,
12254     /**
12255      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12256      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12257      */
12258     typeAhead: false,
12259     /**
12260      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12261      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12262      */
12263     queryDelay: 500,
12264     /**
12265      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12266      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12267      */
12268     pageSize: 0,
12269     /**
12270      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12271      * when editable = true (defaults to false)
12272      */
12273     selectOnFocus:false,
12274     /**
12275      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12276      */
12277     queryParam: 'query',
12278     /**
12279      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12280      * when mode = 'remote' (defaults to 'Loading...')
12281      */
12282     loadingText: 'Loading...',
12283     /**
12284      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12285      */
12286     resizable: false,
12287     /**
12288      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12289      */
12290     handleHeight : 8,
12291     /**
12292      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12293      * traditional select (defaults to true)
12294      */
12295     editable: true,
12296     /**
12297      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12298      */
12299     allQuery: '',
12300     /**
12301      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12302      */
12303     mode: 'remote',
12304     /**
12305      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12306      * listWidth has a higher value)
12307      */
12308     minListWidth : 70,
12309     /**
12310      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12311      * allow the user to set arbitrary text into the field (defaults to false)
12312      */
12313     forceSelection:false,
12314     /**
12315      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12316      * if typeAhead = true (defaults to 250)
12317      */
12318     typeAheadDelay : 250,
12319     /**
12320      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12321      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12322      */
12323     valueNotFoundText : undefined,
12324     /**
12325      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12326      */
12327     blockFocus : false,
12328     
12329     /**
12330      * @cfg {Boolean} disableClear Disable showing of clear button.
12331      */
12332     disableClear : false,
12333     /**
12334      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12335      */
12336     alwaysQuery : false,
12337     
12338     /**
12339      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12340      */
12341     multiple : false,
12342     
12343     /**
12344      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12345      */
12346     invalidClass : "has-warning",
12347     
12348     /**
12349      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12350      */
12351     validClass : "has-success",
12352     
12353     /**
12354      * @cfg {Boolean} specialFilter (true|false) special filter default false
12355      */
12356     specialFilter : false,
12357     
12358     /**
12359      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12360      */
12361     mobileTouchView : true,
12362     
12363     /**
12364      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12365      */
12366     useNativeIOS : false,
12367     
12368     ios_options : false,
12369     
12370     //private
12371     addicon : false,
12372     editicon: false,
12373     
12374     page: 0,
12375     hasQuery: false,
12376     append: false,
12377     loadNext: false,
12378     autoFocus : true,
12379     tickable : false,
12380     btnPosition : 'right',
12381     triggerList : true,
12382     showToggleBtn : true,
12383     animate : true,
12384     emptyResultText: 'Empty',
12385     triggerText : 'Select',
12386     
12387     // element that contains real text value.. (when hidden is used..)
12388     
12389     getAutoCreate : function()
12390     {
12391         var cfg = false;
12392         
12393         /*
12394          * Render classic select for iso
12395          */
12396         
12397         if(Roo.isIOS && this.useNativeIOS){
12398             cfg = this.getAutoCreateNativeIOS();
12399             return cfg;
12400         }
12401         
12402         /*
12403          * Touch Devices
12404          */
12405         
12406         if(Roo.isTouch && this.mobileTouchView){
12407             cfg = this.getAutoCreateTouchView();
12408             return cfg;;
12409         }
12410         
12411         /*
12412          *  Normal ComboBox
12413          */
12414         if(!this.tickable){
12415             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12416             return cfg;
12417         }
12418         
12419         /*
12420          *  ComboBox with tickable selections
12421          */
12422              
12423         var align = this.labelAlign || this.parentLabelAlign();
12424         
12425         cfg = {
12426             cls : 'form-group roo-combobox-tickable' //input-group
12427         };
12428         
12429         var btn_text_select = '';
12430         var btn_text_done = '';
12431         var btn_text_cancel = '';
12432         
12433         if (this.btn_text_show) {
12434             btn_text_select = 'Select';
12435             btn_text_done = 'Done';
12436             btn_text_cancel = 'Cancel'; 
12437         }
12438         
12439         var buttons = {
12440             tag : 'div',
12441             cls : 'tickable-buttons',
12442             cn : [
12443                 {
12444                     tag : 'button',
12445                     type : 'button',
12446                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12447                     //html : this.triggerText
12448                     html: btn_text_select
12449                 },
12450                 {
12451                     tag : 'button',
12452                     type : 'button',
12453                     name : 'ok',
12454                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12455                     //html : 'Done'
12456                     html: btn_text_done
12457                 },
12458                 {
12459                     tag : 'button',
12460                     type : 'button',
12461                     name : 'cancel',
12462                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12463                     //html : 'Cancel'
12464                     html: btn_text_cancel
12465                 }
12466             ]
12467         };
12468         
12469         if(this.editable){
12470             buttons.cn.unshift({
12471                 tag: 'input',
12472                 cls: 'roo-select2-search-field-input'
12473             });
12474         }
12475         
12476         var _this = this;
12477         
12478         Roo.each(buttons.cn, function(c){
12479             if (_this.size) {
12480                 c.cls += ' btn-' + _this.size;
12481             }
12482
12483             if (_this.disabled) {
12484                 c.disabled = true;
12485             }
12486         });
12487         
12488         var box = {
12489             tag: 'div',
12490             cn: [
12491                 {
12492                     tag: 'input',
12493                     type : 'hidden',
12494                     cls: 'form-hidden-field'
12495                 },
12496                 {
12497                     tag: 'ul',
12498                     cls: 'roo-select2-choices',
12499                     cn:[
12500                         {
12501                             tag: 'li',
12502                             cls: 'roo-select2-search-field',
12503                             cn: [
12504
12505                                 buttons
12506                             ]
12507                         }
12508                     ]
12509                 }
12510             ]
12511         };
12512         
12513         var combobox = {
12514             cls: 'roo-select2-container input-group roo-select2-container-multi',
12515             cn: [
12516                 box
12517 //                {
12518 //                    tag: 'ul',
12519 //                    cls: 'typeahead typeahead-long dropdown-menu',
12520 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12521 //                }
12522             ]
12523         };
12524         
12525         if(this.hasFeedback && !this.allowBlank){
12526             
12527             var feedback = {
12528                 tag: 'span',
12529                 cls: 'glyphicon form-control-feedback'
12530             };
12531
12532             combobox.cn.push(feedback);
12533         }
12534         
12535         if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
12536             
12537 //                Roo.log("left and has label");
12538             cfg.cn = [
12539                 {
12540                     tag : 'i',
12541                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12542                     tooltip : 'This field is required'
12543                 },
12544                 {
12545                     tag: 'label',
12546                     'for' :  id,
12547                     cls : 'control-label col-sm-' + this.labelWidth,
12548                     html : this.fieldLabel
12549
12550                 },
12551                 {
12552                     cls : "col-sm-" + (12 - this.labelWidth), 
12553                     cn: [
12554                         combobox
12555                     ]
12556                 }
12557
12558             ];
12559
12560             if(this.indicatorpos == 'right'){
12561                 
12562                 cfg.cn = [
12563                     {
12564                         tag: 'label',
12565                         'for' :  id,
12566                         cls : 'control-label col-sm-' + this.labelWidth,
12567                         html : this.fieldLabel
12568
12569                     },
12570                     {
12571                         tag : 'i',
12572                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12573                         tooltip : 'This field is required'
12574                     },
12575                     {
12576                         cls : "col-sm-" + (12 - this.labelWidth), 
12577                         cn: [
12578                             combobox
12579                         ]
12580                     }
12581
12582                 ];
12583             
12584             }
12585                 
12586                 
12587         } else if ( this.fieldLabel.length) {
12588 //                Roo.log(" label");
12589                  cfg.cn = [
12590                     {
12591                         tag : 'i',
12592                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12593                         tooltip : 'This field is required'
12594                     },
12595                     {
12596                         tag: 'label',
12597                         //cls : 'input-group-addon',
12598                         html : this.fieldLabel
12599                         
12600                     },
12601                     
12602                     combobox
12603                     
12604                 ];
12605                 
12606                 if(this.indicatorpos == 'right'){
12607                     
12608                     cfg.cn = [
12609                         {
12610                             tag: 'label',
12611                             //cls : 'input-group-addon',
12612                             html : this.fieldLabel
12613
12614                         },
12615                         
12616                         {
12617                             tag : 'i',
12618                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12619                             tooltip : 'This field is required'
12620                         },
12621                         
12622                         combobox
12623
12624                     ];
12625                 
12626                 }
12627
12628         } else {
12629             
12630 //                Roo.log(" no label && no align");
12631                 cfg = combobox
12632                      
12633                 
12634         }
12635          
12636         var settings=this;
12637         ['xs','sm','md','lg'].map(function(size){
12638             if (settings[size]) {
12639                 cfg.cls += ' col-' + size + '-' + settings[size];
12640             }
12641         });
12642         
12643         return cfg;
12644         
12645     },
12646     
12647     _initEventsCalled : false,
12648     
12649     // private
12650     initEvents: function()
12651     {   
12652         if (this._initEventsCalled) { // as we call render... prevent looping...
12653             return;
12654         }
12655         this._initEventsCalled = true;
12656         
12657         if (!this.store) {
12658             throw "can not find store for combo";
12659         }
12660         
12661         this.store = Roo.factory(this.store, Roo.data);
12662         
12663         // if we are building from html. then this element is so complex, that we can not really
12664         // use the rendered HTML.
12665         // so we have to trash and replace the previous code.
12666         if (Roo.XComponent.build_from_html) {
12667             
12668             // remove this element....
12669             var e = this.el.dom, k=0;
12670             while (e ) { e = e.previousSibling;  ++k;}
12671
12672             this.el.remove();
12673             
12674             this.el=false;
12675             this.rendered = false;
12676             
12677             this.render(this.parent().getChildContainer(true), k);
12678             
12679             
12680             
12681         }
12682         
12683         if(Roo.isIOS && this.useNativeIOS){
12684             this.initIOSView();
12685             return;
12686         }
12687         
12688         /*
12689          * Touch Devices
12690          */
12691         
12692         if(Roo.isTouch && this.mobileTouchView){
12693             this.initTouchView();
12694             return;
12695         }
12696         
12697         if(this.tickable){
12698             this.initTickableEvents();
12699             return;
12700         }
12701         
12702         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12703         
12704         if(this.hiddenName){
12705             
12706             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12707             
12708             this.hiddenField.dom.value =
12709                 this.hiddenValue !== undefined ? this.hiddenValue :
12710                 this.value !== undefined ? this.value : '';
12711
12712             // prevent input submission
12713             this.el.dom.removeAttribute('name');
12714             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12715              
12716              
12717         }
12718         //if(Roo.isGecko){
12719         //    this.el.dom.setAttribute('autocomplete', 'off');
12720         //}
12721         
12722         var cls = 'x-combo-list';
12723         
12724         //this.list = new Roo.Layer({
12725         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12726         //});
12727         
12728         var _this = this;
12729         
12730         (function(){
12731             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12732             _this.list.setWidth(lw);
12733         }).defer(100);
12734         
12735         this.list.on('mouseover', this.onViewOver, this);
12736         this.list.on('mousemove', this.onViewMove, this);
12737         
12738         this.list.on('scroll', this.onViewScroll, this);
12739         
12740         /*
12741         this.list.swallowEvent('mousewheel');
12742         this.assetHeight = 0;
12743
12744         if(this.title){
12745             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12746             this.assetHeight += this.header.getHeight();
12747         }
12748
12749         this.innerList = this.list.createChild({cls:cls+'-inner'});
12750         this.innerList.on('mouseover', this.onViewOver, this);
12751         this.innerList.on('mousemove', this.onViewMove, this);
12752         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12753         
12754         if(this.allowBlank && !this.pageSize && !this.disableClear){
12755             this.footer = this.list.createChild({cls:cls+'-ft'});
12756             this.pageTb = new Roo.Toolbar(this.footer);
12757            
12758         }
12759         if(this.pageSize){
12760             this.footer = this.list.createChild({cls:cls+'-ft'});
12761             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12762                     {pageSize: this.pageSize});
12763             
12764         }
12765         
12766         if (this.pageTb && this.allowBlank && !this.disableClear) {
12767             var _this = this;
12768             this.pageTb.add(new Roo.Toolbar.Fill(), {
12769                 cls: 'x-btn-icon x-btn-clear',
12770                 text: '&#160;',
12771                 handler: function()
12772                 {
12773                     _this.collapse();
12774                     _this.clearValue();
12775                     _this.onSelect(false, -1);
12776                 }
12777             });
12778         }
12779         if (this.footer) {
12780             this.assetHeight += this.footer.getHeight();
12781         }
12782         */
12783             
12784         if(!this.tpl){
12785             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12786         }
12787
12788         this.view = new Roo.View(this.list, this.tpl, {
12789             singleSelect:true, store: this.store, selectedClass: this.selectedClass
12790         });
12791         //this.view.wrapEl.setDisplayed(false);
12792         this.view.on('click', this.onViewClick, this);
12793         
12794         
12795         
12796         this.store.on('beforeload', this.onBeforeLoad, this);
12797         this.store.on('load', this.onLoad, this);
12798         this.store.on('loadexception', this.onLoadException, this);
12799         /*
12800         if(this.resizable){
12801             this.resizer = new Roo.Resizable(this.list,  {
12802                pinned:true, handles:'se'
12803             });
12804             this.resizer.on('resize', function(r, w, h){
12805                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12806                 this.listWidth = w;
12807                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12808                 this.restrictHeight();
12809             }, this);
12810             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12811         }
12812         */
12813         if(!this.editable){
12814             this.editable = true;
12815             this.setEditable(false);
12816         }
12817         
12818         /*
12819         
12820         if (typeof(this.events.add.listeners) != 'undefined') {
12821             
12822             this.addicon = this.wrap.createChild(
12823                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
12824        
12825             this.addicon.on('click', function(e) {
12826                 this.fireEvent('add', this);
12827             }, this);
12828         }
12829         if (typeof(this.events.edit.listeners) != 'undefined') {
12830             
12831             this.editicon = this.wrap.createChild(
12832                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
12833             if (this.addicon) {
12834                 this.editicon.setStyle('margin-left', '40px');
12835             }
12836             this.editicon.on('click', function(e) {
12837                 
12838                 // we fire even  if inothing is selected..
12839                 this.fireEvent('edit', this, this.lastData );
12840                 
12841             }, this);
12842         }
12843         */
12844         
12845         this.keyNav = new Roo.KeyNav(this.inputEl(), {
12846             "up" : function(e){
12847                 this.inKeyMode = true;
12848                 this.selectPrev();
12849             },
12850
12851             "down" : function(e){
12852                 if(!this.isExpanded()){
12853                     this.onTriggerClick();
12854                 }else{
12855                     this.inKeyMode = true;
12856                     this.selectNext();
12857                 }
12858             },
12859
12860             "enter" : function(e){
12861 //                this.onViewClick();
12862                 //return true;
12863                 this.collapse();
12864                 
12865                 if(this.fireEvent("specialkey", this, e)){
12866                     this.onViewClick(false);
12867                 }
12868                 
12869                 return true;
12870             },
12871
12872             "esc" : function(e){
12873                 this.collapse();
12874             },
12875
12876             "tab" : function(e){
12877                 this.collapse();
12878                 
12879                 if(this.fireEvent("specialkey", this, e)){
12880                     this.onViewClick(false);
12881                 }
12882                 
12883                 return true;
12884             },
12885
12886             scope : this,
12887
12888             doRelay : function(foo, bar, hname){
12889                 if(hname == 'down' || this.scope.isExpanded()){
12890                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12891                 }
12892                 return true;
12893             },
12894
12895             forceKeyDown: true
12896         });
12897         
12898         
12899         this.queryDelay = Math.max(this.queryDelay || 10,
12900                 this.mode == 'local' ? 10 : 250);
12901         
12902         
12903         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12904         
12905         if(this.typeAhead){
12906             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12907         }
12908         if(this.editable !== false){
12909             this.inputEl().on("keyup", this.onKeyUp, this);
12910         }
12911         if(this.forceSelection){
12912             this.inputEl().on('blur', this.doForce, this);
12913         }
12914         
12915         if(this.multiple){
12916             this.choices = this.el.select('ul.roo-select2-choices', true).first();
12917             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12918         }
12919     },
12920     
12921     initTickableEvents: function()
12922     {   
12923         this.createList();
12924         
12925         if(this.hiddenName){
12926             
12927             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12928             
12929             this.hiddenField.dom.value =
12930                 this.hiddenValue !== undefined ? this.hiddenValue :
12931                 this.value !== undefined ? this.value : '';
12932
12933             // prevent input submission
12934             this.el.dom.removeAttribute('name');
12935             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12936              
12937              
12938         }
12939         
12940 //        this.list = this.el.select('ul.dropdown-menu',true).first();
12941         
12942         this.choices = this.el.select('ul.roo-select2-choices', true).first();
12943         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12944         if(this.triggerList){
12945             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12946         }
12947          
12948         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12949         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12950         
12951         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12952         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12953         
12954         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12955         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12956         
12957         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12958         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12959         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12960         
12961         this.okBtn.hide();
12962         this.cancelBtn.hide();
12963         
12964         var _this = this;
12965         
12966         (function(){
12967             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12968             _this.list.setWidth(lw);
12969         }).defer(100);
12970         
12971         this.list.on('mouseover', this.onViewOver, this);
12972         this.list.on('mousemove', this.onViewMove, this);
12973         
12974         this.list.on('scroll', this.onViewScroll, this);
12975         
12976         if(!this.tpl){
12977             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>';
12978         }
12979
12980         this.view = new Roo.View(this.list, this.tpl, {
12981             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12982         });
12983         
12984         //this.view.wrapEl.setDisplayed(false);
12985         this.view.on('click', this.onViewClick, this);
12986         
12987         
12988         
12989         this.store.on('beforeload', this.onBeforeLoad, this);
12990         this.store.on('load', this.onLoad, this);
12991         this.store.on('loadexception', this.onLoadException, this);
12992         
12993         if(this.editable){
12994             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12995                 "up" : function(e){
12996                     this.inKeyMode = true;
12997                     this.selectPrev();
12998                 },
12999
13000                 "down" : function(e){
13001                     this.inKeyMode = true;
13002                     this.selectNext();
13003                 },
13004
13005                 "enter" : function(e){
13006                     if(this.fireEvent("specialkey", this, e)){
13007                         this.onViewClick(false);
13008                     }
13009                     
13010                     return true;
13011                 },
13012
13013                 "esc" : function(e){
13014                     this.onTickableFooterButtonClick(e, false, false);
13015                 },
13016
13017                 "tab" : function(e){
13018                     this.fireEvent("specialkey", this, e);
13019                     
13020                     this.onTickableFooterButtonClick(e, false, false);
13021                     
13022                     return true;
13023                 },
13024
13025                 scope : this,
13026
13027                 doRelay : function(e, fn, key){
13028                     if(this.scope.isExpanded()){
13029                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13030                     }
13031                     return true;
13032                 },
13033
13034                 forceKeyDown: true
13035             });
13036         }
13037         
13038         this.queryDelay = Math.max(this.queryDelay || 10,
13039                 this.mode == 'local' ? 10 : 250);
13040         
13041         
13042         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13043         
13044         if(this.typeAhead){
13045             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13046         }
13047         
13048         if(this.editable !== false){
13049             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13050         }
13051         
13052     },
13053
13054     onDestroy : function(){
13055         if(this.view){
13056             this.view.setStore(null);
13057             this.view.el.removeAllListeners();
13058             this.view.el.remove();
13059             this.view.purgeListeners();
13060         }
13061         if(this.list){
13062             this.list.dom.innerHTML  = '';
13063         }
13064         
13065         if(this.store){
13066             this.store.un('beforeload', this.onBeforeLoad, this);
13067             this.store.un('load', this.onLoad, this);
13068             this.store.un('loadexception', this.onLoadException, this);
13069         }
13070         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13071     },
13072
13073     // private
13074     fireKey : function(e){
13075         if(e.isNavKeyPress() && !this.list.isVisible()){
13076             this.fireEvent("specialkey", this, e);
13077         }
13078     },
13079
13080     // private
13081     onResize: function(w, h){
13082 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13083 //        
13084 //        if(typeof w != 'number'){
13085 //            // we do not handle it!?!?
13086 //            return;
13087 //        }
13088 //        var tw = this.trigger.getWidth();
13089 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13090 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13091 //        var x = w - tw;
13092 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13093 //            
13094 //        //this.trigger.setStyle('left', x+'px');
13095 //        
13096 //        if(this.list && this.listWidth === undefined){
13097 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13098 //            this.list.setWidth(lw);
13099 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13100 //        }
13101         
13102     
13103         
13104     },
13105
13106     /**
13107      * Allow or prevent the user from directly editing the field text.  If false is passed,
13108      * the user will only be able to select from the items defined in the dropdown list.  This method
13109      * is the runtime equivalent of setting the 'editable' config option at config time.
13110      * @param {Boolean} value True to allow the user to directly edit the field text
13111      */
13112     setEditable : function(value){
13113         if(value == this.editable){
13114             return;
13115         }
13116         this.editable = value;
13117         if(!value){
13118             this.inputEl().dom.setAttribute('readOnly', true);
13119             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13120             this.inputEl().addClass('x-combo-noedit');
13121         }else{
13122             this.inputEl().dom.setAttribute('readOnly', false);
13123             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13124             this.inputEl().removeClass('x-combo-noedit');
13125         }
13126     },
13127
13128     // private
13129     
13130     onBeforeLoad : function(combo,opts){
13131         if(!this.hasFocus){
13132             return;
13133         }
13134          if (!opts.add) {
13135             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13136          }
13137         this.restrictHeight();
13138         this.selectedIndex = -1;
13139     },
13140
13141     // private
13142     onLoad : function(){
13143         
13144         this.hasQuery = false;
13145         
13146         if(!this.hasFocus){
13147             return;
13148         }
13149         
13150         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13151             this.loading.hide();
13152         }
13153              
13154         if(this.store.getCount() > 0){
13155             this.expand();
13156             this.restrictHeight();
13157             if(this.lastQuery == this.allQuery){
13158                 if(this.editable && !this.tickable){
13159                     this.inputEl().dom.select();
13160                 }
13161                 
13162                 if(
13163                     !this.selectByValue(this.value, true) &&
13164                     this.autoFocus && 
13165                     (
13166                         !this.store.lastOptions ||
13167                         typeof(this.store.lastOptions.add) == 'undefined' || 
13168                         this.store.lastOptions.add != true
13169                     )
13170                 ){
13171                     this.select(0, true);
13172                 }
13173             }else{
13174                 if(this.autoFocus){
13175                     this.selectNext();
13176                 }
13177                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13178                     this.taTask.delay(this.typeAheadDelay);
13179                 }
13180             }
13181         }else{
13182             this.onEmptyResults();
13183         }
13184         
13185         //this.el.focus();
13186     },
13187     // private
13188     onLoadException : function()
13189     {
13190         this.hasQuery = false;
13191         
13192         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13193             this.loading.hide();
13194         }
13195         
13196         if(this.tickable && this.editable){
13197             return;
13198         }
13199         
13200         this.collapse();
13201         // only causes errors at present
13202         //Roo.log(this.store.reader.jsonData);
13203         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13204             // fixme
13205             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13206         //}
13207         
13208         
13209     },
13210     // private
13211     onTypeAhead : function(){
13212         if(this.store.getCount() > 0){
13213             var r = this.store.getAt(0);
13214             var newValue = r.data[this.displayField];
13215             var len = newValue.length;
13216             var selStart = this.getRawValue().length;
13217             
13218             if(selStart != len){
13219                 this.setRawValue(newValue);
13220                 this.selectText(selStart, newValue.length);
13221             }
13222         }
13223     },
13224
13225     // private
13226     onSelect : function(record, index){
13227         
13228         if(this.fireEvent('beforeselect', this, record, index) !== false){
13229         
13230             this.setFromData(index > -1 ? record.data : false);
13231             
13232             this.collapse();
13233             this.fireEvent('select', this, record, index);
13234         }
13235     },
13236
13237     /**
13238      * Returns the currently selected field value or empty string if no value is set.
13239      * @return {String} value The selected value
13240      */
13241     getValue : function()
13242     {
13243         if(Roo.isIOS && this.useNativeIOS){
13244             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13245         }
13246         
13247         if(this.multiple){
13248             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13249         }
13250         
13251         if(this.valueField){
13252             return typeof this.value != 'undefined' ? this.value : '';
13253         }else{
13254             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13255         }
13256     },
13257     
13258     getRawValue : function()
13259     {
13260         if(Roo.isIOS && this.useNativeIOS){
13261             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13262         }
13263         
13264         var v = this.inputEl().getValue();
13265         
13266         return v;
13267     },
13268
13269     /**
13270      * Clears any text/value currently set in the field
13271      */
13272     clearValue : function(){
13273         
13274         if(this.hiddenField){
13275             this.hiddenField.dom.value = '';
13276         }
13277         this.value = '';
13278         this.setRawValue('');
13279         this.lastSelectionText = '';
13280         this.lastData = false;
13281         
13282         var close = this.closeTriggerEl();
13283         
13284         if(close){
13285             close.hide();
13286         }
13287         
13288         this.validate();
13289         
13290     },
13291
13292     /**
13293      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13294      * will be displayed in the field.  If the value does not match the data value of an existing item,
13295      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13296      * Otherwise the field will be blank (although the value will still be set).
13297      * @param {String} value The value to match
13298      */
13299     setValue : function(v)
13300     {
13301         if(Roo.isIOS && this.useNativeIOS){
13302             this.setIOSValue(v);
13303             return;
13304         }
13305         
13306         if(this.multiple){
13307             this.syncValue();
13308             return;
13309         }
13310         
13311         var text = v;
13312         if(this.valueField){
13313             var r = this.findRecord(this.valueField, v);
13314             if(r){
13315                 text = r.data[this.displayField];
13316             }else if(this.valueNotFoundText !== undefined){
13317                 text = this.valueNotFoundText;
13318             }
13319         }
13320         this.lastSelectionText = text;
13321         if(this.hiddenField){
13322             this.hiddenField.dom.value = v;
13323         }
13324         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13325         this.value = v;
13326         
13327         var close = this.closeTriggerEl();
13328         
13329         if(close){
13330             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13331         }
13332         
13333         this.validate();
13334     },
13335     /**
13336      * @property {Object} the last set data for the element
13337      */
13338     
13339     lastData : false,
13340     /**
13341      * Sets the value of the field based on a object which is related to the record format for the store.
13342      * @param {Object} value the value to set as. or false on reset?
13343      */
13344     setFromData : function(o){
13345         
13346         if(this.multiple){
13347             this.addItem(o);
13348             return;
13349         }
13350             
13351         var dv = ''; // display value
13352         var vv = ''; // value value..
13353         this.lastData = o;
13354         if (this.displayField) {
13355             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13356         } else {
13357             // this is an error condition!!!
13358             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13359         }
13360         
13361         if(this.valueField){
13362             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13363         }
13364         
13365         var close = this.closeTriggerEl();
13366         
13367         if(close){
13368             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13369         }
13370         
13371         if(this.hiddenField){
13372             this.hiddenField.dom.value = vv;
13373             
13374             this.lastSelectionText = dv;
13375             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13376             this.value = vv;
13377             return;
13378         }
13379         // no hidden field.. - we store the value in 'value', but still display
13380         // display field!!!!
13381         this.lastSelectionText = dv;
13382         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13383         this.value = vv;
13384         
13385         
13386         
13387     },
13388     // private
13389     reset : function(){
13390         // overridden so that last data is reset..
13391         
13392         if(this.multiple){
13393             this.clearItem();
13394             return;
13395         }
13396         
13397         this.setValue(this.originalValue);
13398         //this.clearInvalid();
13399         this.lastData = false;
13400         if (this.view) {
13401             this.view.clearSelections();
13402         }
13403         
13404         this.validate();
13405     },
13406     // private
13407     findRecord : function(prop, value){
13408         var record;
13409         if(this.store.getCount() > 0){
13410             this.store.each(function(r){
13411                 if(r.data[prop] == value){
13412                     record = r;
13413                     return false;
13414                 }
13415                 return true;
13416             });
13417         }
13418         return record;
13419     },
13420     
13421     getName: function()
13422     {
13423         // returns hidden if it's set..
13424         if (!this.rendered) {return ''};
13425         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13426         
13427     },
13428     // private
13429     onViewMove : function(e, t){
13430         this.inKeyMode = false;
13431     },
13432
13433     // private
13434     onViewOver : function(e, t){
13435         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13436             return;
13437         }
13438         var item = this.view.findItemFromChild(t);
13439         
13440         if(item){
13441             var index = this.view.indexOf(item);
13442             this.select(index, false);
13443         }
13444     },
13445
13446     // private
13447     onViewClick : function(view, doFocus, el, e)
13448     {
13449         var index = this.view.getSelectedIndexes()[0];
13450         
13451         var r = this.store.getAt(index);
13452         
13453         if(this.tickable){
13454             
13455             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13456                 return;
13457             }
13458             
13459             var rm = false;
13460             var _this = this;
13461             
13462             Roo.each(this.tickItems, function(v,k){
13463                 
13464                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13465                     Roo.log(v);
13466                     _this.tickItems.splice(k, 1);
13467                     
13468                     if(typeof(e) == 'undefined' && view == false){
13469                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13470                     }
13471                     
13472                     rm = true;
13473                     return;
13474                 }
13475             });
13476             
13477             if(rm){
13478                 return;
13479             }
13480             
13481             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13482                 this.tickItems.push(r.data);
13483             }
13484             
13485             if(typeof(e) == 'undefined' && view == false){
13486                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13487             }
13488                     
13489             return;
13490         }
13491         
13492         if(r){
13493             this.onSelect(r, index);
13494         }
13495         if(doFocus !== false && !this.blockFocus){
13496             this.inputEl().focus();
13497         }
13498     },
13499
13500     // private
13501     restrictHeight : function(){
13502         //this.innerList.dom.style.height = '';
13503         //var inner = this.innerList.dom;
13504         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13505         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13506         //this.list.beginUpdate();
13507         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13508         this.list.alignTo(this.inputEl(), this.listAlign);
13509         this.list.alignTo(this.inputEl(), this.listAlign);
13510         //this.list.endUpdate();
13511     },
13512
13513     // private
13514     onEmptyResults : function(){
13515         
13516         if(this.tickable && this.editable){
13517             this.restrictHeight();
13518             return;
13519         }
13520         
13521         this.collapse();
13522     },
13523
13524     /**
13525      * Returns true if the dropdown list is expanded, else false.
13526      */
13527     isExpanded : function(){
13528         return this.list.isVisible();
13529     },
13530
13531     /**
13532      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13533      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13534      * @param {String} value The data value of the item to select
13535      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13536      * selected item if it is not currently in view (defaults to true)
13537      * @return {Boolean} True if the value matched an item in the list, else false
13538      */
13539     selectByValue : function(v, scrollIntoView){
13540         if(v !== undefined && v !== null){
13541             var r = this.findRecord(this.valueField || this.displayField, v);
13542             if(r){
13543                 this.select(this.store.indexOf(r), scrollIntoView);
13544                 return true;
13545             }
13546         }
13547         return false;
13548     },
13549
13550     /**
13551      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13552      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13553      * @param {Number} index The zero-based index of the list item to select
13554      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13555      * selected item if it is not currently in view (defaults to true)
13556      */
13557     select : function(index, scrollIntoView){
13558         this.selectedIndex = index;
13559         this.view.select(index);
13560         if(scrollIntoView !== false){
13561             var el = this.view.getNode(index);
13562             /*
13563              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13564              */
13565             if(el){
13566                 this.list.scrollChildIntoView(el, false);
13567             }
13568         }
13569     },
13570
13571     // private
13572     selectNext : function(){
13573         var ct = this.store.getCount();
13574         if(ct > 0){
13575             if(this.selectedIndex == -1){
13576                 this.select(0);
13577             }else if(this.selectedIndex < ct-1){
13578                 this.select(this.selectedIndex+1);
13579             }
13580         }
13581     },
13582
13583     // private
13584     selectPrev : function(){
13585         var ct = this.store.getCount();
13586         if(ct > 0){
13587             if(this.selectedIndex == -1){
13588                 this.select(0);
13589             }else if(this.selectedIndex != 0){
13590                 this.select(this.selectedIndex-1);
13591             }
13592         }
13593     },
13594
13595     // private
13596     onKeyUp : function(e){
13597         if(this.editable !== false && !e.isSpecialKey()){
13598             this.lastKey = e.getKey();
13599             this.dqTask.delay(this.queryDelay);
13600         }
13601     },
13602
13603     // private
13604     validateBlur : function(){
13605         return !this.list || !this.list.isVisible();   
13606     },
13607
13608     // private
13609     initQuery : function(){
13610         
13611         var v = this.getRawValue();
13612         
13613         if(this.tickable && this.editable){
13614             v = this.tickableInputEl().getValue();
13615         }
13616         
13617         this.doQuery(v);
13618     },
13619
13620     // private
13621     doForce : function(){
13622         if(this.inputEl().dom.value.length > 0){
13623             this.inputEl().dom.value =
13624                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13625              
13626         }
13627     },
13628
13629     /**
13630      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
13631      * query allowing the query action to be canceled if needed.
13632      * @param {String} query The SQL query to execute
13633      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13634      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
13635      * saved in the current store (defaults to false)
13636      */
13637     doQuery : function(q, forceAll){
13638         
13639         if(q === undefined || q === null){
13640             q = '';
13641         }
13642         var qe = {
13643             query: q,
13644             forceAll: forceAll,
13645             combo: this,
13646             cancel:false
13647         };
13648         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13649             return false;
13650         }
13651         q = qe.query;
13652         
13653         forceAll = qe.forceAll;
13654         if(forceAll === true || (q.length >= this.minChars)){
13655             
13656             this.hasQuery = true;
13657             
13658             if(this.lastQuery != q || this.alwaysQuery){
13659                 this.lastQuery = q;
13660                 if(this.mode == 'local'){
13661                     this.selectedIndex = -1;
13662                     if(forceAll){
13663                         this.store.clearFilter();
13664                     }else{
13665                         
13666                         if(this.specialFilter){
13667                             this.fireEvent('specialfilter', this);
13668                             this.onLoad();
13669                             return;
13670                         }
13671                         
13672                         this.store.filter(this.displayField, q);
13673                     }
13674                     
13675                     this.store.fireEvent("datachanged", this.store);
13676                     
13677                     this.onLoad();
13678                     
13679                     
13680                 }else{
13681                     
13682                     this.store.baseParams[this.queryParam] = q;
13683                     
13684                     var options = {params : this.getParams(q)};
13685                     
13686                     if(this.loadNext){
13687                         options.add = true;
13688                         options.params.start = this.page * this.pageSize;
13689                     }
13690                     
13691                     this.store.load(options);
13692                     
13693                     /*
13694                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
13695                      *  we should expand the list on onLoad
13696                      *  so command out it
13697                      */
13698 //                    this.expand();
13699                 }
13700             }else{
13701                 this.selectedIndex = -1;
13702                 this.onLoad();   
13703             }
13704         }
13705         
13706         this.loadNext = false;
13707     },
13708     
13709     // private
13710     getParams : function(q){
13711         var p = {};
13712         //p[this.queryParam] = q;
13713         
13714         if(this.pageSize){
13715             p.start = 0;
13716             p.limit = this.pageSize;
13717         }
13718         return p;
13719     },
13720
13721     /**
13722      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13723      */
13724     collapse : function(){
13725         if(!this.isExpanded()){
13726             return;
13727         }
13728         
13729         this.list.hide();
13730         
13731         this.hasFocus = false;
13732         
13733         if(this.tickable){
13734             this.okBtn.hide();
13735             this.cancelBtn.hide();
13736             this.trigger.show();
13737             
13738             if(this.editable){
13739                 this.tickableInputEl().dom.value = '';
13740                 this.tickableInputEl().blur();
13741             }
13742             
13743         }
13744         
13745         Roo.get(document).un('mousedown', this.collapseIf, this);
13746         Roo.get(document).un('mousewheel', this.collapseIf, this);
13747         if (!this.editable) {
13748             Roo.get(document).un('keydown', this.listKeyPress, this);
13749         }
13750         this.fireEvent('collapse', this);
13751         
13752         this.validate();
13753     },
13754
13755     // private
13756     collapseIf : function(e){
13757         var in_combo  = e.within(this.el);
13758         var in_list =  e.within(this.list);
13759         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13760         
13761         if (in_combo || in_list || is_list) {
13762             //e.stopPropagation();
13763             return;
13764         }
13765         
13766         if(this.tickable){
13767             this.onTickableFooterButtonClick(e, false, false);
13768         }
13769
13770         this.collapse();
13771         
13772     },
13773
13774     /**
13775      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13776      */
13777     expand : function(){
13778        
13779         if(this.isExpanded() || !this.hasFocus){
13780             return;
13781         }
13782         
13783         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13784         this.list.setWidth(lw);
13785         
13786         Roo.log('expand');
13787         
13788         this.list.show();
13789         
13790         this.restrictHeight();
13791         
13792         if(this.tickable){
13793             
13794             this.tickItems = Roo.apply([], this.item);
13795             
13796             this.okBtn.show();
13797             this.cancelBtn.show();
13798             this.trigger.hide();
13799             
13800             if(this.editable){
13801                 this.tickableInputEl().focus();
13802             }
13803             
13804         }
13805         
13806         Roo.get(document).on('mousedown', this.collapseIf, this);
13807         Roo.get(document).on('mousewheel', this.collapseIf, this);
13808         if (!this.editable) {
13809             Roo.get(document).on('keydown', this.listKeyPress, this);
13810         }
13811         
13812         this.fireEvent('expand', this);
13813     },
13814
13815     // private
13816     // Implements the default empty TriggerField.onTriggerClick function
13817     onTriggerClick : function(e)
13818     {
13819         Roo.log('trigger click');
13820         
13821         if(this.disabled || !this.triggerList){
13822             return;
13823         }
13824         
13825         this.page = 0;
13826         this.loadNext = false;
13827         
13828         if(this.isExpanded()){
13829             this.collapse();
13830             if (!this.blockFocus) {
13831                 this.inputEl().focus();
13832             }
13833             
13834         }else {
13835             this.hasFocus = true;
13836             if(this.triggerAction == 'all') {
13837                 this.doQuery(this.allQuery, true);
13838             } else {
13839                 this.doQuery(this.getRawValue());
13840             }
13841             if (!this.blockFocus) {
13842                 this.inputEl().focus();
13843             }
13844         }
13845     },
13846     
13847     onTickableTriggerClick : function(e)
13848     {
13849         if(this.disabled){
13850             return;
13851         }
13852         
13853         this.page = 0;
13854         this.loadNext = false;
13855         this.hasFocus = true;
13856         
13857         if(this.triggerAction == 'all') {
13858             this.doQuery(this.allQuery, true);
13859         } else {
13860             this.doQuery(this.getRawValue());
13861         }
13862     },
13863     
13864     onSearchFieldClick : function(e)
13865     {
13866         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13867             this.onTickableFooterButtonClick(e, false, false);
13868             return;
13869         }
13870         
13871         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13872             return;
13873         }
13874         
13875         this.page = 0;
13876         this.loadNext = false;
13877         this.hasFocus = true;
13878         
13879         if(this.triggerAction == 'all') {
13880             this.doQuery(this.allQuery, true);
13881         } else {
13882             this.doQuery(this.getRawValue());
13883         }
13884     },
13885     
13886     listKeyPress : function(e)
13887     {
13888         //Roo.log('listkeypress');
13889         // scroll to first matching element based on key pres..
13890         if (e.isSpecialKey()) {
13891             return false;
13892         }
13893         var k = String.fromCharCode(e.getKey()).toUpperCase();
13894         //Roo.log(k);
13895         var match  = false;
13896         var csel = this.view.getSelectedNodes();
13897         var cselitem = false;
13898         if (csel.length) {
13899             var ix = this.view.indexOf(csel[0]);
13900             cselitem  = this.store.getAt(ix);
13901             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13902                 cselitem = false;
13903             }
13904             
13905         }
13906         
13907         this.store.each(function(v) { 
13908             if (cselitem) {
13909                 // start at existing selection.
13910                 if (cselitem.id == v.id) {
13911                     cselitem = false;
13912                 }
13913                 return true;
13914             }
13915                 
13916             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13917                 match = this.store.indexOf(v);
13918                 return false;
13919             }
13920             return true;
13921         }, this);
13922         
13923         if (match === false) {
13924             return true; // no more action?
13925         }
13926         // scroll to?
13927         this.view.select(match);
13928         var sn = Roo.get(this.view.getSelectedNodes()[0]);
13929         sn.scrollIntoView(sn.dom.parentNode, false);
13930     },
13931     
13932     onViewScroll : function(e, t){
13933         
13934         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){
13935             return;
13936         }
13937         
13938         this.hasQuery = true;
13939         
13940         this.loading = this.list.select('.loading', true).first();
13941         
13942         if(this.loading === null){
13943             this.list.createChild({
13944                 tag: 'div',
13945                 cls: 'loading roo-select2-more-results roo-select2-active',
13946                 html: 'Loading more results...'
13947             });
13948             
13949             this.loading = this.list.select('.loading', true).first();
13950             
13951             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13952             
13953             this.loading.hide();
13954         }
13955         
13956         this.loading.show();
13957         
13958         var _combo = this;
13959         
13960         this.page++;
13961         this.loadNext = true;
13962         
13963         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13964         
13965         return;
13966     },
13967     
13968     addItem : function(o)
13969     {   
13970         var dv = ''; // display value
13971         
13972         if (this.displayField) {
13973             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13974         } else {
13975             // this is an error condition!!!
13976             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13977         }
13978         
13979         if(!dv.length){
13980             return;
13981         }
13982         
13983         var choice = this.choices.createChild({
13984             tag: 'li',
13985             cls: 'roo-select2-search-choice',
13986             cn: [
13987                 {
13988                     tag: 'div',
13989                     html: dv
13990                 },
13991                 {
13992                     tag: 'a',
13993                     href: '#',
13994                     cls: 'roo-select2-search-choice-close',
13995                     tabindex: '-1'
13996                 }
13997             ]
13998             
13999         }, this.searchField);
14000         
14001         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14002         
14003         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14004         
14005         this.item.push(o);
14006         
14007         this.lastData = o;
14008         
14009         this.syncValue();
14010         
14011         this.inputEl().dom.value = '';
14012         
14013         this.validate();
14014     },
14015     
14016     onRemoveItem : function(e, _self, o)
14017     {
14018         e.preventDefault();
14019         
14020         this.lastItem = Roo.apply([], this.item);
14021         
14022         var index = this.item.indexOf(o.data) * 1;
14023         
14024         if( index < 0){
14025             Roo.log('not this item?!');
14026             return;
14027         }
14028         
14029         this.item.splice(index, 1);
14030         o.item.remove();
14031         
14032         this.syncValue();
14033         
14034         this.fireEvent('remove', this, e);
14035         
14036         this.validate();
14037         
14038     },
14039     
14040     syncValue : function()
14041     {
14042         if(!this.item.length){
14043             this.clearValue();
14044             return;
14045         }
14046             
14047         var value = [];
14048         var _this = this;
14049         Roo.each(this.item, function(i){
14050             if(_this.valueField){
14051                 value.push(i[_this.valueField]);
14052                 return;
14053             }
14054
14055             value.push(i);
14056         });
14057
14058         this.value = value.join(',');
14059
14060         if(this.hiddenField){
14061             this.hiddenField.dom.value = this.value;
14062         }
14063         
14064         this.store.fireEvent("datachanged", this.store);
14065         
14066         this.validate();
14067     },
14068     
14069     clearItem : function()
14070     {
14071         if(!this.multiple){
14072             return;
14073         }
14074         
14075         this.item = [];
14076         
14077         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14078            c.remove();
14079         });
14080         
14081         this.syncValue();
14082         
14083         this.validate();
14084         
14085         if(this.tickable && !Roo.isTouch){
14086             this.view.refresh();
14087         }
14088     },
14089     
14090     inputEl: function ()
14091     {
14092         if(Roo.isIOS && this.useNativeIOS){
14093             return this.el.select('select.roo-ios-select', true).first();
14094         }
14095         
14096         if(Roo.isTouch && this.mobileTouchView){
14097             return this.el.select('input.form-control',true).first();
14098         }
14099         
14100         if(this.tickable){
14101             return this.searchField;
14102         }
14103         
14104         return this.el.select('input.form-control',true).first();
14105     },
14106     
14107     onTickableFooterButtonClick : function(e, btn, el)
14108     {
14109         e.preventDefault();
14110         
14111         this.lastItem = Roo.apply([], this.item);
14112         
14113         if(btn && btn.name == 'cancel'){
14114             this.tickItems = Roo.apply([], this.item);
14115             this.collapse();
14116             return;
14117         }
14118         
14119         this.clearItem();
14120         
14121         var _this = this;
14122         
14123         Roo.each(this.tickItems, function(o){
14124             _this.addItem(o);
14125         });
14126         
14127         this.collapse();
14128         
14129     },
14130     
14131     validate : function()
14132     {
14133         var v = this.getRawValue();
14134         
14135         if(this.multiple){
14136             v = this.getValue();
14137         }
14138         
14139         if(this.disabled || this.allowBlank || v.length){
14140             this.markValid();
14141             return true;
14142         }
14143         
14144         this.markInvalid();
14145         return false;
14146     },
14147     
14148     tickableInputEl : function()
14149     {
14150         if(!this.tickable || !this.editable){
14151             return this.inputEl();
14152         }
14153         
14154         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14155     },
14156     
14157     
14158     getAutoCreateTouchView : function()
14159     {
14160         var id = Roo.id();
14161         
14162         var cfg = {
14163             cls: 'form-group' //input-group
14164         };
14165         
14166         var input =  {
14167             tag: 'input',
14168             id : id,
14169             type : this.inputType,
14170             cls : 'form-control x-combo-noedit',
14171             autocomplete: 'new-password',
14172             placeholder : this.placeholder || '',
14173             readonly : true
14174         };
14175         
14176         if (this.name) {
14177             input.name = this.name;
14178         }
14179         
14180         if (this.size) {
14181             input.cls += ' input-' + this.size;
14182         }
14183         
14184         if (this.disabled) {
14185             input.disabled = true;
14186         }
14187         
14188         var inputblock = {
14189             cls : '',
14190             cn : [
14191                 input
14192             ]
14193         };
14194         
14195         if(this.before){
14196             inputblock.cls += ' input-group';
14197             
14198             inputblock.cn.unshift({
14199                 tag :'span',
14200                 cls : 'input-group-addon',
14201                 html : this.before
14202             });
14203         }
14204         
14205         if(this.removable && !this.multiple){
14206             inputblock.cls += ' roo-removable';
14207             
14208             inputblock.cn.push({
14209                 tag: 'button',
14210                 html : 'x',
14211                 cls : 'roo-combo-removable-btn close'
14212             });
14213         }
14214
14215         if(this.hasFeedback && !this.allowBlank){
14216             
14217             inputblock.cls += ' has-feedback';
14218             
14219             inputblock.cn.push({
14220                 tag: 'span',
14221                 cls: 'glyphicon form-control-feedback'
14222             });
14223             
14224         }
14225         
14226         if (this.after) {
14227             
14228             inputblock.cls += (this.before) ? '' : ' input-group';
14229             
14230             inputblock.cn.push({
14231                 tag :'span',
14232                 cls : 'input-group-addon',
14233                 html : this.after
14234             });
14235         }
14236
14237         var box = {
14238             tag: 'div',
14239             cn: [
14240                 {
14241                     tag: 'input',
14242                     type : 'hidden',
14243                     cls: 'form-hidden-field'
14244                 },
14245                 inputblock
14246             ]
14247             
14248         };
14249         
14250         if(this.multiple){
14251             box = {
14252                 tag: 'div',
14253                 cn: [
14254                     {
14255                         tag: 'input',
14256                         type : 'hidden',
14257                         cls: 'form-hidden-field'
14258                     },
14259                     {
14260                         tag: 'ul',
14261                         cls: 'roo-select2-choices',
14262                         cn:[
14263                             {
14264                                 tag: 'li',
14265                                 cls: 'roo-select2-search-field',
14266                                 cn: [
14267
14268                                     inputblock
14269                                 ]
14270                             }
14271                         ]
14272                     }
14273                 ]
14274             }
14275         };
14276         
14277         var combobox = {
14278             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14279             cn: [
14280                 box
14281             ]
14282         };
14283         
14284         if(!this.multiple && this.showToggleBtn){
14285             
14286             var caret = {
14287                         tag: 'span',
14288                         cls: 'caret'
14289             };
14290             
14291             if (this.caret != false) {
14292                 caret = {
14293                      tag: 'i',
14294                      cls: 'fa fa-' + this.caret
14295                 };
14296                 
14297             }
14298             
14299             combobox.cn.push({
14300                 tag :'span',
14301                 cls : 'input-group-addon btn dropdown-toggle',
14302                 cn : [
14303                     caret,
14304                     {
14305                         tag: 'span',
14306                         cls: 'combobox-clear',
14307                         cn  : [
14308                             {
14309                                 tag : 'i',
14310                                 cls: 'icon-remove'
14311                             }
14312                         ]
14313                     }
14314                 ]
14315
14316             })
14317         }
14318         
14319         if(this.multiple){
14320             combobox.cls += ' roo-select2-container-multi';
14321         }
14322         
14323         var align = this.labelAlign || this.parentLabelAlign();
14324         
14325         cfg.cn = combobox;
14326         
14327         if(this.fieldLabel.length && this.labelWidth){
14328             
14329             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
14330             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
14331             
14332             cfg.cn = [
14333                 {
14334                    tag : 'i',
14335                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14336                    tooltip : 'This field is required'
14337                 },
14338                 {
14339                     tag: 'label',
14340                     cls : 'control-label ' + lw,
14341                     html : this.fieldLabel
14342
14343                 },
14344                 {
14345                     cls : cw, 
14346                     cn: [
14347                         combobox
14348                     ]
14349                 }
14350             ];
14351             
14352             if(this.indicatorpos == 'right'){
14353                 cfg.cn = [
14354                     {
14355                         tag: 'label',
14356                         cls : 'control-label ' + lw,
14357                         html : this.fieldLabel
14358
14359                     },
14360                     {
14361                        tag : 'i',
14362                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14363                        tooltip : 'This field is required'
14364                     },
14365                     {
14366                         cls : cw, 
14367                         cn: [
14368                             combobox
14369                         ]
14370                     }
14371                 ];
14372             }
14373         }
14374         
14375         var settings = this;
14376         
14377         ['xs','sm','md','lg'].map(function(size){
14378             if (settings[size]) {
14379                 cfg.cls += ' col-' + size + '-' + settings[size];
14380             }
14381         });
14382         
14383         return cfg;
14384     },
14385     
14386     initTouchView : function()
14387     {
14388         this.renderTouchView();
14389         
14390         this.touchViewEl.on('scroll', function(){
14391             this.el.dom.scrollTop = 0;
14392         }, this);
14393         
14394         this.originalValue = this.getValue();
14395         
14396         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14397         
14398         this.inputEl().on("click", this.showTouchView, this);
14399         if (this.triggerEl) {
14400             this.triggerEl.on("click", this.showTouchView, this);
14401         }
14402         
14403         
14404         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14405         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14406         
14407         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14408         
14409         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14410         this.store.on('load', this.onTouchViewLoad, this);
14411         this.store.on('loadexception', this.onTouchViewLoadException, this);
14412         
14413         if(this.hiddenName){
14414             
14415             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14416             
14417             this.hiddenField.dom.value =
14418                 this.hiddenValue !== undefined ? this.hiddenValue :
14419                 this.value !== undefined ? this.value : '';
14420         
14421             this.el.dom.removeAttribute('name');
14422             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14423         }
14424         
14425         if(this.multiple){
14426             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14427             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14428         }
14429         
14430         if(this.removable && !this.multiple){
14431             var close = this.closeTriggerEl();
14432             if(close){
14433                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14434                 close.on('click', this.removeBtnClick, this, close);
14435             }
14436         }
14437         /*
14438          * fix the bug in Safari iOS8
14439          */
14440         this.inputEl().on("focus", function(e){
14441             document.activeElement.blur();
14442         }, this);
14443         
14444         return;
14445         
14446         
14447     },
14448     
14449     renderTouchView : function()
14450     {
14451         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14452         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14453         
14454         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14455         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14456         
14457         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14458         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14459         this.touchViewBodyEl.setStyle('overflow', 'auto');
14460         
14461         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14462         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14463         
14464         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14465         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14466         
14467     },
14468     
14469     showTouchView : function()
14470     {
14471         if(this.disabled){
14472             return;
14473         }
14474         
14475         this.touchViewHeaderEl.hide();
14476
14477         if(this.modalTitle.length){
14478             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14479             this.touchViewHeaderEl.show();
14480         }
14481
14482         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
14483         this.touchViewEl.show();
14484
14485         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14486         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14487                 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14488
14489         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14490
14491         if(this.modalTitle.length){
14492             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14493         }
14494         
14495         this.touchViewBodyEl.setHeight(bodyHeight);
14496
14497         if(this.animate){
14498             var _this = this;
14499             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14500         }else{
14501             this.touchViewEl.addClass('in');
14502         }
14503
14504         this.doTouchViewQuery();
14505         
14506     },
14507     
14508     hideTouchView : function()
14509     {
14510         this.touchViewEl.removeClass('in');
14511
14512         if(this.animate){
14513             var _this = this;
14514             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14515         }else{
14516             this.touchViewEl.setStyle('display', 'none');
14517         }
14518         
14519     },
14520     
14521     setTouchViewValue : function()
14522     {
14523         if(this.multiple){
14524             this.clearItem();
14525         
14526             var _this = this;
14527
14528             Roo.each(this.tickItems, function(o){
14529                 this.addItem(o);
14530             }, this);
14531         }
14532         
14533         this.hideTouchView();
14534     },
14535     
14536     doTouchViewQuery : function()
14537     {
14538         var qe = {
14539             query: '',
14540             forceAll: true,
14541             combo: this,
14542             cancel:false
14543         };
14544         
14545         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14546             return false;
14547         }
14548         
14549         if(!this.alwaysQuery || this.mode == 'local'){
14550             this.onTouchViewLoad();
14551             return;
14552         }
14553         
14554         this.store.load();
14555     },
14556     
14557     onTouchViewBeforeLoad : function(combo,opts)
14558     {
14559         return;
14560     },
14561
14562     // private
14563     onTouchViewLoad : function()
14564     {
14565         if(this.store.getCount() < 1){
14566             this.onTouchViewEmptyResults();
14567             return;
14568         }
14569         
14570         this.clearTouchView();
14571         
14572         var rawValue = this.getRawValue();
14573         
14574         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14575         
14576         this.tickItems = [];
14577         
14578         this.store.data.each(function(d, rowIndex){
14579             var row = this.touchViewListGroup.createChild(template);
14580             
14581             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14582                 row.addClass(d.data.cls);
14583             }
14584             
14585             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14586                 var cfg = {
14587                     data : d.data,
14588                     html : d.data[this.displayField]
14589                 };
14590                 
14591                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14592                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14593                 }
14594             }
14595             row.removeClass('selected');
14596             if(!this.multiple && this.valueField &&
14597                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14598             {
14599                 // radio buttons..
14600                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14601                 row.addClass('selected');
14602             }
14603             
14604             if(this.multiple && this.valueField &&
14605                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14606             {
14607                 
14608                 // checkboxes...
14609                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14610                 this.tickItems.push(d.data);
14611             }
14612             
14613             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14614             
14615         }, this);
14616         
14617         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14618         
14619         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14620
14621         if(this.modalTitle.length){
14622             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14623         }
14624
14625         var listHeight = this.touchViewListGroup.getHeight();
14626         
14627         var _this = this;
14628         
14629         if(firstChecked && listHeight > bodyHeight){
14630             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14631         }
14632         
14633     },
14634     
14635     onTouchViewLoadException : function()
14636     {
14637         this.hideTouchView();
14638     },
14639     
14640     onTouchViewEmptyResults : function()
14641     {
14642         this.clearTouchView();
14643         
14644         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14645         
14646         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14647         
14648     },
14649     
14650     clearTouchView : function()
14651     {
14652         this.touchViewListGroup.dom.innerHTML = '';
14653     },
14654     
14655     onTouchViewClick : function(e, el, o)
14656     {
14657         e.preventDefault();
14658         
14659         var row = o.row;
14660         var rowIndex = o.rowIndex;
14661         
14662         var r = this.store.getAt(rowIndex);
14663         
14664         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14665             
14666             if(!this.multiple){
14667                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14668                     c.dom.removeAttribute('checked');
14669                 }, this);
14670
14671                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14672
14673                 this.setFromData(r.data);
14674
14675                 var close = this.closeTriggerEl();
14676
14677                 if(close){
14678                     close.show();
14679                 }
14680
14681                 this.hideTouchView();
14682
14683                 this.fireEvent('select', this, r, rowIndex);
14684
14685                 return;
14686             }
14687
14688             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14689                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14690                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14691                 return;
14692             }
14693
14694             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14695             this.addItem(r.data);
14696             this.tickItems.push(r.data);
14697         }
14698     },
14699     
14700     getAutoCreateNativeIOS : function()
14701     {
14702         var cfg = {
14703             cls: 'form-group' //input-group,
14704         };
14705         
14706         var combobox =  {
14707             tag: 'select',
14708             cls : 'roo-ios-select'
14709         };
14710         
14711         if (this.name) {
14712             combobox.name = this.name;
14713         }
14714         
14715         if (this.disabled) {
14716             combobox.disabled = true;
14717         }
14718         
14719         var settings = this;
14720         
14721         ['xs','sm','md','lg'].map(function(size){
14722             if (settings[size]) {
14723                 cfg.cls += ' col-' + size + '-' + settings[size];
14724             }
14725         });
14726         
14727         cfg.cn = combobox;
14728         
14729         return cfg;
14730         
14731     },
14732     
14733     initIOSView : function()
14734     {
14735         this.store.on('load', this.onIOSViewLoad, this);
14736         
14737         return;
14738     },
14739     
14740     onIOSViewLoad : function()
14741     {
14742         if(this.store.getCount() < 1){
14743             return;
14744         }
14745         
14746         this.clearIOSView();
14747         
14748         if(this.allowBlank) {
14749             
14750             var default_text = '-- SELECT --';
14751             
14752             var opt = this.inputEl().createChild({
14753                 tag: 'option',
14754                 value : 0,
14755                 html : default_text
14756             });
14757             
14758             var o = {};
14759             o[this.valueField] = 0;
14760             o[this.displayField] = default_text;
14761             
14762             this.ios_options.push({
14763                 data : o,
14764                 el : opt
14765             });
14766             
14767         }
14768         
14769         this.store.data.each(function(d, rowIndex){
14770             
14771             var html = '';
14772             
14773             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14774                 html = d.data[this.displayField];
14775             }
14776             
14777             var value = '';
14778             
14779             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
14780                 value = d.data[this.valueField];
14781             }
14782             
14783             var option = {
14784                 tag: 'option',
14785                 value : value,
14786                 html : html
14787             };
14788             
14789             if(this.value == d.data[this.valueField]){
14790                 option['selected'] = true;
14791             }
14792             
14793             var opt = this.inputEl().createChild(option);
14794             
14795             this.ios_options.push({
14796                 data : d.data,
14797                 el : opt
14798             });
14799             
14800         }, this);
14801         
14802         this.inputEl().on('change', function(){
14803            this.fireEvent('select', this);
14804         }, this);
14805         
14806     },
14807     
14808     clearIOSView: function()
14809     {
14810         this.inputEl().dom.innerHTML = '';
14811         
14812         this.ios_options = [];
14813     },
14814     
14815     setIOSValue: function(v)
14816     {
14817         this.value = v;
14818         
14819         if(!this.ios_options){
14820             return;
14821         }
14822         
14823         Roo.each(this.ios_options, function(opts){
14824            
14825            opts.el.dom.removeAttribute('selected');
14826            
14827            if(opts.data[this.valueField] != v){
14828                return;
14829            }
14830            
14831            opts.el.dom.setAttribute('selected', true);
14832            
14833         }, this);
14834     }
14835
14836     /** 
14837     * @cfg {Boolean} grow 
14838     * @hide 
14839     */
14840     /** 
14841     * @cfg {Number} growMin 
14842     * @hide 
14843     */
14844     /** 
14845     * @cfg {Number} growMax 
14846     * @hide 
14847     */
14848     /**
14849      * @hide
14850      * @method autoSize
14851      */
14852 });
14853
14854 Roo.apply(Roo.bootstrap.ComboBox,  {
14855     
14856     header : {
14857         tag: 'div',
14858         cls: 'modal-header',
14859         cn: [
14860             {
14861                 tag: 'h4',
14862                 cls: 'modal-title'
14863             }
14864         ]
14865     },
14866     
14867     body : {
14868         tag: 'div',
14869         cls: 'modal-body',
14870         cn: [
14871             {
14872                 tag: 'ul',
14873                 cls: 'list-group'
14874             }
14875         ]
14876     },
14877     
14878     listItemRadio : {
14879         tag: 'li',
14880         cls: 'list-group-item',
14881         cn: [
14882             {
14883                 tag: 'span',
14884                 cls: 'roo-combobox-list-group-item-value'
14885             },
14886             {
14887                 tag: 'div',
14888                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14889                 cn: [
14890                     {
14891                         tag: 'input',
14892                         type: 'radio'
14893                     },
14894                     {
14895                         tag: 'label'
14896                     }
14897                 ]
14898             }
14899         ]
14900     },
14901     
14902     listItemCheckbox : {
14903         tag: 'li',
14904         cls: 'list-group-item',
14905         cn: [
14906             {
14907                 tag: 'span',
14908                 cls: 'roo-combobox-list-group-item-value'
14909             },
14910             {
14911                 tag: 'div',
14912                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14913                 cn: [
14914                     {
14915                         tag: 'input',
14916                         type: 'checkbox'
14917                     },
14918                     {
14919                         tag: 'label'
14920                     }
14921                 ]
14922             }
14923         ]
14924     },
14925     
14926     emptyResult : {
14927         tag: 'div',
14928         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14929     },
14930     
14931     footer : {
14932         tag: 'div',
14933         cls: 'modal-footer',
14934         cn: [
14935             {
14936                 tag: 'div',
14937                 cls: 'row',
14938                 cn: [
14939                     {
14940                         tag: 'div',
14941                         cls: 'col-xs-6 text-left',
14942                         cn: {
14943                             tag: 'button',
14944                             cls: 'btn btn-danger roo-touch-view-cancel',
14945                             html: 'Cancel'
14946                         }
14947                     },
14948                     {
14949                         tag: 'div',
14950                         cls: 'col-xs-6 text-right',
14951                         cn: {
14952                             tag: 'button',
14953                             cls: 'btn btn-success roo-touch-view-ok',
14954                             html: 'OK'
14955                         }
14956                     }
14957                 ]
14958             }
14959         ]
14960         
14961     }
14962 });
14963
14964 Roo.apply(Roo.bootstrap.ComboBox,  {
14965     
14966     touchViewTemplate : {
14967         tag: 'div',
14968         cls: 'modal fade roo-combobox-touch-view',
14969         cn: [
14970             {
14971                 tag: 'div',
14972                 cls: 'modal-dialog',
14973                 style : 'position:fixed', // we have to fix position....
14974                 cn: [
14975                     {
14976                         tag: 'div',
14977                         cls: 'modal-content',
14978                         cn: [
14979                             Roo.bootstrap.ComboBox.header,
14980                             Roo.bootstrap.ComboBox.body,
14981                             Roo.bootstrap.ComboBox.footer
14982                         ]
14983                     }
14984                 ]
14985             }
14986         ]
14987     }
14988 });/*
14989  * Based on:
14990  * Ext JS Library 1.1.1
14991  * Copyright(c) 2006-2007, Ext JS, LLC.
14992  *
14993  * Originally Released Under LGPL - original licence link has changed is not relivant.
14994  *
14995  * Fork - LGPL
14996  * <script type="text/javascript">
14997  */
14998
14999 /**
15000  * @class Roo.View
15001  * @extends Roo.util.Observable
15002  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15003  * This class also supports single and multi selection modes. <br>
15004  * Create a data model bound view:
15005  <pre><code>
15006  var store = new Roo.data.Store(...);
15007
15008  var view = new Roo.View({
15009     el : "my-element",
15010     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15011  
15012     singleSelect: true,
15013     selectedClass: "ydataview-selected",
15014     store: store
15015  });
15016
15017  // listen for node click?
15018  view.on("click", function(vw, index, node, e){
15019  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15020  });
15021
15022  // load XML data
15023  dataModel.load("foobar.xml");
15024  </code></pre>
15025  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15026  * <br><br>
15027  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15028  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15029  * 
15030  * Note: old style constructor is still suported (container, template, config)
15031  * 
15032  * @constructor
15033  * Create a new View
15034  * @param {Object} config The config object
15035  * 
15036  */
15037 Roo.View = function(config, depreciated_tpl, depreciated_config){
15038     
15039     this.parent = false;
15040     
15041     if (typeof(depreciated_tpl) == 'undefined') {
15042         // new way.. - universal constructor.
15043         Roo.apply(this, config);
15044         this.el  = Roo.get(this.el);
15045     } else {
15046         // old format..
15047         this.el  = Roo.get(config);
15048         this.tpl = depreciated_tpl;
15049         Roo.apply(this, depreciated_config);
15050     }
15051     this.wrapEl  = this.el.wrap().wrap();
15052     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15053     
15054     
15055     if(typeof(this.tpl) == "string"){
15056         this.tpl = new Roo.Template(this.tpl);
15057     } else {
15058         // support xtype ctors..
15059         this.tpl = new Roo.factory(this.tpl, Roo);
15060     }
15061     
15062     
15063     this.tpl.compile();
15064     
15065     /** @private */
15066     this.addEvents({
15067         /**
15068          * @event beforeclick
15069          * Fires before a click is processed. Returns false to cancel the default action.
15070          * @param {Roo.View} this
15071          * @param {Number} index The index of the target node
15072          * @param {HTMLElement} node The target node
15073          * @param {Roo.EventObject} e The raw event object
15074          */
15075             "beforeclick" : true,
15076         /**
15077          * @event click
15078          * Fires when a template node is clicked.
15079          * @param {Roo.View} this
15080          * @param {Number} index The index of the target node
15081          * @param {HTMLElement} node The target node
15082          * @param {Roo.EventObject} e The raw event object
15083          */
15084             "click" : true,
15085         /**
15086          * @event dblclick
15087          * Fires when a template node is double clicked.
15088          * @param {Roo.View} this
15089          * @param {Number} index The index of the target node
15090          * @param {HTMLElement} node The target node
15091          * @param {Roo.EventObject} e The raw event object
15092          */
15093             "dblclick" : true,
15094         /**
15095          * @event contextmenu
15096          * Fires when a template node is right clicked.
15097          * @param {Roo.View} this
15098          * @param {Number} index The index of the target node
15099          * @param {HTMLElement} node The target node
15100          * @param {Roo.EventObject} e The raw event object
15101          */
15102             "contextmenu" : true,
15103         /**
15104          * @event selectionchange
15105          * Fires when the selected nodes change.
15106          * @param {Roo.View} this
15107          * @param {Array} selections Array of the selected nodes
15108          */
15109             "selectionchange" : true,
15110     
15111         /**
15112          * @event beforeselect
15113          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15114          * @param {Roo.View} this
15115          * @param {HTMLElement} node The node to be selected
15116          * @param {Array} selections Array of currently selected nodes
15117          */
15118             "beforeselect" : true,
15119         /**
15120          * @event preparedata
15121          * Fires on every row to render, to allow you to change the data.
15122          * @param {Roo.View} this
15123          * @param {Object} data to be rendered (change this)
15124          */
15125           "preparedata" : true
15126           
15127           
15128         });
15129
15130
15131
15132     this.el.on({
15133         "click": this.onClick,
15134         "dblclick": this.onDblClick,
15135         "contextmenu": this.onContextMenu,
15136         scope:this
15137     });
15138
15139     this.selections = [];
15140     this.nodes = [];
15141     this.cmp = new Roo.CompositeElementLite([]);
15142     if(this.store){
15143         this.store = Roo.factory(this.store, Roo.data);
15144         this.setStore(this.store, true);
15145     }
15146     
15147     if ( this.footer && this.footer.xtype) {
15148            
15149          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15150         
15151         this.footer.dataSource = this.store;
15152         this.footer.container = fctr;
15153         this.footer = Roo.factory(this.footer, Roo);
15154         fctr.insertFirst(this.el);
15155         
15156         // this is a bit insane - as the paging toolbar seems to detach the el..
15157 //        dom.parentNode.parentNode.parentNode
15158          // they get detached?
15159     }
15160     
15161     
15162     Roo.View.superclass.constructor.call(this);
15163     
15164     
15165 };
15166
15167 Roo.extend(Roo.View, Roo.util.Observable, {
15168     
15169      /**
15170      * @cfg {Roo.data.Store} store Data store to load data from.
15171      */
15172     store : false,
15173     
15174     /**
15175      * @cfg {String|Roo.Element} el The container element.
15176      */
15177     el : '',
15178     
15179     /**
15180      * @cfg {String|Roo.Template} tpl The template used by this View 
15181      */
15182     tpl : false,
15183     /**
15184      * @cfg {String} dataName the named area of the template to use as the data area
15185      *                          Works with domtemplates roo-name="name"
15186      */
15187     dataName: false,
15188     /**
15189      * @cfg {String} selectedClass The css class to add to selected nodes
15190      */
15191     selectedClass : "x-view-selected",
15192      /**
15193      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15194      */
15195     emptyText : "",
15196     
15197     /**
15198      * @cfg {String} text to display on mask (default Loading)
15199      */
15200     mask : false,
15201     /**
15202      * @cfg {Boolean} multiSelect Allow multiple selection
15203      */
15204     multiSelect : false,
15205     /**
15206      * @cfg {Boolean} singleSelect Allow single selection
15207      */
15208     singleSelect:  false,
15209     
15210     /**
15211      * @cfg {Boolean} toggleSelect - selecting 
15212      */
15213     toggleSelect : false,
15214     
15215     /**
15216      * @cfg {Boolean} tickable - selecting 
15217      */
15218     tickable : false,
15219     
15220     /**
15221      * Returns the element this view is bound to.
15222      * @return {Roo.Element}
15223      */
15224     getEl : function(){
15225         return this.wrapEl;
15226     },
15227     
15228     
15229
15230     /**
15231      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15232      */
15233     refresh : function(){
15234         //Roo.log('refresh');
15235         var t = this.tpl;
15236         
15237         // if we are using something like 'domtemplate', then
15238         // the what gets used is:
15239         // t.applySubtemplate(NAME, data, wrapping data..)
15240         // the outer template then get' applied with
15241         //     the store 'extra data'
15242         // and the body get's added to the
15243         //      roo-name="data" node?
15244         //      <span class='roo-tpl-{name}'></span> ?????
15245         
15246         
15247         
15248         this.clearSelections();
15249         this.el.update("");
15250         var html = [];
15251         var records = this.store.getRange();
15252         if(records.length < 1) {
15253             
15254             // is this valid??  = should it render a template??
15255             
15256             this.el.update(this.emptyText);
15257             return;
15258         }
15259         var el = this.el;
15260         if (this.dataName) {
15261             this.el.update(t.apply(this.store.meta)); //????
15262             el = this.el.child('.roo-tpl-' + this.dataName);
15263         }
15264         
15265         for(var i = 0, len = records.length; i < len; i++){
15266             var data = this.prepareData(records[i].data, i, records[i]);
15267             this.fireEvent("preparedata", this, data, i, records[i]);
15268             
15269             var d = Roo.apply({}, data);
15270             
15271             if(this.tickable){
15272                 Roo.apply(d, {'roo-id' : Roo.id()});
15273                 
15274                 var _this = this;
15275             
15276                 Roo.each(this.parent.item, function(item){
15277                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15278                         return;
15279                     }
15280                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15281                 });
15282             }
15283             
15284             html[html.length] = Roo.util.Format.trim(
15285                 this.dataName ?
15286                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15287                     t.apply(d)
15288             );
15289         }
15290         
15291         
15292         
15293         el.update(html.join(""));
15294         this.nodes = el.dom.childNodes;
15295         this.updateIndexes(0);
15296     },
15297     
15298
15299     /**
15300      * Function to override to reformat the data that is sent to
15301      * the template for each node.
15302      * DEPRICATED - use the preparedata event handler.
15303      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15304      * a JSON object for an UpdateManager bound view).
15305      */
15306     prepareData : function(data, index, record)
15307     {
15308         this.fireEvent("preparedata", this, data, index, record);
15309         return data;
15310     },
15311
15312     onUpdate : function(ds, record){
15313         // Roo.log('on update');   
15314         this.clearSelections();
15315         var index = this.store.indexOf(record);
15316         var n = this.nodes[index];
15317         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15318         n.parentNode.removeChild(n);
15319         this.updateIndexes(index, index);
15320     },
15321
15322     
15323     
15324 // --------- FIXME     
15325     onAdd : function(ds, records, index)
15326     {
15327         //Roo.log(['on Add', ds, records, index] );        
15328         this.clearSelections();
15329         if(this.nodes.length == 0){
15330             this.refresh();
15331             return;
15332         }
15333         var n = this.nodes[index];
15334         for(var i = 0, len = records.length; i < len; i++){
15335             var d = this.prepareData(records[i].data, i, records[i]);
15336             if(n){
15337                 this.tpl.insertBefore(n, d);
15338             }else{
15339                 
15340                 this.tpl.append(this.el, d);
15341             }
15342         }
15343         this.updateIndexes(index);
15344     },
15345
15346     onRemove : function(ds, record, index){
15347        // Roo.log('onRemove');
15348         this.clearSelections();
15349         var el = this.dataName  ?
15350             this.el.child('.roo-tpl-' + this.dataName) :
15351             this.el; 
15352         
15353         el.dom.removeChild(this.nodes[index]);
15354         this.updateIndexes(index);
15355     },
15356
15357     /**
15358      * Refresh an individual node.
15359      * @param {Number} index
15360      */
15361     refreshNode : function(index){
15362         this.onUpdate(this.store, this.store.getAt(index));
15363     },
15364
15365     updateIndexes : function(startIndex, endIndex){
15366         var ns = this.nodes;
15367         startIndex = startIndex || 0;
15368         endIndex = endIndex || ns.length - 1;
15369         for(var i = startIndex; i <= endIndex; i++){
15370             ns[i].nodeIndex = i;
15371         }
15372     },
15373
15374     /**
15375      * Changes the data store this view uses and refresh the view.
15376      * @param {Store} store
15377      */
15378     setStore : function(store, initial){
15379         if(!initial && this.store){
15380             this.store.un("datachanged", this.refresh);
15381             this.store.un("add", this.onAdd);
15382             this.store.un("remove", this.onRemove);
15383             this.store.un("update", this.onUpdate);
15384             this.store.un("clear", this.refresh);
15385             this.store.un("beforeload", this.onBeforeLoad);
15386             this.store.un("load", this.onLoad);
15387             this.store.un("loadexception", this.onLoad);
15388         }
15389         if(store){
15390           
15391             store.on("datachanged", this.refresh, this);
15392             store.on("add", this.onAdd, this);
15393             store.on("remove", this.onRemove, this);
15394             store.on("update", this.onUpdate, this);
15395             store.on("clear", this.refresh, this);
15396             store.on("beforeload", this.onBeforeLoad, this);
15397             store.on("load", this.onLoad, this);
15398             store.on("loadexception", this.onLoad, this);
15399         }
15400         
15401         if(store){
15402             this.refresh();
15403         }
15404     },
15405     /**
15406      * onbeforeLoad - masks the loading area.
15407      *
15408      */
15409     onBeforeLoad : function(store,opts)
15410     {
15411          //Roo.log('onBeforeLoad');   
15412         if (!opts.add) {
15413             this.el.update("");
15414         }
15415         this.el.mask(this.mask ? this.mask : "Loading" ); 
15416     },
15417     onLoad : function ()
15418     {
15419         this.el.unmask();
15420     },
15421     
15422
15423     /**
15424      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15425      * @param {HTMLElement} node
15426      * @return {HTMLElement} The template node
15427      */
15428     findItemFromChild : function(node){
15429         var el = this.dataName  ?
15430             this.el.child('.roo-tpl-' + this.dataName,true) :
15431             this.el.dom; 
15432         
15433         if(!node || node.parentNode == el){
15434                     return node;
15435             }
15436             var p = node.parentNode;
15437             while(p && p != el){
15438             if(p.parentNode == el){
15439                 return p;
15440             }
15441             p = p.parentNode;
15442         }
15443             return null;
15444     },
15445
15446     /** @ignore */
15447     onClick : function(e){
15448         var item = this.findItemFromChild(e.getTarget());
15449         if(item){
15450             var index = this.indexOf(item);
15451             if(this.onItemClick(item, index, e) !== false){
15452                 this.fireEvent("click", this, index, item, e);
15453             }
15454         }else{
15455             this.clearSelections();
15456         }
15457     },
15458
15459     /** @ignore */
15460     onContextMenu : function(e){
15461         var item = this.findItemFromChild(e.getTarget());
15462         if(item){
15463             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15464         }
15465     },
15466
15467     /** @ignore */
15468     onDblClick : function(e){
15469         var item = this.findItemFromChild(e.getTarget());
15470         if(item){
15471             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15472         }
15473     },
15474
15475     onItemClick : function(item, index, e)
15476     {
15477         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15478             return false;
15479         }
15480         if (this.toggleSelect) {
15481             var m = this.isSelected(item) ? 'unselect' : 'select';
15482             //Roo.log(m);
15483             var _t = this;
15484             _t[m](item, true, false);
15485             return true;
15486         }
15487         if(this.multiSelect || this.singleSelect){
15488             if(this.multiSelect && e.shiftKey && this.lastSelection){
15489                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15490             }else{
15491                 this.select(item, this.multiSelect && e.ctrlKey);
15492                 this.lastSelection = item;
15493             }
15494             
15495             if(!this.tickable){
15496                 e.preventDefault();
15497             }
15498             
15499         }
15500         return true;
15501     },
15502
15503     /**
15504      * Get the number of selected nodes.
15505      * @return {Number}
15506      */
15507     getSelectionCount : function(){
15508         return this.selections.length;
15509     },
15510
15511     /**
15512      * Get the currently selected nodes.
15513      * @return {Array} An array of HTMLElements
15514      */
15515     getSelectedNodes : function(){
15516         return this.selections;
15517     },
15518
15519     /**
15520      * Get the indexes of the selected nodes.
15521      * @return {Array}
15522      */
15523     getSelectedIndexes : function(){
15524         var indexes = [], s = this.selections;
15525         for(var i = 0, len = s.length; i < len; i++){
15526             indexes.push(s[i].nodeIndex);
15527         }
15528         return indexes;
15529     },
15530
15531     /**
15532      * Clear all selections
15533      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15534      */
15535     clearSelections : function(suppressEvent){
15536         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15537             this.cmp.elements = this.selections;
15538             this.cmp.removeClass(this.selectedClass);
15539             this.selections = [];
15540             if(!suppressEvent){
15541                 this.fireEvent("selectionchange", this, this.selections);
15542             }
15543         }
15544     },
15545
15546     /**
15547      * Returns true if the passed node is selected
15548      * @param {HTMLElement/Number} node The node or node index
15549      * @return {Boolean}
15550      */
15551     isSelected : function(node){
15552         var s = this.selections;
15553         if(s.length < 1){
15554             return false;
15555         }
15556         node = this.getNode(node);
15557         return s.indexOf(node) !== -1;
15558     },
15559
15560     /**
15561      * Selects nodes.
15562      * @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
15563      * @param {Boolean} keepExisting (optional) true to keep existing selections
15564      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15565      */
15566     select : function(nodeInfo, keepExisting, suppressEvent){
15567         if(nodeInfo instanceof Array){
15568             if(!keepExisting){
15569                 this.clearSelections(true);
15570             }
15571             for(var i = 0, len = nodeInfo.length; i < len; i++){
15572                 this.select(nodeInfo[i], true, true);
15573             }
15574             return;
15575         } 
15576         var node = this.getNode(nodeInfo);
15577         if(!node || this.isSelected(node)){
15578             return; // already selected.
15579         }
15580         if(!keepExisting){
15581             this.clearSelections(true);
15582         }
15583         
15584         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15585             Roo.fly(node).addClass(this.selectedClass);
15586             this.selections.push(node);
15587             if(!suppressEvent){
15588                 this.fireEvent("selectionchange", this, this.selections);
15589             }
15590         }
15591         
15592         
15593     },
15594       /**
15595      * Unselects nodes.
15596      * @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
15597      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15598      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15599      */
15600     unselect : function(nodeInfo, keepExisting, suppressEvent)
15601     {
15602         if(nodeInfo instanceof Array){
15603             Roo.each(this.selections, function(s) {
15604                 this.unselect(s, nodeInfo);
15605             }, this);
15606             return;
15607         }
15608         var node = this.getNode(nodeInfo);
15609         if(!node || !this.isSelected(node)){
15610             //Roo.log("not selected");
15611             return; // not selected.
15612         }
15613         // fireevent???
15614         var ns = [];
15615         Roo.each(this.selections, function(s) {
15616             if (s == node ) {
15617                 Roo.fly(node).removeClass(this.selectedClass);
15618
15619                 return;
15620             }
15621             ns.push(s);
15622         },this);
15623         
15624         this.selections= ns;
15625         this.fireEvent("selectionchange", this, this.selections);
15626     },
15627
15628     /**
15629      * Gets a template node.
15630      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15631      * @return {HTMLElement} The node or null if it wasn't found
15632      */
15633     getNode : function(nodeInfo){
15634         if(typeof nodeInfo == "string"){
15635             return document.getElementById(nodeInfo);
15636         }else if(typeof nodeInfo == "number"){
15637             return this.nodes[nodeInfo];
15638         }
15639         return nodeInfo;
15640     },
15641
15642     /**
15643      * Gets a range template nodes.
15644      * @param {Number} startIndex
15645      * @param {Number} endIndex
15646      * @return {Array} An array of nodes
15647      */
15648     getNodes : function(start, end){
15649         var ns = this.nodes;
15650         start = start || 0;
15651         end = typeof end == "undefined" ? ns.length - 1 : end;
15652         var nodes = [];
15653         if(start <= end){
15654             for(var i = start; i <= end; i++){
15655                 nodes.push(ns[i]);
15656             }
15657         } else{
15658             for(var i = start; i >= end; i--){
15659                 nodes.push(ns[i]);
15660             }
15661         }
15662         return nodes;
15663     },
15664
15665     /**
15666      * Finds the index of the passed node
15667      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15668      * @return {Number} The index of the node or -1
15669      */
15670     indexOf : function(node){
15671         node = this.getNode(node);
15672         if(typeof node.nodeIndex == "number"){
15673             return node.nodeIndex;
15674         }
15675         var ns = this.nodes;
15676         for(var i = 0, len = ns.length; i < len; i++){
15677             if(ns[i] == node){
15678                 return i;
15679             }
15680         }
15681         return -1;
15682     }
15683 });
15684 /*
15685  * - LGPL
15686  *
15687  * based on jquery fullcalendar
15688  * 
15689  */
15690
15691 Roo.bootstrap = Roo.bootstrap || {};
15692 /**
15693  * @class Roo.bootstrap.Calendar
15694  * @extends Roo.bootstrap.Component
15695  * Bootstrap Calendar class
15696  * @cfg {Boolean} loadMask (true|false) default false
15697  * @cfg {Object} header generate the user specific header of the calendar, default false
15698
15699  * @constructor
15700  * Create a new Container
15701  * @param {Object} config The config object
15702  */
15703
15704
15705
15706 Roo.bootstrap.Calendar = function(config){
15707     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15708      this.addEvents({
15709         /**
15710              * @event select
15711              * Fires when a date is selected
15712              * @param {DatePicker} this
15713              * @param {Date} date The selected date
15714              */
15715         'select': true,
15716         /**
15717              * @event monthchange
15718              * Fires when the displayed month changes 
15719              * @param {DatePicker} this
15720              * @param {Date} date The selected month
15721              */
15722         'monthchange': true,
15723         /**
15724              * @event evententer
15725              * Fires when mouse over an event
15726              * @param {Calendar} this
15727              * @param {event} Event
15728              */
15729         'evententer': true,
15730         /**
15731              * @event eventleave
15732              * Fires when the mouse leaves an
15733              * @param {Calendar} this
15734              * @param {event}
15735              */
15736         'eventleave': true,
15737         /**
15738              * @event eventclick
15739              * Fires when the mouse click an
15740              * @param {Calendar} this
15741              * @param {event}
15742              */
15743         'eventclick': true
15744         
15745     });
15746
15747 };
15748
15749 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
15750     
15751      /**
15752      * @cfg {Number} startDay
15753      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15754      */
15755     startDay : 0,
15756     
15757     loadMask : false,
15758     
15759     header : false,
15760       
15761     getAutoCreate : function(){
15762         
15763         
15764         var fc_button = function(name, corner, style, content ) {
15765             return Roo.apply({},{
15766                 tag : 'span',
15767                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
15768                          (corner.length ?
15769                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15770                             ''
15771                         ),
15772                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15773                 unselectable: 'on'
15774             });
15775         };
15776         
15777         var header = {};
15778         
15779         if(!this.header){
15780             header = {
15781                 tag : 'table',
15782                 cls : 'fc-header',
15783                 style : 'width:100%',
15784                 cn : [
15785                     {
15786                         tag: 'tr',
15787                         cn : [
15788                             {
15789                                 tag : 'td',
15790                                 cls : 'fc-header-left',
15791                                 cn : [
15792                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
15793                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
15794                                     { tag: 'span', cls: 'fc-header-space' },
15795                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
15796
15797
15798                                 ]
15799                             },
15800
15801                             {
15802                                 tag : 'td',
15803                                 cls : 'fc-header-center',
15804                                 cn : [
15805                                     {
15806                                         tag: 'span',
15807                                         cls: 'fc-header-title',
15808                                         cn : {
15809                                             tag: 'H2',
15810                                             html : 'month / year'
15811                                         }
15812                                     }
15813
15814                                 ]
15815                             },
15816                             {
15817                                 tag : 'td',
15818                                 cls : 'fc-header-right',
15819                                 cn : [
15820                               /*      fc_button('month', 'left', '', 'month' ),
15821                                     fc_button('week', '', '', 'week' ),
15822                                     fc_button('day', 'right', '', 'day' )
15823                                 */    
15824
15825                                 ]
15826                             }
15827
15828                         ]
15829                     }
15830                 ]
15831             };
15832         }
15833         
15834         header = this.header;
15835         
15836        
15837         var cal_heads = function() {
15838             var ret = [];
15839             // fixme - handle this.
15840             
15841             for (var i =0; i < Date.dayNames.length; i++) {
15842                 var d = Date.dayNames[i];
15843                 ret.push({
15844                     tag: 'th',
15845                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15846                     html : d.substring(0,3)
15847                 });
15848                 
15849             }
15850             ret[0].cls += ' fc-first';
15851             ret[6].cls += ' fc-last';
15852             return ret;
15853         };
15854         var cal_cell = function(n) {
15855             return  {
15856                 tag: 'td',
15857                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15858                 cn : [
15859                     {
15860                         cn : [
15861                             {
15862                                 cls: 'fc-day-number',
15863                                 html: 'D'
15864                             },
15865                             {
15866                                 cls: 'fc-day-content',
15867                              
15868                                 cn : [
15869                                      {
15870                                         style: 'position: relative;' // height: 17px;
15871                                     }
15872                                 ]
15873                             }
15874                             
15875                             
15876                         ]
15877                     }
15878                 ]
15879                 
15880             }
15881         };
15882         var cal_rows = function() {
15883             
15884             var ret = [];
15885             for (var r = 0; r < 6; r++) {
15886                 var row= {
15887                     tag : 'tr',
15888                     cls : 'fc-week',
15889                     cn : []
15890                 };
15891                 
15892                 for (var i =0; i < Date.dayNames.length; i++) {
15893                     var d = Date.dayNames[i];
15894                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15895
15896                 }
15897                 row.cn[0].cls+=' fc-first';
15898                 row.cn[0].cn[0].style = 'min-height:90px';
15899                 row.cn[6].cls+=' fc-last';
15900                 ret.push(row);
15901                 
15902             }
15903             ret[0].cls += ' fc-first';
15904             ret[4].cls += ' fc-prev-last';
15905             ret[5].cls += ' fc-last';
15906             return ret;
15907             
15908         };
15909         
15910         var cal_table = {
15911             tag: 'table',
15912             cls: 'fc-border-separate',
15913             style : 'width:100%',
15914             cellspacing  : 0,
15915             cn : [
15916                 { 
15917                     tag: 'thead',
15918                     cn : [
15919                         { 
15920                             tag: 'tr',
15921                             cls : 'fc-first fc-last',
15922                             cn : cal_heads()
15923                         }
15924                     ]
15925                 },
15926                 { 
15927                     tag: 'tbody',
15928                     cn : cal_rows()
15929                 }
15930                   
15931             ]
15932         };
15933          
15934          var cfg = {
15935             cls : 'fc fc-ltr',
15936             cn : [
15937                 header,
15938                 {
15939                     cls : 'fc-content',
15940                     style : "position: relative;",
15941                     cn : [
15942                         {
15943                             cls : 'fc-view fc-view-month fc-grid',
15944                             style : 'position: relative',
15945                             unselectable : 'on',
15946                             cn : [
15947                                 {
15948                                     cls : 'fc-event-container',
15949                                     style : 'position:absolute;z-index:8;top:0;left:0;'
15950                                 },
15951                                 cal_table
15952                             ]
15953                         }
15954                     ]
15955     
15956                 }
15957            ] 
15958             
15959         };
15960         
15961          
15962         
15963         return cfg;
15964     },
15965     
15966     
15967     initEvents : function()
15968     {
15969         if(!this.store){
15970             throw "can not find store for calendar";
15971         }
15972         
15973         var mark = {
15974             tag: "div",
15975             cls:"x-dlg-mask",
15976             style: "text-align:center",
15977             cn: [
15978                 {
15979                     tag: "div",
15980                     style: "background-color:white;width:50%;margin:250 auto",
15981                     cn: [
15982                         {
15983                             tag: "img",
15984                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
15985                         },
15986                         {
15987                             tag: "span",
15988                             html: "Loading"
15989                         }
15990                         
15991                     ]
15992                 }
15993             ]
15994         };
15995         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15996         
15997         var size = this.el.select('.fc-content', true).first().getSize();
15998         this.maskEl.setSize(size.width, size.height);
15999         this.maskEl.enableDisplayMode("block");
16000         if(!this.loadMask){
16001             this.maskEl.hide();
16002         }
16003         
16004         this.store = Roo.factory(this.store, Roo.data);
16005         this.store.on('load', this.onLoad, this);
16006         this.store.on('beforeload', this.onBeforeLoad, this);
16007         
16008         this.resize();
16009         
16010         this.cells = this.el.select('.fc-day',true);
16011         //Roo.log(this.cells);
16012         this.textNodes = this.el.query('.fc-day-number');
16013         this.cells.addClassOnOver('fc-state-hover');
16014         
16015         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16016         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16017         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16018         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16019         
16020         this.on('monthchange', this.onMonthChange, this);
16021         
16022         this.update(new Date().clearTime());
16023     },
16024     
16025     resize : function() {
16026         var sz  = this.el.getSize();
16027         
16028         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16029         this.el.select('.fc-day-content div',true).setHeight(34);
16030     },
16031     
16032     
16033     // private
16034     showPrevMonth : function(e){
16035         this.update(this.activeDate.add("mo", -1));
16036     },
16037     showToday : function(e){
16038         this.update(new Date().clearTime());
16039     },
16040     // private
16041     showNextMonth : function(e){
16042         this.update(this.activeDate.add("mo", 1));
16043     },
16044
16045     // private
16046     showPrevYear : function(){
16047         this.update(this.activeDate.add("y", -1));
16048     },
16049
16050     // private
16051     showNextYear : function(){
16052         this.update(this.activeDate.add("y", 1));
16053     },
16054
16055     
16056    // private
16057     update : function(date)
16058     {
16059         var vd = this.activeDate;
16060         this.activeDate = date;
16061 //        if(vd && this.el){
16062 //            var t = date.getTime();
16063 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16064 //                Roo.log('using add remove');
16065 //                
16066 //                this.fireEvent('monthchange', this, date);
16067 //                
16068 //                this.cells.removeClass("fc-state-highlight");
16069 //                this.cells.each(function(c){
16070 //                   if(c.dateValue == t){
16071 //                       c.addClass("fc-state-highlight");
16072 //                       setTimeout(function(){
16073 //                            try{c.dom.firstChild.focus();}catch(e){}
16074 //                       }, 50);
16075 //                       return false;
16076 //                   }
16077 //                   return true;
16078 //                });
16079 //                return;
16080 //            }
16081 //        }
16082         
16083         var days = date.getDaysInMonth();
16084         
16085         var firstOfMonth = date.getFirstDateOfMonth();
16086         var startingPos = firstOfMonth.getDay()-this.startDay;
16087         
16088         if(startingPos < this.startDay){
16089             startingPos += 7;
16090         }
16091         
16092         var pm = date.add(Date.MONTH, -1);
16093         var prevStart = pm.getDaysInMonth()-startingPos;
16094 //        
16095         this.cells = this.el.select('.fc-day',true);
16096         this.textNodes = this.el.query('.fc-day-number');
16097         this.cells.addClassOnOver('fc-state-hover');
16098         
16099         var cells = this.cells.elements;
16100         var textEls = this.textNodes;
16101         
16102         Roo.each(cells, function(cell){
16103             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16104         });
16105         
16106         days += startingPos;
16107
16108         // convert everything to numbers so it's fast
16109         var day = 86400000;
16110         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16111         //Roo.log(d);
16112         //Roo.log(pm);
16113         //Roo.log(prevStart);
16114         
16115         var today = new Date().clearTime().getTime();
16116         var sel = date.clearTime().getTime();
16117         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16118         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16119         var ddMatch = this.disabledDatesRE;
16120         var ddText = this.disabledDatesText;
16121         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16122         var ddaysText = this.disabledDaysText;
16123         var format = this.format;
16124         
16125         var setCellClass = function(cal, cell){
16126             cell.row = 0;
16127             cell.events = [];
16128             cell.more = [];
16129             //Roo.log('set Cell Class');
16130             cell.title = "";
16131             var t = d.getTime();
16132             
16133             //Roo.log(d);
16134             
16135             cell.dateValue = t;
16136             if(t == today){
16137                 cell.className += " fc-today";
16138                 cell.className += " fc-state-highlight";
16139                 cell.title = cal.todayText;
16140             }
16141             if(t == sel){
16142                 // disable highlight in other month..
16143                 //cell.className += " fc-state-highlight";
16144                 
16145             }
16146             // disabling
16147             if(t < min) {
16148                 cell.className = " fc-state-disabled";
16149                 cell.title = cal.minText;
16150                 return;
16151             }
16152             if(t > max) {
16153                 cell.className = " fc-state-disabled";
16154                 cell.title = cal.maxText;
16155                 return;
16156             }
16157             if(ddays){
16158                 if(ddays.indexOf(d.getDay()) != -1){
16159                     cell.title = ddaysText;
16160                     cell.className = " fc-state-disabled";
16161                 }
16162             }
16163             if(ddMatch && format){
16164                 var fvalue = d.dateFormat(format);
16165                 if(ddMatch.test(fvalue)){
16166                     cell.title = ddText.replace("%0", fvalue);
16167                     cell.className = " fc-state-disabled";
16168                 }
16169             }
16170             
16171             if (!cell.initialClassName) {
16172                 cell.initialClassName = cell.dom.className;
16173             }
16174             
16175             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16176         };
16177
16178         var i = 0;
16179         
16180         for(; i < startingPos; i++) {
16181             textEls[i].innerHTML = (++prevStart);
16182             d.setDate(d.getDate()+1);
16183             
16184             cells[i].className = "fc-past fc-other-month";
16185             setCellClass(this, cells[i]);
16186         }
16187         
16188         var intDay = 0;
16189         
16190         for(; i < days; i++){
16191             intDay = i - startingPos + 1;
16192             textEls[i].innerHTML = (intDay);
16193             d.setDate(d.getDate()+1);
16194             
16195             cells[i].className = ''; // "x-date-active";
16196             setCellClass(this, cells[i]);
16197         }
16198         var extraDays = 0;
16199         
16200         for(; i < 42; i++) {
16201             textEls[i].innerHTML = (++extraDays);
16202             d.setDate(d.getDate()+1);
16203             
16204             cells[i].className = "fc-future fc-other-month";
16205             setCellClass(this, cells[i]);
16206         }
16207         
16208         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16209         
16210         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16211         
16212         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16213         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16214         
16215         if(totalRows != 6){
16216             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16217             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16218         }
16219         
16220         this.fireEvent('monthchange', this, date);
16221         
16222         
16223         /*
16224         if(!this.internalRender){
16225             var main = this.el.dom.firstChild;
16226             var w = main.offsetWidth;
16227             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16228             Roo.fly(main).setWidth(w);
16229             this.internalRender = true;
16230             // opera does not respect the auto grow header center column
16231             // then, after it gets a width opera refuses to recalculate
16232             // without a second pass
16233             if(Roo.isOpera && !this.secondPass){
16234                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16235                 this.secondPass = true;
16236                 this.update.defer(10, this, [date]);
16237             }
16238         }
16239         */
16240         
16241     },
16242     
16243     findCell : function(dt) {
16244         dt = dt.clearTime().getTime();
16245         var ret = false;
16246         this.cells.each(function(c){
16247             //Roo.log("check " +c.dateValue + '?=' + dt);
16248             if(c.dateValue == dt){
16249                 ret = c;
16250                 return false;
16251             }
16252             return true;
16253         });
16254         
16255         return ret;
16256     },
16257     
16258     findCells : function(ev) {
16259         var s = ev.start.clone().clearTime().getTime();
16260        // Roo.log(s);
16261         var e= ev.end.clone().clearTime().getTime();
16262        // Roo.log(e);
16263         var ret = [];
16264         this.cells.each(function(c){
16265              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16266             
16267             if(c.dateValue > e){
16268                 return ;
16269             }
16270             if(c.dateValue < s){
16271                 return ;
16272             }
16273             ret.push(c);
16274         });
16275         
16276         return ret;    
16277     },
16278     
16279 //    findBestRow: function(cells)
16280 //    {
16281 //        var ret = 0;
16282 //        
16283 //        for (var i =0 ; i < cells.length;i++) {
16284 //            ret  = Math.max(cells[i].rows || 0,ret);
16285 //        }
16286 //        return ret;
16287 //        
16288 //    },
16289     
16290     
16291     addItem : function(ev)
16292     {
16293         // look for vertical location slot in
16294         var cells = this.findCells(ev);
16295         
16296 //        ev.row = this.findBestRow(cells);
16297         
16298         // work out the location.
16299         
16300         var crow = false;
16301         var rows = [];
16302         for(var i =0; i < cells.length; i++) {
16303             
16304             cells[i].row = cells[0].row;
16305             
16306             if(i == 0){
16307                 cells[i].row = cells[i].row + 1;
16308             }
16309             
16310             if (!crow) {
16311                 crow = {
16312                     start : cells[i],
16313                     end :  cells[i]
16314                 };
16315                 continue;
16316             }
16317             if (crow.start.getY() == cells[i].getY()) {
16318                 // on same row.
16319                 crow.end = cells[i];
16320                 continue;
16321             }
16322             // different row.
16323             rows.push(crow);
16324             crow = {
16325                 start: cells[i],
16326                 end : cells[i]
16327             };
16328             
16329         }
16330         
16331         rows.push(crow);
16332         ev.els = [];
16333         ev.rows = rows;
16334         ev.cells = cells;
16335         
16336         cells[0].events.push(ev);
16337         
16338         this.calevents.push(ev);
16339     },
16340     
16341     clearEvents: function() {
16342         
16343         if(!this.calevents){
16344             return;
16345         }
16346         
16347         Roo.each(this.cells.elements, function(c){
16348             c.row = 0;
16349             c.events = [];
16350             c.more = [];
16351         });
16352         
16353         Roo.each(this.calevents, function(e) {
16354             Roo.each(e.els, function(el) {
16355                 el.un('mouseenter' ,this.onEventEnter, this);
16356                 el.un('mouseleave' ,this.onEventLeave, this);
16357                 el.remove();
16358             },this);
16359         },this);
16360         
16361         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16362             e.remove();
16363         });
16364         
16365     },
16366     
16367     renderEvents: function()
16368     {   
16369         var _this = this;
16370         
16371         this.cells.each(function(c) {
16372             
16373             if(c.row < 5){
16374                 return;
16375             }
16376             
16377             var ev = c.events;
16378             
16379             var r = 4;
16380             if(c.row != c.events.length){
16381                 r = 4 - (4 - (c.row - c.events.length));
16382             }
16383             
16384             c.events = ev.slice(0, r);
16385             c.more = ev.slice(r);
16386             
16387             if(c.more.length && c.more.length == 1){
16388                 c.events.push(c.more.pop());
16389             }
16390             
16391             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16392             
16393         });
16394             
16395         this.cells.each(function(c) {
16396             
16397             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16398             
16399             
16400             for (var e = 0; e < c.events.length; e++){
16401                 var ev = c.events[e];
16402                 var rows = ev.rows;
16403                 
16404                 for(var i = 0; i < rows.length; i++) {
16405                 
16406                     // how many rows should it span..
16407
16408                     var  cfg = {
16409                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16410                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16411
16412                         unselectable : "on",
16413                         cn : [
16414                             {
16415                                 cls: 'fc-event-inner',
16416                                 cn : [
16417     //                                {
16418     //                                  tag:'span',
16419     //                                  cls: 'fc-event-time',
16420     //                                  html : cells.length > 1 ? '' : ev.time
16421     //                                },
16422                                     {
16423                                       tag:'span',
16424                                       cls: 'fc-event-title',
16425                                       html : String.format('{0}', ev.title)
16426                                     }
16427
16428
16429                                 ]
16430                             },
16431                             {
16432                                 cls: 'ui-resizable-handle ui-resizable-e',
16433                                 html : '&nbsp;&nbsp;&nbsp'
16434                             }
16435
16436                         ]
16437                     };
16438
16439                     if (i == 0) {
16440                         cfg.cls += ' fc-event-start';
16441                     }
16442                     if ((i+1) == rows.length) {
16443                         cfg.cls += ' fc-event-end';
16444                     }
16445
16446                     var ctr = _this.el.select('.fc-event-container',true).first();
16447                     var cg = ctr.createChild(cfg);
16448
16449                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16450                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16451
16452                     var r = (c.more.length) ? 1 : 0;
16453                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16454                     cg.setWidth(ebox.right - sbox.x -2);
16455
16456                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16457                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16458                     cg.on('click', _this.onEventClick, _this, ev);
16459
16460                     ev.els.push(cg);
16461                     
16462                 }
16463                 
16464             }
16465             
16466             
16467             if(c.more.length){
16468                 var  cfg = {
16469                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16470                     style : 'position: absolute',
16471                     unselectable : "on",
16472                     cn : [
16473                         {
16474                             cls: 'fc-event-inner',
16475                             cn : [
16476                                 {
16477                                   tag:'span',
16478                                   cls: 'fc-event-title',
16479                                   html : 'More'
16480                                 }
16481
16482
16483                             ]
16484                         },
16485                         {
16486                             cls: 'ui-resizable-handle ui-resizable-e',
16487                             html : '&nbsp;&nbsp;&nbsp'
16488                         }
16489
16490                     ]
16491                 };
16492
16493                 var ctr = _this.el.select('.fc-event-container',true).first();
16494                 var cg = ctr.createChild(cfg);
16495
16496                 var sbox = c.select('.fc-day-content',true).first().getBox();
16497                 var ebox = c.select('.fc-day-content',true).first().getBox();
16498                 //Roo.log(cg);
16499                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
16500                 cg.setWidth(ebox.right - sbox.x -2);
16501
16502                 cg.on('click', _this.onMoreEventClick, _this, c.more);
16503                 
16504             }
16505             
16506         });
16507         
16508         
16509         
16510     },
16511     
16512     onEventEnter: function (e, el,event,d) {
16513         this.fireEvent('evententer', this, el, event);
16514     },
16515     
16516     onEventLeave: function (e, el,event,d) {
16517         this.fireEvent('eventleave', this, el, event);
16518     },
16519     
16520     onEventClick: function (e, el,event,d) {
16521         this.fireEvent('eventclick', this, el, event);
16522     },
16523     
16524     onMonthChange: function () {
16525         this.store.load();
16526     },
16527     
16528     onMoreEventClick: function(e, el, more)
16529     {
16530         var _this = this;
16531         
16532         this.calpopover.placement = 'right';
16533         this.calpopover.setTitle('More');
16534         
16535         this.calpopover.setContent('');
16536         
16537         var ctr = this.calpopover.el.select('.popover-content', true).first();
16538         
16539         Roo.each(more, function(m){
16540             var cfg = {
16541                 cls : 'fc-event-hori fc-event-draggable',
16542                 html : m.title
16543             };
16544             var cg = ctr.createChild(cfg);
16545             
16546             cg.on('click', _this.onEventClick, _this, m);
16547         });
16548         
16549         this.calpopover.show(el);
16550         
16551         
16552     },
16553     
16554     onLoad: function () 
16555     {   
16556         this.calevents = [];
16557         var cal = this;
16558         
16559         if(this.store.getCount() > 0){
16560             this.store.data.each(function(d){
16561                cal.addItem({
16562                     id : d.data.id,
16563                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16564                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16565                     time : d.data.start_time,
16566                     title : d.data.title,
16567                     description : d.data.description,
16568                     venue : d.data.venue
16569                 });
16570             });
16571         }
16572         
16573         this.renderEvents();
16574         
16575         if(this.calevents.length && this.loadMask){
16576             this.maskEl.hide();
16577         }
16578     },
16579     
16580     onBeforeLoad: function()
16581     {
16582         this.clearEvents();
16583         if(this.loadMask){
16584             this.maskEl.show();
16585         }
16586     }
16587 });
16588
16589  
16590  /*
16591  * - LGPL
16592  *
16593  * element
16594  * 
16595  */
16596
16597 /**
16598  * @class Roo.bootstrap.Popover
16599  * @extends Roo.bootstrap.Component
16600  * Bootstrap Popover class
16601  * @cfg {String} html contents of the popover   (or false to use children..)
16602  * @cfg {String} title of popover (or false to hide)
16603  * @cfg {String} placement how it is placed
16604  * @cfg {String} trigger click || hover (or false to trigger manually)
16605  * @cfg {String} over what (parent or false to trigger manually.)
16606  * @cfg {Number} delay - delay before showing
16607  
16608  * @constructor
16609  * Create a new Popover
16610  * @param {Object} config The config object
16611  */
16612
16613 Roo.bootstrap.Popover = function(config){
16614     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16615     
16616     this.addEvents({
16617         // raw events
16618          /**
16619          * @event show
16620          * After the popover show
16621          * 
16622          * @param {Roo.bootstrap.Popover} this
16623          */
16624         "show" : true,
16625         /**
16626          * @event hide
16627          * After the popover hide
16628          * 
16629          * @param {Roo.bootstrap.Popover} this
16630          */
16631         "hide" : true
16632     });
16633 };
16634
16635 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
16636     
16637     title: 'Fill in a title',
16638     html: false,
16639     
16640     placement : 'right',
16641     trigger : 'hover', // hover
16642     
16643     delay : 0,
16644     
16645     over: 'parent',
16646     
16647     can_build_overlaid : false,
16648     
16649     getChildContainer : function()
16650     {
16651         return this.el.select('.popover-content',true).first();
16652     },
16653     
16654     getAutoCreate : function(){
16655          
16656         var cfg = {
16657            cls : 'popover roo-dynamic',
16658            style: 'display:block',
16659            cn : [
16660                 {
16661                     cls : 'arrow'
16662                 },
16663                 {
16664                     cls : 'popover-inner',
16665                     cn : [
16666                         {
16667                             tag: 'h3',
16668                             cls: 'popover-title',
16669                             html : this.title
16670                         },
16671                         {
16672                             cls : 'popover-content',
16673                             html : this.html
16674                         }
16675                     ]
16676                     
16677                 }
16678            ]
16679         };
16680         
16681         return cfg;
16682     },
16683     setTitle: function(str)
16684     {
16685         this.title = str;
16686         this.el.select('.popover-title',true).first().dom.innerHTML = str;
16687     },
16688     setContent: function(str)
16689     {
16690         this.html = str;
16691         this.el.select('.popover-content',true).first().dom.innerHTML = str;
16692     },
16693     // as it get's added to the bottom of the page.
16694     onRender : function(ct, position)
16695     {
16696         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16697         if(!this.el){
16698             var cfg = Roo.apply({},  this.getAutoCreate());
16699             cfg.id = Roo.id();
16700             
16701             if (this.cls) {
16702                 cfg.cls += ' ' + this.cls;
16703             }
16704             if (this.style) {
16705                 cfg.style = this.style;
16706             }
16707             //Roo.log("adding to ");
16708             this.el = Roo.get(document.body).createChild(cfg, position);
16709 //            Roo.log(this.el);
16710         }
16711         this.initEvents();
16712     },
16713     
16714     initEvents : function()
16715     {
16716         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16717         this.el.enableDisplayMode('block');
16718         this.el.hide();
16719         if (this.over === false) {
16720             return; 
16721         }
16722         if (this.triggers === false) {
16723             return;
16724         }
16725         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16726         var triggers = this.trigger ? this.trigger.split(' ') : [];
16727         Roo.each(triggers, function(trigger) {
16728         
16729             if (trigger == 'click') {
16730                 on_el.on('click', this.toggle, this);
16731             } else if (trigger != 'manual') {
16732                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
16733                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16734       
16735                 on_el.on(eventIn  ,this.enter, this);
16736                 on_el.on(eventOut, this.leave, this);
16737             }
16738         }, this);
16739         
16740     },
16741     
16742     
16743     // private
16744     timeout : null,
16745     hoverState : null,
16746     
16747     toggle : function () {
16748         this.hoverState == 'in' ? this.leave() : this.enter();
16749     },
16750     
16751     enter : function () {
16752         
16753         clearTimeout(this.timeout);
16754     
16755         this.hoverState = 'in';
16756     
16757         if (!this.delay || !this.delay.show) {
16758             this.show();
16759             return;
16760         }
16761         var _t = this;
16762         this.timeout = setTimeout(function () {
16763             if (_t.hoverState == 'in') {
16764                 _t.show();
16765             }
16766         }, this.delay.show)
16767     },
16768     
16769     leave : function() {
16770         clearTimeout(this.timeout);
16771     
16772         this.hoverState = 'out';
16773     
16774         if (!this.delay || !this.delay.hide) {
16775             this.hide();
16776             return;
16777         }
16778         var _t = this;
16779         this.timeout = setTimeout(function () {
16780             if (_t.hoverState == 'out') {
16781                 _t.hide();
16782             }
16783         }, this.delay.hide)
16784     },
16785     
16786     show : function (on_el)
16787     {
16788         if (!on_el) {
16789             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16790         }
16791         
16792         // set content.
16793         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16794         if (this.html !== false) {
16795             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16796         }
16797         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16798         if (!this.title.length) {
16799             this.el.select('.popover-title',true).hide();
16800         }
16801         
16802         var placement = typeof this.placement == 'function' ?
16803             this.placement.call(this, this.el, on_el) :
16804             this.placement;
16805             
16806         var autoToken = /\s?auto?\s?/i;
16807         var autoPlace = autoToken.test(placement);
16808         if (autoPlace) {
16809             placement = placement.replace(autoToken, '') || 'top';
16810         }
16811         
16812         //this.el.detach()
16813         //this.el.setXY([0,0]);
16814         this.el.show();
16815         this.el.dom.style.display='block';
16816         this.el.addClass(placement);
16817         
16818         //this.el.appendTo(on_el);
16819         
16820         var p = this.getPosition();
16821         var box = this.el.getBox();
16822         
16823         if (autoPlace) {
16824             // fixme..
16825         }
16826         var align = Roo.bootstrap.Popover.alignment[placement];
16827         this.el.alignTo(on_el, align[0],align[1]);
16828         //var arrow = this.el.select('.arrow',true).first();
16829         //arrow.set(align[2], 
16830         
16831         this.el.addClass('in');
16832         
16833         
16834         if (this.el.hasClass('fade')) {
16835             // fade it?
16836         }
16837         
16838         this.hoverState = 'in';
16839         
16840         this.fireEvent('show', this);
16841         
16842     },
16843     hide : function()
16844     {
16845         this.el.setXY([0,0]);
16846         this.el.removeClass('in');
16847         this.el.hide();
16848         this.hoverState = null;
16849         
16850         this.fireEvent('hide', this);
16851     }
16852     
16853 });
16854
16855 Roo.bootstrap.Popover.alignment = {
16856     'left' : ['r-l', [-10,0], 'right'],
16857     'right' : ['l-r', [10,0], 'left'],
16858     'bottom' : ['t-b', [0,10], 'top'],
16859     'top' : [ 'b-t', [0,-10], 'bottom']
16860 };
16861
16862  /*
16863  * - LGPL
16864  *
16865  * Progress
16866  * 
16867  */
16868
16869 /**
16870  * @class Roo.bootstrap.Progress
16871  * @extends Roo.bootstrap.Component
16872  * Bootstrap Progress class
16873  * @cfg {Boolean} striped striped of the progress bar
16874  * @cfg {Boolean} active animated of the progress bar
16875  * 
16876  * 
16877  * @constructor
16878  * Create a new Progress
16879  * @param {Object} config The config object
16880  */
16881
16882 Roo.bootstrap.Progress = function(config){
16883     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16884 };
16885
16886 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
16887     
16888     striped : false,
16889     active: false,
16890     
16891     getAutoCreate : function(){
16892         var cfg = {
16893             tag: 'div',
16894             cls: 'progress'
16895         };
16896         
16897         
16898         if(this.striped){
16899             cfg.cls += ' progress-striped';
16900         }
16901       
16902         if(this.active){
16903             cfg.cls += ' active';
16904         }
16905         
16906         
16907         return cfg;
16908     }
16909    
16910 });
16911
16912  
16913
16914  /*
16915  * - LGPL
16916  *
16917  * ProgressBar
16918  * 
16919  */
16920
16921 /**
16922  * @class Roo.bootstrap.ProgressBar
16923  * @extends Roo.bootstrap.Component
16924  * Bootstrap ProgressBar class
16925  * @cfg {Number} aria_valuenow aria-value now
16926  * @cfg {Number} aria_valuemin aria-value min
16927  * @cfg {Number} aria_valuemax aria-value max
16928  * @cfg {String} label label for the progress bar
16929  * @cfg {String} panel (success | info | warning | danger )
16930  * @cfg {String} role role of the progress bar
16931  * @cfg {String} sr_only text
16932  * 
16933  * 
16934  * @constructor
16935  * Create a new ProgressBar
16936  * @param {Object} config The config object
16937  */
16938
16939 Roo.bootstrap.ProgressBar = function(config){
16940     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16941 };
16942
16943 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
16944     
16945     aria_valuenow : 0,
16946     aria_valuemin : 0,
16947     aria_valuemax : 100,
16948     label : false,
16949     panel : false,
16950     role : false,
16951     sr_only: false,
16952     
16953     getAutoCreate : function()
16954     {
16955         
16956         var cfg = {
16957             tag: 'div',
16958             cls: 'progress-bar',
16959             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16960         };
16961         
16962         if(this.sr_only){
16963             cfg.cn = {
16964                 tag: 'span',
16965                 cls: 'sr-only',
16966                 html: this.sr_only
16967             }
16968         }
16969         
16970         if(this.role){
16971             cfg.role = this.role;
16972         }
16973         
16974         if(this.aria_valuenow){
16975             cfg['aria-valuenow'] = this.aria_valuenow;
16976         }
16977         
16978         if(this.aria_valuemin){
16979             cfg['aria-valuemin'] = this.aria_valuemin;
16980         }
16981         
16982         if(this.aria_valuemax){
16983             cfg['aria-valuemax'] = this.aria_valuemax;
16984         }
16985         
16986         if(this.label && !this.sr_only){
16987             cfg.html = this.label;
16988         }
16989         
16990         if(this.panel){
16991             cfg.cls += ' progress-bar-' + this.panel;
16992         }
16993         
16994         return cfg;
16995     },
16996     
16997     update : function(aria_valuenow)
16998     {
16999         this.aria_valuenow = aria_valuenow;
17000         
17001         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17002     }
17003    
17004 });
17005
17006  
17007
17008  /*
17009  * - LGPL
17010  *
17011  * column
17012  * 
17013  */
17014
17015 /**
17016  * @class Roo.bootstrap.TabGroup
17017  * @extends Roo.bootstrap.Column
17018  * Bootstrap Column class
17019  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17020  * @cfg {Boolean} carousel true to make the group behave like a carousel
17021  * @cfg {Boolean} bullets show bullets for the panels
17022  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17023  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17024  * @cfg {Boolean} showarrow (true|false) show arrow default true
17025  * 
17026  * @constructor
17027  * Create a new TabGroup
17028  * @param {Object} config The config object
17029  */
17030
17031 Roo.bootstrap.TabGroup = function(config){
17032     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17033     if (!this.navId) {
17034         this.navId = Roo.id();
17035     }
17036     this.tabs = [];
17037     Roo.bootstrap.TabGroup.register(this);
17038     
17039 };
17040
17041 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17042     
17043     carousel : false,
17044     transition : false,
17045     bullets : 0,
17046     timer : 0,
17047     autoslide : false,
17048     slideFn : false,
17049     slideOnTouch : false,
17050     showarrow : true,
17051     
17052     getAutoCreate : function()
17053     {
17054         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17055         
17056         cfg.cls += ' tab-content';
17057         
17058         if (this.carousel) {
17059             cfg.cls += ' carousel slide';
17060             
17061             cfg.cn = [{
17062                cls : 'carousel-inner',
17063                cn : []
17064             }];
17065         
17066             if(this.bullets  && !Roo.isTouch){
17067                 
17068                 var bullets = {
17069                     cls : 'carousel-bullets',
17070                     cn : []
17071                 };
17072                
17073                 if(this.bullets_cls){
17074                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17075                 }
17076                 
17077                 bullets.cn.push({
17078                     cls : 'clear'
17079                 });
17080                 
17081                 cfg.cn[0].cn.push(bullets);
17082             }
17083             
17084             if(this.showarrow){
17085                 cfg.cn[0].cn.push({
17086                     tag : 'div',
17087                     class : 'carousel-arrow',
17088                     cn : [
17089                         {
17090                             tag : 'div',
17091                             class : 'carousel-prev',
17092                             cn : [
17093                                 {
17094                                     tag : 'i',
17095                                     class : 'fa fa-chevron-left'
17096                                 }
17097                             ]
17098                         },
17099                         {
17100                             tag : 'div',
17101                             class : 'carousel-next',
17102                             cn : [
17103                                 {
17104                                     tag : 'i',
17105                                     class : 'fa fa-chevron-right'
17106                                 }
17107                             ]
17108                         }
17109                     ]
17110                 });
17111             }
17112             
17113         }
17114         
17115         return cfg;
17116     },
17117     
17118     initEvents:  function()
17119     {
17120 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17121 //            this.el.on("touchstart", this.onTouchStart, this);
17122 //        }
17123         
17124         if(this.autoslide){
17125             var _this = this;
17126             
17127             this.slideFn = window.setInterval(function() {
17128                 _this.showPanelNext();
17129             }, this.timer);
17130         }
17131         
17132         if(this.showarrow){
17133             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17134             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17135         }
17136         
17137         
17138     },
17139     
17140 //    onTouchStart : function(e, el, o)
17141 //    {
17142 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17143 //            return;
17144 //        }
17145 //        
17146 //        this.showPanelNext();
17147 //    },
17148     
17149     
17150     getChildContainer : function()
17151     {
17152         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17153     },
17154     
17155     /**
17156     * register a Navigation item
17157     * @param {Roo.bootstrap.NavItem} the navitem to add
17158     */
17159     register : function(item)
17160     {
17161         this.tabs.push( item);
17162         item.navId = this.navId; // not really needed..
17163         this.addBullet();
17164     
17165     },
17166     
17167     getActivePanel : function()
17168     {
17169         var r = false;
17170         Roo.each(this.tabs, function(t) {
17171             if (t.active) {
17172                 r = t;
17173                 return false;
17174             }
17175             return null;
17176         });
17177         return r;
17178         
17179     },
17180     getPanelByName : function(n)
17181     {
17182         var r = false;
17183         Roo.each(this.tabs, function(t) {
17184             if (t.tabId == n) {
17185                 r = t;
17186                 return false;
17187             }
17188             return null;
17189         });
17190         return r;
17191     },
17192     indexOfPanel : function(p)
17193     {
17194         var r = false;
17195         Roo.each(this.tabs, function(t,i) {
17196             if (t.tabId == p.tabId) {
17197                 r = i;
17198                 return false;
17199             }
17200             return null;
17201         });
17202         return r;
17203     },
17204     /**
17205      * show a specific panel
17206      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17207      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17208      */
17209     showPanel : function (pan)
17210     {
17211         if(this.transition || typeof(pan) == 'undefined'){
17212             Roo.log("waiting for the transitionend");
17213             return;
17214         }
17215         
17216         if (typeof(pan) == 'number') {
17217             pan = this.tabs[pan];
17218         }
17219         
17220         if (typeof(pan) == 'string') {
17221             pan = this.getPanelByName(pan);
17222         }
17223         
17224         var cur = this.getActivePanel();
17225         
17226         if(!pan || !cur){
17227             Roo.log('pan or acitve pan is undefined');
17228             return false;
17229         }
17230         
17231         if (pan.tabId == this.getActivePanel().tabId) {
17232             return true;
17233         }
17234         
17235         if (false === cur.fireEvent('beforedeactivate')) {
17236             return false;
17237         }
17238         
17239         if(this.bullets > 0 && !Roo.isTouch){
17240             this.setActiveBullet(this.indexOfPanel(pan));
17241         }
17242         
17243         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17244             
17245             this.transition = true;
17246             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17247             var lr = dir == 'next' ? 'left' : 'right';
17248             pan.el.addClass(dir); // or prev
17249             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17250             cur.el.addClass(lr); // or right
17251             pan.el.addClass(lr);
17252             
17253             var _this = this;
17254             cur.el.on('transitionend', function() {
17255                 Roo.log("trans end?");
17256                 
17257                 pan.el.removeClass([lr,dir]);
17258                 pan.setActive(true);
17259                 
17260                 cur.el.removeClass([lr]);
17261                 cur.setActive(false);
17262                 
17263                 _this.transition = false;
17264                 
17265             }, this, { single:  true } );
17266             
17267             return true;
17268         }
17269         
17270         cur.setActive(false);
17271         pan.setActive(true);
17272         
17273         return true;
17274         
17275     },
17276     showPanelNext : function()
17277     {
17278         var i = this.indexOfPanel(this.getActivePanel());
17279         
17280         if (i >= this.tabs.length - 1 && !this.autoslide) {
17281             return;
17282         }
17283         
17284         if (i >= this.tabs.length - 1 && this.autoslide) {
17285             i = -1;
17286         }
17287         
17288         this.showPanel(this.tabs[i+1]);
17289     },
17290     
17291     showPanelPrev : function()
17292     {
17293         var i = this.indexOfPanel(this.getActivePanel());
17294         
17295         if (i  < 1 && !this.autoslide) {
17296             return;
17297         }
17298         
17299         if (i < 1 && this.autoslide) {
17300             i = this.tabs.length;
17301         }
17302         
17303         this.showPanel(this.tabs[i-1]);
17304     },
17305     
17306     
17307     addBullet: function()
17308     {
17309         if(!this.bullets || Roo.isTouch){
17310             return;
17311         }
17312         var ctr = this.el.select('.carousel-bullets',true).first();
17313         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17314         var bullet = ctr.createChild({
17315             cls : 'bullet bullet-' + i
17316         },ctr.dom.lastChild);
17317         
17318         
17319         var _this = this;
17320         
17321         bullet.on('click', (function(e, el, o, ii, t){
17322
17323             e.preventDefault();
17324
17325             this.showPanel(ii);
17326
17327             if(this.autoslide && this.slideFn){
17328                 clearInterval(this.slideFn);
17329                 this.slideFn = window.setInterval(function() {
17330                     _this.showPanelNext();
17331                 }, this.timer);
17332             }
17333
17334         }).createDelegate(this, [i, bullet], true));
17335                 
17336         
17337     },
17338      
17339     setActiveBullet : function(i)
17340     {
17341         if(Roo.isTouch){
17342             return;
17343         }
17344         
17345         Roo.each(this.el.select('.bullet', true).elements, function(el){
17346             el.removeClass('selected');
17347         });
17348
17349         var bullet = this.el.select('.bullet-' + i, true).first();
17350         
17351         if(!bullet){
17352             return;
17353         }
17354         
17355         bullet.addClass('selected');
17356     }
17357     
17358     
17359   
17360 });
17361
17362  
17363
17364  
17365  
17366 Roo.apply(Roo.bootstrap.TabGroup, {
17367     
17368     groups: {},
17369      /**
17370     * register a Navigation Group
17371     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17372     */
17373     register : function(navgrp)
17374     {
17375         this.groups[navgrp.navId] = navgrp;
17376         
17377     },
17378     /**
17379     * fetch a Navigation Group based on the navigation ID
17380     * if one does not exist , it will get created.
17381     * @param {string} the navgroup to add
17382     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17383     */
17384     get: function(navId) {
17385         if (typeof(this.groups[navId]) == 'undefined') {
17386             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17387         }
17388         return this.groups[navId] ;
17389     }
17390     
17391     
17392     
17393 });
17394
17395  /*
17396  * - LGPL
17397  *
17398  * TabPanel
17399  * 
17400  */
17401
17402 /**
17403  * @class Roo.bootstrap.TabPanel
17404  * @extends Roo.bootstrap.Component
17405  * Bootstrap TabPanel class
17406  * @cfg {Boolean} active panel active
17407  * @cfg {String} html panel content
17408  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17409  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17410  * @cfg {String} href click to link..
17411  * 
17412  * 
17413  * @constructor
17414  * Create a new TabPanel
17415  * @param {Object} config The config object
17416  */
17417
17418 Roo.bootstrap.TabPanel = function(config){
17419     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17420     this.addEvents({
17421         /**
17422              * @event changed
17423              * Fires when the active status changes
17424              * @param {Roo.bootstrap.TabPanel} this
17425              * @param {Boolean} state the new state
17426             
17427          */
17428         'changed': true,
17429         /**
17430              * @event beforedeactivate
17431              * Fires before a tab is de-activated - can be used to do validation on a form.
17432              * @param {Roo.bootstrap.TabPanel} this
17433              * @return {Boolean} false if there is an error
17434             
17435          */
17436         'beforedeactivate': true
17437      });
17438     
17439     this.tabId = this.tabId || Roo.id();
17440   
17441 };
17442
17443 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17444     
17445     active: false,
17446     html: false,
17447     tabId: false,
17448     navId : false,
17449     href : '',
17450     
17451     getAutoCreate : function(){
17452         var cfg = {
17453             tag: 'div',
17454             // item is needed for carousel - not sure if it has any effect otherwise
17455             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17456             html: this.html || ''
17457         };
17458         
17459         if(this.active){
17460             cfg.cls += ' active';
17461         }
17462         
17463         if(this.tabId){
17464             cfg.tabId = this.tabId;
17465         }
17466         
17467         
17468         return cfg;
17469     },
17470     
17471     initEvents:  function()
17472     {
17473         var p = this.parent();
17474         
17475         this.navId = this.navId || p.navId;
17476         
17477         if (typeof(this.navId) != 'undefined') {
17478             // not really needed.. but just in case.. parent should be a NavGroup.
17479             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17480             
17481             tg.register(this);
17482             
17483             var i = tg.tabs.length - 1;
17484             
17485             if(this.active && tg.bullets > 0 && i < tg.bullets){
17486                 tg.setActiveBullet(i);
17487             }
17488         }
17489         
17490         this.el.on('click', this.onClick, this);
17491         
17492         if(Roo.isTouch){
17493             this.el.on("touchstart", this.onTouchStart, this);
17494             this.el.on("touchmove", this.onTouchMove, this);
17495             this.el.on("touchend", this.onTouchEnd, this);
17496         }
17497         
17498     },
17499     
17500     onRender : function(ct, position)
17501     {
17502         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17503     },
17504     
17505     setActive : function(state)
17506     {
17507         Roo.log("panel - set active " + this.tabId + "=" + state);
17508         
17509         this.active = state;
17510         if (!state) {
17511             this.el.removeClass('active');
17512             
17513         } else  if (!this.el.hasClass('active')) {
17514             this.el.addClass('active');
17515         }
17516         
17517         this.fireEvent('changed', this, state);
17518     },
17519     
17520     onClick : function(e)
17521     {
17522         e.preventDefault();
17523         
17524         if(!this.href.length){
17525             return;
17526         }
17527         
17528         window.location.href = this.href;
17529     },
17530     
17531     startX : 0,
17532     startY : 0,
17533     endX : 0,
17534     endY : 0,
17535     swiping : false,
17536     
17537     onTouchStart : function(e)
17538     {
17539         this.swiping = false;
17540         
17541         this.startX = e.browserEvent.touches[0].clientX;
17542         this.startY = e.browserEvent.touches[0].clientY;
17543     },
17544     
17545     onTouchMove : function(e)
17546     {
17547         this.swiping = true;
17548         
17549         this.endX = e.browserEvent.touches[0].clientX;
17550         this.endY = e.browserEvent.touches[0].clientY;
17551     },
17552     
17553     onTouchEnd : function(e)
17554     {
17555         if(!this.swiping){
17556             this.onClick(e);
17557             return;
17558         }
17559         
17560         var tabGroup = this.parent();
17561         
17562         if(this.endX > this.startX){ // swiping right
17563             tabGroup.showPanelPrev();
17564             return;
17565         }
17566         
17567         if(this.startX > this.endX){ // swiping left
17568             tabGroup.showPanelNext();
17569             return;
17570         }
17571     }
17572     
17573     
17574 });
17575  
17576
17577  
17578
17579  /*
17580  * - LGPL
17581  *
17582  * DateField
17583  * 
17584  */
17585
17586 /**
17587  * @class Roo.bootstrap.DateField
17588  * @extends Roo.bootstrap.Input
17589  * Bootstrap DateField class
17590  * @cfg {Number} weekStart default 0
17591  * @cfg {String} viewMode default empty, (months|years)
17592  * @cfg {String} minViewMode default empty, (months|years)
17593  * @cfg {Number} startDate default -Infinity
17594  * @cfg {Number} endDate default Infinity
17595  * @cfg {Boolean} todayHighlight default false
17596  * @cfg {Boolean} todayBtn default false
17597  * @cfg {Boolean} calendarWeeks default false
17598  * @cfg {Object} daysOfWeekDisabled default empty
17599  * @cfg {Boolean} singleMode default false (true | false)
17600  * 
17601  * @cfg {Boolean} keyboardNavigation default true
17602  * @cfg {String} language default en
17603  * 
17604  * @constructor
17605  * Create a new DateField
17606  * @param {Object} config The config object
17607  */
17608
17609 Roo.bootstrap.DateField = function(config){
17610     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
17611      this.addEvents({
17612             /**
17613              * @event show
17614              * Fires when this field show.
17615              * @param {Roo.bootstrap.DateField} this
17616              * @param {Mixed} date The date value
17617              */
17618             show : true,
17619             /**
17620              * @event show
17621              * Fires when this field hide.
17622              * @param {Roo.bootstrap.DateField} this
17623              * @param {Mixed} date The date value
17624              */
17625             hide : true,
17626             /**
17627              * @event select
17628              * Fires when select a date.
17629              * @param {Roo.bootstrap.DateField} this
17630              * @param {Mixed} date The date value
17631              */
17632             select : true,
17633             /**
17634              * @event beforeselect
17635              * Fires when before select a date.
17636              * @param {Roo.bootstrap.DateField} this
17637              * @param {Mixed} date The date value
17638              */
17639             beforeselect : true
17640         });
17641 };
17642
17643 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
17644     
17645     /**
17646      * @cfg {String} format
17647      * The default date format string which can be overriden for localization support.  The format must be
17648      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17649      */
17650     format : "m/d/y",
17651     /**
17652      * @cfg {String} altFormats
17653      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17654      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17655      */
17656     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17657     
17658     weekStart : 0,
17659     
17660     viewMode : '',
17661     
17662     minViewMode : '',
17663     
17664     todayHighlight : false,
17665     
17666     todayBtn: false,
17667     
17668     language: 'en',
17669     
17670     keyboardNavigation: true,
17671     
17672     calendarWeeks: false,
17673     
17674     startDate: -Infinity,
17675     
17676     endDate: Infinity,
17677     
17678     daysOfWeekDisabled: [],
17679     
17680     _events: [],
17681     
17682     singleMode : false,
17683     
17684     UTCDate: function()
17685     {
17686         return new Date(Date.UTC.apply(Date, arguments));
17687     },
17688     
17689     UTCToday: function()
17690     {
17691         var today = new Date();
17692         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
17693     },
17694     
17695     getDate: function() {
17696             var d = this.getUTCDate();
17697             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
17698     },
17699     
17700     getUTCDate: function() {
17701             return this.date;
17702     },
17703     
17704     setDate: function(d) {
17705             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
17706     },
17707     
17708     setUTCDate: function(d) {
17709             this.date = d;
17710             this.setValue(this.formatDate(this.date));
17711     },
17712         
17713     onRender: function(ct, position)
17714     {
17715         
17716         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
17717         
17718         this.language = this.language || 'en';
17719         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
17720         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
17721         
17722         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17723         this.format = this.format || 'm/d/y';
17724         this.isInline = false;
17725         this.isInput = true;
17726         this.component = this.el.select('.add-on', true).first() || false;
17727         this.component = (this.component && this.component.length === 0) ? false : this.component;
17728         this.hasInput = this.component && this.inputEl().length;
17729         
17730         if (typeof(this.minViewMode === 'string')) {
17731             switch (this.minViewMode) {
17732                 case 'months':
17733                     this.minViewMode = 1;
17734                     break;
17735                 case 'years':
17736                     this.minViewMode = 2;
17737                     break;
17738                 default:
17739                     this.minViewMode = 0;
17740                     break;
17741             }
17742         }
17743         
17744         if (typeof(this.viewMode === 'string')) {
17745             switch (this.viewMode) {
17746                 case 'months':
17747                     this.viewMode = 1;
17748                     break;
17749                 case 'years':
17750                     this.viewMode = 2;
17751                     break;
17752                 default:
17753                     this.viewMode = 0;
17754                     break;
17755             }
17756         }
17757                 
17758         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
17759         
17760 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
17761         
17762         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17763         
17764         this.picker().on('mousedown', this.onMousedown, this);
17765         this.picker().on('click', this.onClick, this);
17766         
17767         this.picker().addClass('datepicker-dropdown');
17768         
17769         this.startViewMode = this.viewMode;
17770         
17771         if(this.singleMode){
17772             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17773                 v.setVisibilityMode(Roo.Element.DISPLAY);
17774                 v.hide();
17775             });
17776             
17777             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17778                 v.setStyle('width', '189px');
17779             });
17780         }
17781         
17782         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17783             if(!this.calendarWeeks){
17784                 v.remove();
17785                 return;
17786             }
17787             
17788             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17789             v.attr('colspan', function(i, val){
17790                 return parseInt(val) + 1;
17791             });
17792         });
17793                         
17794         
17795         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17796         
17797         this.setStartDate(this.startDate);
17798         this.setEndDate(this.endDate);
17799         
17800         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17801         
17802         this.fillDow();
17803         this.fillMonths();
17804         this.update();
17805         this.showMode();
17806         
17807         if(this.isInline) {
17808             this.show();
17809         }
17810     },
17811     
17812     picker : function()
17813     {
17814         return this.pickerEl;
17815 //        return this.el.select('.datepicker', true).first();
17816     },
17817     
17818     fillDow: function()
17819     {
17820         var dowCnt = this.weekStart;
17821         
17822         var dow = {
17823             tag: 'tr',
17824             cn: [
17825                 
17826             ]
17827         };
17828         
17829         if(this.calendarWeeks){
17830             dow.cn.push({
17831                 tag: 'th',
17832                 cls: 'cw',
17833                 html: '&nbsp;'
17834             })
17835         }
17836         
17837         while (dowCnt < this.weekStart + 7) {
17838             dow.cn.push({
17839                 tag: 'th',
17840                 cls: 'dow',
17841                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17842             });
17843         }
17844         
17845         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17846     },
17847     
17848     fillMonths: function()
17849     {    
17850         var i = 0;
17851         var months = this.picker().select('>.datepicker-months td', true).first();
17852         
17853         months.dom.innerHTML = '';
17854         
17855         while (i < 12) {
17856             var month = {
17857                 tag: 'span',
17858                 cls: 'month',
17859                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17860             };
17861             
17862             months.createChild(month);
17863         }
17864         
17865     },
17866     
17867     update: function()
17868     {
17869         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;
17870         
17871         if (this.date < this.startDate) {
17872             this.viewDate = new Date(this.startDate);
17873         } else if (this.date > this.endDate) {
17874             this.viewDate = new Date(this.endDate);
17875         } else {
17876             this.viewDate = new Date(this.date);
17877         }
17878         
17879         this.fill();
17880     },
17881     
17882     fill: function() 
17883     {
17884         var d = new Date(this.viewDate),
17885                 year = d.getUTCFullYear(),
17886                 month = d.getUTCMonth(),
17887                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17888                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17889                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17890                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17891                 currentDate = this.date && this.date.valueOf(),
17892                 today = this.UTCToday();
17893         
17894         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17895         
17896 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17897         
17898 //        this.picker.select('>tfoot th.today').
17899 //                                              .text(dates[this.language].today)
17900 //                                              .toggle(this.todayBtn !== false);
17901     
17902         this.updateNavArrows();
17903         this.fillMonths();
17904                                                 
17905         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17906         
17907         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17908          
17909         prevMonth.setUTCDate(day);
17910         
17911         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17912         
17913         var nextMonth = new Date(prevMonth);
17914         
17915         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17916         
17917         nextMonth = nextMonth.valueOf();
17918         
17919         var fillMonths = false;
17920         
17921         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17922         
17923         while(prevMonth.valueOf() < nextMonth) {
17924             var clsName = '';
17925             
17926             if (prevMonth.getUTCDay() === this.weekStart) {
17927                 if(fillMonths){
17928                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17929                 }
17930                     
17931                 fillMonths = {
17932                     tag: 'tr',
17933                     cn: []
17934                 };
17935                 
17936                 if(this.calendarWeeks){
17937                     // ISO 8601: First week contains first thursday.
17938                     // ISO also states week starts on Monday, but we can be more abstract here.
17939                     var
17940                     // Start of current week: based on weekstart/current date
17941                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17942                     // Thursday of this week
17943                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17944                     // First Thursday of year, year from thursday
17945                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17946                     // Calendar week: ms between thursdays, div ms per day, div 7 days
17947                     calWeek =  (th - yth) / 864e5 / 7 + 1;
17948                     
17949                     fillMonths.cn.push({
17950                         tag: 'td',
17951                         cls: 'cw',
17952                         html: calWeek
17953                     });
17954                 }
17955             }
17956             
17957             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17958                 clsName += ' old';
17959             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17960                 clsName += ' new';
17961             }
17962             if (this.todayHighlight &&
17963                 prevMonth.getUTCFullYear() == today.getFullYear() &&
17964                 prevMonth.getUTCMonth() == today.getMonth() &&
17965                 prevMonth.getUTCDate() == today.getDate()) {
17966                 clsName += ' today';
17967             }
17968             
17969             if (currentDate && prevMonth.valueOf() === currentDate) {
17970                 clsName += ' active';
17971             }
17972             
17973             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17974                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17975                     clsName += ' disabled';
17976             }
17977             
17978             fillMonths.cn.push({
17979                 tag: 'td',
17980                 cls: 'day ' + clsName,
17981                 html: prevMonth.getDate()
17982             });
17983             
17984             prevMonth.setDate(prevMonth.getDate()+1);
17985         }
17986           
17987         var currentYear = this.date && this.date.getUTCFullYear();
17988         var currentMonth = this.date && this.date.getUTCMonth();
17989         
17990         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17991         
17992         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17993             v.removeClass('active');
17994             
17995             if(currentYear === year && k === currentMonth){
17996                 v.addClass('active');
17997             }
17998             
17999             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18000                 v.addClass('disabled');
18001             }
18002             
18003         });
18004         
18005         
18006         year = parseInt(year/10, 10) * 10;
18007         
18008         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18009         
18010         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18011         
18012         year -= 1;
18013         for (var i = -1; i < 11; i++) {
18014             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18015                 tag: 'span',
18016                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18017                 html: year
18018             });
18019             
18020             year += 1;
18021         }
18022     },
18023     
18024     showMode: function(dir) 
18025     {
18026         if (dir) {
18027             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18028         }
18029         
18030         Roo.each(this.picker().select('>div',true).elements, function(v){
18031             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18032             v.hide();
18033         });
18034         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18035     },
18036     
18037     place: function()
18038     {
18039         if(this.isInline) {
18040             return;
18041         }
18042         
18043         this.picker().removeClass(['bottom', 'top']);
18044         
18045         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18046             /*
18047              * place to the top of element!
18048              *
18049              */
18050             
18051             this.picker().addClass('top');
18052             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18053             
18054             return;
18055         }
18056         
18057         this.picker().addClass('bottom');
18058         
18059         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18060     },
18061     
18062     parseDate : function(value)
18063     {
18064         if(!value || value instanceof Date){
18065             return value;
18066         }
18067         var v = Date.parseDate(value, this.format);
18068         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18069             v = Date.parseDate(value, 'Y-m-d');
18070         }
18071         if(!v && this.altFormats){
18072             if(!this.altFormatsArray){
18073                 this.altFormatsArray = this.altFormats.split("|");
18074             }
18075             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18076                 v = Date.parseDate(value, this.altFormatsArray[i]);
18077             }
18078         }
18079         return v;
18080     },
18081     
18082     formatDate : function(date, fmt)
18083     {   
18084         return (!date || !(date instanceof Date)) ?
18085         date : date.dateFormat(fmt || this.format);
18086     },
18087     
18088     onFocus : function()
18089     {
18090         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18091         this.show();
18092     },
18093     
18094     onBlur : function()
18095     {
18096         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18097         
18098         var d = this.inputEl().getValue();
18099         
18100         this.setValue(d);
18101                 
18102         this.hide();
18103     },
18104     
18105     show : function()
18106     {
18107         this.picker().show();
18108         this.update();
18109         this.place();
18110         
18111         this.fireEvent('show', this, this.date);
18112     },
18113     
18114     hide : function()
18115     {
18116         if(this.isInline) {
18117             return;
18118         }
18119         this.picker().hide();
18120         this.viewMode = this.startViewMode;
18121         this.showMode();
18122         
18123         this.fireEvent('hide', this, this.date);
18124         
18125     },
18126     
18127     onMousedown: function(e)
18128     {
18129         e.stopPropagation();
18130         e.preventDefault();
18131     },
18132     
18133     keyup: function(e)
18134     {
18135         Roo.bootstrap.DateField.superclass.keyup.call(this);
18136         this.update();
18137     },
18138
18139     setValue: function(v)
18140     {
18141         if(this.fireEvent('beforeselect', this, v) !== false){
18142             var d = new Date(this.parseDate(v) ).clearTime();
18143         
18144             if(isNaN(d.getTime())){
18145                 this.date = this.viewDate = '';
18146                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18147                 return;
18148             }
18149
18150             v = this.formatDate(d);
18151
18152             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18153
18154             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18155
18156             this.update();
18157
18158             this.fireEvent('select', this, this.date);
18159         }
18160     },
18161     
18162     getValue: function()
18163     {
18164         return this.formatDate(this.date);
18165     },
18166     
18167     fireKey: function(e)
18168     {
18169         if (!this.picker().isVisible()){
18170             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18171                 this.show();
18172             }
18173             return;
18174         }
18175         
18176         var dateChanged = false,
18177         dir, day, month,
18178         newDate, newViewDate;
18179         
18180         switch(e.keyCode){
18181             case 27: // escape
18182                 this.hide();
18183                 e.preventDefault();
18184                 break;
18185             case 37: // left
18186             case 39: // right
18187                 if (!this.keyboardNavigation) {
18188                     break;
18189                 }
18190                 dir = e.keyCode == 37 ? -1 : 1;
18191                 
18192                 if (e.ctrlKey){
18193                     newDate = this.moveYear(this.date, dir);
18194                     newViewDate = this.moveYear(this.viewDate, dir);
18195                 } else if (e.shiftKey){
18196                     newDate = this.moveMonth(this.date, dir);
18197                     newViewDate = this.moveMonth(this.viewDate, dir);
18198                 } else {
18199                     newDate = new Date(this.date);
18200                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18201                     newViewDate = new Date(this.viewDate);
18202                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18203                 }
18204                 if (this.dateWithinRange(newDate)){
18205                     this.date = newDate;
18206                     this.viewDate = newViewDate;
18207                     this.setValue(this.formatDate(this.date));
18208 //                    this.update();
18209                     e.preventDefault();
18210                     dateChanged = true;
18211                 }
18212                 break;
18213             case 38: // up
18214             case 40: // down
18215                 if (!this.keyboardNavigation) {
18216                     break;
18217                 }
18218                 dir = e.keyCode == 38 ? -1 : 1;
18219                 if (e.ctrlKey){
18220                     newDate = this.moveYear(this.date, dir);
18221                     newViewDate = this.moveYear(this.viewDate, dir);
18222                 } else if (e.shiftKey){
18223                     newDate = this.moveMonth(this.date, dir);
18224                     newViewDate = this.moveMonth(this.viewDate, dir);
18225                 } else {
18226                     newDate = new Date(this.date);
18227                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18228                     newViewDate = new Date(this.viewDate);
18229                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18230                 }
18231                 if (this.dateWithinRange(newDate)){
18232                     this.date = newDate;
18233                     this.viewDate = newViewDate;
18234                     this.setValue(this.formatDate(this.date));
18235 //                    this.update();
18236                     e.preventDefault();
18237                     dateChanged = true;
18238                 }
18239                 break;
18240             case 13: // enter
18241                 this.setValue(this.formatDate(this.date));
18242                 this.hide();
18243                 e.preventDefault();
18244                 break;
18245             case 9: // tab
18246                 this.setValue(this.formatDate(this.date));
18247                 this.hide();
18248                 break;
18249             case 16: // shift
18250             case 17: // ctrl
18251             case 18: // alt
18252                 break;
18253             default :
18254                 this.hide();
18255                 
18256         }
18257     },
18258     
18259     
18260     onClick: function(e) 
18261     {
18262         e.stopPropagation();
18263         e.preventDefault();
18264         
18265         var target = e.getTarget();
18266         
18267         if(target.nodeName.toLowerCase() === 'i'){
18268             target = Roo.get(target).dom.parentNode;
18269         }
18270         
18271         var nodeName = target.nodeName;
18272         var className = target.className;
18273         var html = target.innerHTML;
18274         //Roo.log(nodeName);
18275         
18276         switch(nodeName.toLowerCase()) {
18277             case 'th':
18278                 switch(className) {
18279                     case 'switch':
18280                         this.showMode(1);
18281                         break;
18282                     case 'prev':
18283                     case 'next':
18284                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18285                         switch(this.viewMode){
18286                                 case 0:
18287                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18288                                         break;
18289                                 case 1:
18290                                 case 2:
18291                                         this.viewDate = this.moveYear(this.viewDate, dir);
18292                                         break;
18293                         }
18294                         this.fill();
18295                         break;
18296                     case 'today':
18297                         var date = new Date();
18298                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18299 //                        this.fill()
18300                         this.setValue(this.formatDate(this.date));
18301                         
18302                         this.hide();
18303                         break;
18304                 }
18305                 break;
18306             case 'span':
18307                 if (className.indexOf('disabled') < 0) {
18308                     this.viewDate.setUTCDate(1);
18309                     if (className.indexOf('month') > -1) {
18310                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18311                     } else {
18312                         var year = parseInt(html, 10) || 0;
18313                         this.viewDate.setUTCFullYear(year);
18314                         
18315                     }
18316                     
18317                     if(this.singleMode){
18318                         this.setValue(this.formatDate(this.viewDate));
18319                         this.hide();
18320                         return;
18321                     }
18322                     
18323                     this.showMode(-1);
18324                     this.fill();
18325                 }
18326                 break;
18327                 
18328             case 'td':
18329                 //Roo.log(className);
18330                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18331                     var day = parseInt(html, 10) || 1;
18332                     var year = this.viewDate.getUTCFullYear(),
18333                         month = this.viewDate.getUTCMonth();
18334
18335                     if (className.indexOf('old') > -1) {
18336                         if(month === 0 ){
18337                             month = 11;
18338                             year -= 1;
18339                         }else{
18340                             month -= 1;
18341                         }
18342                     } else if (className.indexOf('new') > -1) {
18343                         if (month == 11) {
18344                             month = 0;
18345                             year += 1;
18346                         } else {
18347                             month += 1;
18348                         }
18349                     }
18350                     //Roo.log([year,month,day]);
18351                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18352                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18353 //                    this.fill();
18354                     //Roo.log(this.formatDate(this.date));
18355                     this.setValue(this.formatDate(this.date));
18356                     this.hide();
18357                 }
18358                 break;
18359         }
18360     },
18361     
18362     setStartDate: function(startDate)
18363     {
18364         this.startDate = startDate || -Infinity;
18365         if (this.startDate !== -Infinity) {
18366             this.startDate = this.parseDate(this.startDate);
18367         }
18368         this.update();
18369         this.updateNavArrows();
18370     },
18371
18372     setEndDate: function(endDate)
18373     {
18374         this.endDate = endDate || Infinity;
18375         if (this.endDate !== Infinity) {
18376             this.endDate = this.parseDate(this.endDate);
18377         }
18378         this.update();
18379         this.updateNavArrows();
18380     },
18381     
18382     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18383     {
18384         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18385         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18386             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18387         }
18388         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18389             return parseInt(d, 10);
18390         });
18391         this.update();
18392         this.updateNavArrows();
18393     },
18394     
18395     updateNavArrows: function() 
18396     {
18397         if(this.singleMode){
18398             return;
18399         }
18400         
18401         var d = new Date(this.viewDate),
18402         year = d.getUTCFullYear(),
18403         month = d.getUTCMonth();
18404         
18405         Roo.each(this.picker().select('.prev', true).elements, function(v){
18406             v.show();
18407             switch (this.viewMode) {
18408                 case 0:
18409
18410                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18411                         v.hide();
18412                     }
18413                     break;
18414                 case 1:
18415                 case 2:
18416                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18417                         v.hide();
18418                     }
18419                     break;
18420             }
18421         });
18422         
18423         Roo.each(this.picker().select('.next', true).elements, function(v){
18424             v.show();
18425             switch (this.viewMode) {
18426                 case 0:
18427
18428                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18429                         v.hide();
18430                     }
18431                     break;
18432                 case 1:
18433                 case 2:
18434                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18435                         v.hide();
18436                     }
18437                     break;
18438             }
18439         })
18440     },
18441     
18442     moveMonth: function(date, dir)
18443     {
18444         if (!dir) {
18445             return date;
18446         }
18447         var new_date = new Date(date.valueOf()),
18448         day = new_date.getUTCDate(),
18449         month = new_date.getUTCMonth(),
18450         mag = Math.abs(dir),
18451         new_month, test;
18452         dir = dir > 0 ? 1 : -1;
18453         if (mag == 1){
18454             test = dir == -1
18455             // If going back one month, make sure month is not current month
18456             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18457             ? function(){
18458                 return new_date.getUTCMonth() == month;
18459             }
18460             // If going forward one month, make sure month is as expected
18461             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18462             : function(){
18463                 return new_date.getUTCMonth() != new_month;
18464             };
18465             new_month = month + dir;
18466             new_date.setUTCMonth(new_month);
18467             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18468             if (new_month < 0 || new_month > 11) {
18469                 new_month = (new_month + 12) % 12;
18470             }
18471         } else {
18472             // For magnitudes >1, move one month at a time...
18473             for (var i=0; i<mag; i++) {
18474                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18475                 new_date = this.moveMonth(new_date, dir);
18476             }
18477             // ...then reset the day, keeping it in the new month
18478             new_month = new_date.getUTCMonth();
18479             new_date.setUTCDate(day);
18480             test = function(){
18481                 return new_month != new_date.getUTCMonth();
18482             };
18483         }
18484         // Common date-resetting loop -- if date is beyond end of month, make it
18485         // end of month
18486         while (test()){
18487             new_date.setUTCDate(--day);
18488             new_date.setUTCMonth(new_month);
18489         }
18490         return new_date;
18491     },
18492
18493     moveYear: function(date, dir)
18494     {
18495         return this.moveMonth(date, dir*12);
18496     },
18497
18498     dateWithinRange: function(date)
18499     {
18500         return date >= this.startDate && date <= this.endDate;
18501     },
18502
18503     
18504     remove: function() 
18505     {
18506         this.picker().remove();
18507     },
18508     
18509     validateValue : function(value)
18510     {
18511         if(value.length < 1)  {
18512             if(this.allowBlank){
18513                 return true;
18514             }
18515             return false;
18516         }
18517         
18518         if(value.length < this.minLength){
18519             return false;
18520         }
18521         if(value.length > this.maxLength){
18522             return false;
18523         }
18524         if(this.vtype){
18525             var vt = Roo.form.VTypes;
18526             if(!vt[this.vtype](value, this)){
18527                 return false;
18528             }
18529         }
18530         if(typeof this.validator == "function"){
18531             var msg = this.validator(value);
18532             if(msg !== true){
18533                 return false;
18534             }
18535         }
18536         
18537         if(this.regex && !this.regex.test(value)){
18538             return false;
18539         }
18540         
18541         if(typeof(this.parseDate(value)) == 'undefined'){
18542             return false;
18543         }
18544         
18545         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
18546             return false;
18547         }      
18548         
18549         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
18550             return false;
18551         } 
18552         
18553         
18554         return true;
18555     }
18556    
18557 });
18558
18559 Roo.apply(Roo.bootstrap.DateField,  {
18560     
18561     head : {
18562         tag: 'thead',
18563         cn: [
18564         {
18565             tag: 'tr',
18566             cn: [
18567             {
18568                 tag: 'th',
18569                 cls: 'prev',
18570                 html: '<i class="fa fa-arrow-left"/>'
18571             },
18572             {
18573                 tag: 'th',
18574                 cls: 'switch',
18575                 colspan: '5'
18576             },
18577             {
18578                 tag: 'th',
18579                 cls: 'next',
18580                 html: '<i class="fa fa-arrow-right"/>'
18581             }
18582
18583             ]
18584         }
18585         ]
18586     },
18587     
18588     content : {
18589         tag: 'tbody',
18590         cn: [
18591         {
18592             tag: 'tr',
18593             cn: [
18594             {
18595                 tag: 'td',
18596                 colspan: '7'
18597             }
18598             ]
18599         }
18600         ]
18601     },
18602     
18603     footer : {
18604         tag: 'tfoot',
18605         cn: [
18606         {
18607             tag: 'tr',
18608             cn: [
18609             {
18610                 tag: 'th',
18611                 colspan: '7',
18612                 cls: 'today'
18613             }
18614                     
18615             ]
18616         }
18617         ]
18618     },
18619     
18620     dates:{
18621         en: {
18622             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
18623             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
18624             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
18625             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18626             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
18627             today: "Today"
18628         }
18629     },
18630     
18631     modes: [
18632     {
18633         clsName: 'days',
18634         navFnc: 'Month',
18635         navStep: 1
18636     },
18637     {
18638         clsName: 'months',
18639         navFnc: 'FullYear',
18640         navStep: 1
18641     },
18642     {
18643         clsName: 'years',
18644         navFnc: 'FullYear',
18645         navStep: 10
18646     }]
18647 });
18648
18649 Roo.apply(Roo.bootstrap.DateField,  {
18650   
18651     template : {
18652         tag: 'div',
18653         cls: 'datepicker dropdown-menu roo-dynamic',
18654         cn: [
18655         {
18656             tag: 'div',
18657             cls: 'datepicker-days',
18658             cn: [
18659             {
18660                 tag: 'table',
18661                 cls: 'table-condensed',
18662                 cn:[
18663                 Roo.bootstrap.DateField.head,
18664                 {
18665                     tag: 'tbody'
18666                 },
18667                 Roo.bootstrap.DateField.footer
18668                 ]
18669             }
18670             ]
18671         },
18672         {
18673             tag: 'div',
18674             cls: 'datepicker-months',
18675             cn: [
18676             {
18677                 tag: 'table',
18678                 cls: 'table-condensed',
18679                 cn:[
18680                 Roo.bootstrap.DateField.head,
18681                 Roo.bootstrap.DateField.content,
18682                 Roo.bootstrap.DateField.footer
18683                 ]
18684             }
18685             ]
18686         },
18687         {
18688             tag: 'div',
18689             cls: 'datepicker-years',
18690             cn: [
18691             {
18692                 tag: 'table',
18693                 cls: 'table-condensed',
18694                 cn:[
18695                 Roo.bootstrap.DateField.head,
18696                 Roo.bootstrap.DateField.content,
18697                 Roo.bootstrap.DateField.footer
18698                 ]
18699             }
18700             ]
18701         }
18702         ]
18703     }
18704 });
18705
18706  
18707
18708  /*
18709  * - LGPL
18710  *
18711  * TimeField
18712  * 
18713  */
18714
18715 /**
18716  * @class Roo.bootstrap.TimeField
18717  * @extends Roo.bootstrap.Input
18718  * Bootstrap DateField class
18719  * 
18720  * 
18721  * @constructor
18722  * Create a new TimeField
18723  * @param {Object} config The config object
18724  */
18725
18726 Roo.bootstrap.TimeField = function(config){
18727     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
18728     this.addEvents({
18729             /**
18730              * @event show
18731              * Fires when this field show.
18732              * @param {Roo.bootstrap.DateField} thisthis
18733              * @param {Mixed} date The date value
18734              */
18735             show : true,
18736             /**
18737              * @event show
18738              * Fires when this field hide.
18739              * @param {Roo.bootstrap.DateField} this
18740              * @param {Mixed} date The date value
18741              */
18742             hide : true,
18743             /**
18744              * @event select
18745              * Fires when select a date.
18746              * @param {Roo.bootstrap.DateField} this
18747              * @param {Mixed} date The date value
18748              */
18749             select : true
18750         });
18751 };
18752
18753 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
18754     
18755     /**
18756      * @cfg {String} format
18757      * The default time format string which can be overriden for localization support.  The format must be
18758      * valid according to {@link Date#parseDate} (defaults to 'H:i').
18759      */
18760     format : "H:i",
18761        
18762     onRender: function(ct, position)
18763     {
18764         
18765         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
18766                 
18767         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
18768         
18769         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18770         
18771         this.pop = this.picker().select('>.datepicker-time',true).first();
18772         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18773         
18774         this.picker().on('mousedown', this.onMousedown, this);
18775         this.picker().on('click', this.onClick, this);
18776         
18777         this.picker().addClass('datepicker-dropdown');
18778     
18779         this.fillTime();
18780         this.update();
18781             
18782         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
18783         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
18784         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
18785         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
18786         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
18787         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
18788
18789     },
18790     
18791     fireKey: function(e){
18792         if (!this.picker().isVisible()){
18793             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18794                 this.show();
18795             }
18796             return;
18797         }
18798
18799         e.preventDefault();
18800         
18801         switch(e.keyCode){
18802             case 27: // escape
18803                 this.hide();
18804                 break;
18805             case 37: // left
18806             case 39: // right
18807                 this.onTogglePeriod();
18808                 break;
18809             case 38: // up
18810                 this.onIncrementMinutes();
18811                 break;
18812             case 40: // down
18813                 this.onDecrementMinutes();
18814                 break;
18815             case 13: // enter
18816             case 9: // tab
18817                 this.setTime();
18818                 break;
18819         }
18820     },
18821     
18822     onClick: function(e) {
18823         e.stopPropagation();
18824         e.preventDefault();
18825     },
18826     
18827     picker : function()
18828     {
18829         return this.el.select('.datepicker', true).first();
18830     },
18831     
18832     fillTime: function()
18833     {    
18834         var time = this.pop.select('tbody', true).first();
18835         
18836         time.dom.innerHTML = '';
18837         
18838         time.createChild({
18839             tag: 'tr',
18840             cn: [
18841                 {
18842                     tag: 'td',
18843                     cn: [
18844                         {
18845                             tag: 'a',
18846                             href: '#',
18847                             cls: 'btn',
18848                             cn: [
18849                                 {
18850                                     tag: 'span',
18851                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
18852                                 }
18853                             ]
18854                         } 
18855                     ]
18856                 },
18857                 {
18858                     tag: 'td',
18859                     cls: 'separator'
18860                 },
18861                 {
18862                     tag: 'td',
18863                     cn: [
18864                         {
18865                             tag: 'a',
18866                             href: '#',
18867                             cls: 'btn',
18868                             cn: [
18869                                 {
18870                                     tag: 'span',
18871                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
18872                                 }
18873                             ]
18874                         }
18875                     ]
18876                 },
18877                 {
18878                     tag: 'td',
18879                     cls: 'separator'
18880                 }
18881             ]
18882         });
18883         
18884         time.createChild({
18885             tag: 'tr',
18886             cn: [
18887                 {
18888                     tag: 'td',
18889                     cn: [
18890                         {
18891                             tag: 'span',
18892                             cls: 'timepicker-hour',
18893                             html: '00'
18894                         }  
18895                     ]
18896                 },
18897                 {
18898                     tag: 'td',
18899                     cls: 'separator',
18900                     html: ':'
18901                 },
18902                 {
18903                     tag: 'td',
18904                     cn: [
18905                         {
18906                             tag: 'span',
18907                             cls: 'timepicker-minute',
18908                             html: '00'
18909                         }  
18910                     ]
18911                 },
18912                 {
18913                     tag: 'td',
18914                     cls: 'separator'
18915                 },
18916                 {
18917                     tag: 'td',
18918                     cn: [
18919                         {
18920                             tag: 'button',
18921                             type: 'button',
18922                             cls: 'btn btn-primary period',
18923                             html: 'AM'
18924                             
18925                         }
18926                     ]
18927                 }
18928             ]
18929         });
18930         
18931         time.createChild({
18932             tag: 'tr',
18933             cn: [
18934                 {
18935                     tag: 'td',
18936                     cn: [
18937                         {
18938                             tag: 'a',
18939                             href: '#',
18940                             cls: 'btn',
18941                             cn: [
18942                                 {
18943                                     tag: 'span',
18944                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
18945                                 }
18946                             ]
18947                         }
18948                     ]
18949                 },
18950                 {
18951                     tag: 'td',
18952                     cls: 'separator'
18953                 },
18954                 {
18955                     tag: 'td',
18956                     cn: [
18957                         {
18958                             tag: 'a',
18959                             href: '#',
18960                             cls: 'btn',
18961                             cn: [
18962                                 {
18963                                     tag: 'span',
18964                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
18965                                 }
18966                             ]
18967                         }
18968                     ]
18969                 },
18970                 {
18971                     tag: 'td',
18972                     cls: 'separator'
18973                 }
18974             ]
18975         });
18976         
18977     },
18978     
18979     update: function()
18980     {
18981         
18982         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18983         
18984         this.fill();
18985     },
18986     
18987     fill: function() 
18988     {
18989         var hours = this.time.getHours();
18990         var minutes = this.time.getMinutes();
18991         var period = 'AM';
18992         
18993         if(hours > 11){
18994             period = 'PM';
18995         }
18996         
18997         if(hours == 0){
18998             hours = 12;
18999         }
19000         
19001         
19002         if(hours > 12){
19003             hours = hours - 12;
19004         }
19005         
19006         if(hours < 10){
19007             hours = '0' + hours;
19008         }
19009         
19010         if(minutes < 10){
19011             minutes = '0' + minutes;
19012         }
19013         
19014         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19015         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19016         this.pop.select('button', true).first().dom.innerHTML = period;
19017         
19018     },
19019     
19020     place: function()
19021     {   
19022         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19023         
19024         var cls = ['bottom'];
19025         
19026         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19027             cls.pop();
19028             cls.push('top');
19029         }
19030         
19031         cls.push('right');
19032         
19033         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19034             cls.pop();
19035             cls.push('left');
19036         }
19037         
19038         this.picker().addClass(cls.join('-'));
19039         
19040         var _this = this;
19041         
19042         Roo.each(cls, function(c){
19043             if(c == 'bottom'){
19044                 _this.picker().setTop(_this.inputEl().getHeight());
19045                 return;
19046             }
19047             if(c == 'top'){
19048                 _this.picker().setTop(0 - _this.picker().getHeight());
19049                 return;
19050             }
19051             
19052             if(c == 'left'){
19053                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19054                 return;
19055             }
19056             if(c == 'right'){
19057                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19058                 return;
19059             }
19060         });
19061         
19062     },
19063   
19064     onFocus : function()
19065     {
19066         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19067         this.show();
19068     },
19069     
19070     onBlur : function()
19071     {
19072         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19073         this.hide();
19074     },
19075     
19076     show : function()
19077     {
19078         this.picker().show();
19079         this.pop.show();
19080         this.update();
19081         this.place();
19082         
19083         this.fireEvent('show', this, this.date);
19084     },
19085     
19086     hide : function()
19087     {
19088         this.picker().hide();
19089         this.pop.hide();
19090         
19091         this.fireEvent('hide', this, this.date);
19092     },
19093     
19094     setTime : function()
19095     {
19096         this.hide();
19097         this.setValue(this.time.format(this.format));
19098         
19099         this.fireEvent('select', this, this.date);
19100         
19101         
19102     },
19103     
19104     onMousedown: function(e){
19105         e.stopPropagation();
19106         e.preventDefault();
19107     },
19108     
19109     onIncrementHours: function()
19110     {
19111         Roo.log('onIncrementHours');
19112         this.time = this.time.add(Date.HOUR, 1);
19113         this.update();
19114         
19115     },
19116     
19117     onDecrementHours: function()
19118     {
19119         Roo.log('onDecrementHours');
19120         this.time = this.time.add(Date.HOUR, -1);
19121         this.update();
19122     },
19123     
19124     onIncrementMinutes: function()
19125     {
19126         Roo.log('onIncrementMinutes');
19127         this.time = this.time.add(Date.MINUTE, 1);
19128         this.update();
19129     },
19130     
19131     onDecrementMinutes: function()
19132     {
19133         Roo.log('onDecrementMinutes');
19134         this.time = this.time.add(Date.MINUTE, -1);
19135         this.update();
19136     },
19137     
19138     onTogglePeriod: function()
19139     {
19140         Roo.log('onTogglePeriod');
19141         this.time = this.time.add(Date.HOUR, 12);
19142         this.update();
19143     }
19144     
19145    
19146 });
19147
19148 Roo.apply(Roo.bootstrap.TimeField,  {
19149     
19150     content : {
19151         tag: 'tbody',
19152         cn: [
19153             {
19154                 tag: 'tr',
19155                 cn: [
19156                 {
19157                     tag: 'td',
19158                     colspan: '7'
19159                 }
19160                 ]
19161             }
19162         ]
19163     },
19164     
19165     footer : {
19166         tag: 'tfoot',
19167         cn: [
19168             {
19169                 tag: 'tr',
19170                 cn: [
19171                 {
19172                     tag: 'th',
19173                     colspan: '7',
19174                     cls: '',
19175                     cn: [
19176                         {
19177                             tag: 'button',
19178                             cls: 'btn btn-info ok',
19179                             html: 'OK'
19180                         }
19181                     ]
19182                 }
19183
19184                 ]
19185             }
19186         ]
19187     }
19188 });
19189
19190 Roo.apply(Roo.bootstrap.TimeField,  {
19191   
19192     template : {
19193         tag: 'div',
19194         cls: 'datepicker dropdown-menu',
19195         cn: [
19196             {
19197                 tag: 'div',
19198                 cls: 'datepicker-time',
19199                 cn: [
19200                 {
19201                     tag: 'table',
19202                     cls: 'table-condensed',
19203                     cn:[
19204                     Roo.bootstrap.TimeField.content,
19205                     Roo.bootstrap.TimeField.footer
19206                     ]
19207                 }
19208                 ]
19209             }
19210         ]
19211     }
19212 });
19213
19214  
19215
19216  /*
19217  * - LGPL
19218  *
19219  * MonthField
19220  * 
19221  */
19222
19223 /**
19224  * @class Roo.bootstrap.MonthField
19225  * @extends Roo.bootstrap.Input
19226  * Bootstrap MonthField class
19227  * 
19228  * @cfg {String} language default en
19229  * 
19230  * @constructor
19231  * Create a new MonthField
19232  * @param {Object} config The config object
19233  */
19234
19235 Roo.bootstrap.MonthField = function(config){
19236     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19237     
19238     this.addEvents({
19239         /**
19240          * @event show
19241          * Fires when this field show.
19242          * @param {Roo.bootstrap.MonthField} this
19243          * @param {Mixed} date The date value
19244          */
19245         show : true,
19246         /**
19247          * @event show
19248          * Fires when this field hide.
19249          * @param {Roo.bootstrap.MonthField} this
19250          * @param {Mixed} date The date value
19251          */
19252         hide : true,
19253         /**
19254          * @event select
19255          * Fires when select a date.
19256          * @param {Roo.bootstrap.MonthField} this
19257          * @param {String} oldvalue The old value
19258          * @param {String} newvalue The new value
19259          */
19260         select : true
19261     });
19262 };
19263
19264 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19265     
19266     onRender: function(ct, position)
19267     {
19268         
19269         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19270         
19271         this.language = this.language || 'en';
19272         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19273         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19274         
19275         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19276         this.isInline = false;
19277         this.isInput = true;
19278         this.component = this.el.select('.add-on', true).first() || false;
19279         this.component = (this.component && this.component.length === 0) ? false : this.component;
19280         this.hasInput = this.component && this.inputEL().length;
19281         
19282         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19283         
19284         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19285         
19286         this.picker().on('mousedown', this.onMousedown, this);
19287         this.picker().on('click', this.onClick, this);
19288         
19289         this.picker().addClass('datepicker-dropdown');
19290         
19291         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19292             v.setStyle('width', '189px');
19293         });
19294         
19295         this.fillMonths();
19296         
19297         this.update();
19298         
19299         if(this.isInline) {
19300             this.show();
19301         }
19302         
19303     },
19304     
19305     setValue: function(v, suppressEvent)
19306     {   
19307         var o = this.getValue();
19308         
19309         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19310         
19311         this.update();
19312
19313         if(suppressEvent !== true){
19314             this.fireEvent('select', this, o, v);
19315         }
19316         
19317     },
19318     
19319     getValue: function()
19320     {
19321         return this.value;
19322     },
19323     
19324     onClick: function(e) 
19325     {
19326         e.stopPropagation();
19327         e.preventDefault();
19328         
19329         var target = e.getTarget();
19330         
19331         if(target.nodeName.toLowerCase() === 'i'){
19332             target = Roo.get(target).dom.parentNode;
19333         }
19334         
19335         var nodeName = target.nodeName;
19336         var className = target.className;
19337         var html = target.innerHTML;
19338         
19339         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19340             return;
19341         }
19342         
19343         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19344         
19345         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19346         
19347         this.hide();
19348                         
19349     },
19350     
19351     picker : function()
19352     {
19353         return this.pickerEl;
19354     },
19355     
19356     fillMonths: function()
19357     {    
19358         var i = 0;
19359         var months = this.picker().select('>.datepicker-months td', true).first();
19360         
19361         months.dom.innerHTML = '';
19362         
19363         while (i < 12) {
19364             var month = {
19365                 tag: 'span',
19366                 cls: 'month',
19367                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19368             };
19369             
19370             months.createChild(month);
19371         }
19372         
19373     },
19374     
19375     update: function()
19376     {
19377         var _this = this;
19378         
19379         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19380             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19381         }
19382         
19383         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19384             e.removeClass('active');
19385             
19386             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19387                 e.addClass('active');
19388             }
19389         })
19390     },
19391     
19392     place: function()
19393     {
19394         if(this.isInline) {
19395             return;
19396         }
19397         
19398         this.picker().removeClass(['bottom', 'top']);
19399         
19400         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19401             /*
19402              * place to the top of element!
19403              *
19404              */
19405             
19406             this.picker().addClass('top');
19407             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19408             
19409             return;
19410         }
19411         
19412         this.picker().addClass('bottom');
19413         
19414         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19415     },
19416     
19417     onFocus : function()
19418     {
19419         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19420         this.show();
19421     },
19422     
19423     onBlur : function()
19424     {
19425         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19426         
19427         var d = this.inputEl().getValue();
19428         
19429         this.setValue(d);
19430                 
19431         this.hide();
19432     },
19433     
19434     show : function()
19435     {
19436         this.picker().show();
19437         this.picker().select('>.datepicker-months', true).first().show();
19438         this.update();
19439         this.place();
19440         
19441         this.fireEvent('show', this, this.date);
19442     },
19443     
19444     hide : function()
19445     {
19446         if(this.isInline) {
19447             return;
19448         }
19449         this.picker().hide();
19450         this.fireEvent('hide', this, this.date);
19451         
19452     },
19453     
19454     onMousedown: function(e)
19455     {
19456         e.stopPropagation();
19457         e.preventDefault();
19458     },
19459     
19460     keyup: function(e)
19461     {
19462         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19463         this.update();
19464     },
19465
19466     fireKey: function(e)
19467     {
19468         if (!this.picker().isVisible()){
19469             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19470                 this.show();
19471             }
19472             return;
19473         }
19474         
19475         var dir;
19476         
19477         switch(e.keyCode){
19478             case 27: // escape
19479                 this.hide();
19480                 e.preventDefault();
19481                 break;
19482             case 37: // left
19483             case 39: // right
19484                 dir = e.keyCode == 37 ? -1 : 1;
19485                 
19486                 this.vIndex = this.vIndex + dir;
19487                 
19488                 if(this.vIndex < 0){
19489                     this.vIndex = 0;
19490                 }
19491                 
19492                 if(this.vIndex > 11){
19493                     this.vIndex = 11;
19494                 }
19495                 
19496                 if(isNaN(this.vIndex)){
19497                     this.vIndex = 0;
19498                 }
19499                 
19500                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19501                 
19502                 break;
19503             case 38: // up
19504             case 40: // down
19505                 
19506                 dir = e.keyCode == 38 ? -1 : 1;
19507                 
19508                 this.vIndex = this.vIndex + dir * 4;
19509                 
19510                 if(this.vIndex < 0){
19511                     this.vIndex = 0;
19512                 }
19513                 
19514                 if(this.vIndex > 11){
19515                     this.vIndex = 11;
19516                 }
19517                 
19518                 if(isNaN(this.vIndex)){
19519                     this.vIndex = 0;
19520                 }
19521                 
19522                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19523                 break;
19524                 
19525             case 13: // enter
19526                 
19527                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19528                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19529                 }
19530                 
19531                 this.hide();
19532                 e.preventDefault();
19533                 break;
19534             case 9: // tab
19535                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19536                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19537                 }
19538                 this.hide();
19539                 break;
19540             case 16: // shift
19541             case 17: // ctrl
19542             case 18: // alt
19543                 break;
19544             default :
19545                 this.hide();
19546                 
19547         }
19548     },
19549     
19550     remove: function() 
19551     {
19552         this.picker().remove();
19553     }
19554    
19555 });
19556
19557 Roo.apply(Roo.bootstrap.MonthField,  {
19558     
19559     content : {
19560         tag: 'tbody',
19561         cn: [
19562         {
19563             tag: 'tr',
19564             cn: [
19565             {
19566                 tag: 'td',
19567                 colspan: '7'
19568             }
19569             ]
19570         }
19571         ]
19572     },
19573     
19574     dates:{
19575         en: {
19576             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19577             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19578         }
19579     }
19580 });
19581
19582 Roo.apply(Roo.bootstrap.MonthField,  {
19583   
19584     template : {
19585         tag: 'div',
19586         cls: 'datepicker dropdown-menu roo-dynamic',
19587         cn: [
19588             {
19589                 tag: 'div',
19590                 cls: 'datepicker-months',
19591                 cn: [
19592                 {
19593                     tag: 'table',
19594                     cls: 'table-condensed',
19595                     cn:[
19596                         Roo.bootstrap.DateField.content
19597                     ]
19598                 }
19599                 ]
19600             }
19601         ]
19602     }
19603 });
19604
19605  
19606
19607  
19608  /*
19609  * - LGPL
19610  *
19611  * CheckBox
19612  * 
19613  */
19614
19615 /**
19616  * @class Roo.bootstrap.CheckBox
19617  * @extends Roo.bootstrap.Input
19618  * Bootstrap CheckBox class
19619  * 
19620  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
19621  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
19622  * @cfg {String} boxLabel The text that appears beside the checkbox
19623  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
19624  * @cfg {Boolean} checked initnal the element
19625  * @cfg {Boolean} inline inline the element (default false)
19626  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
19627  * 
19628  * @constructor
19629  * Create a new CheckBox
19630  * @param {Object} config The config object
19631  */
19632
19633 Roo.bootstrap.CheckBox = function(config){
19634     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
19635    
19636     this.addEvents({
19637         /**
19638         * @event check
19639         * Fires when the element is checked or unchecked.
19640         * @param {Roo.bootstrap.CheckBox} this This input
19641         * @param {Boolean} checked The new checked value
19642         */
19643        check : true
19644     });
19645     
19646 };
19647
19648 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
19649   
19650     inputType: 'checkbox',
19651     inputValue: 1,
19652     valueOff: 0,
19653     boxLabel: false,
19654     checked: false,
19655     weight : false,
19656     inline: false,
19657     
19658     getAutoCreate : function()
19659     {
19660         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19661         
19662         var id = Roo.id();
19663         
19664         var cfg = {};
19665         
19666         cfg.cls = 'form-group ' + this.inputType; //input-group
19667         
19668         if(this.inline){
19669             cfg.cls += ' ' + this.inputType + '-inline';
19670         }
19671         
19672         var input =  {
19673             tag: 'input',
19674             id : id,
19675             type : this.inputType,
19676             value : this.inputValue,
19677             cls : 'roo-' + this.inputType, //'form-box',
19678             placeholder : this.placeholder || ''
19679             
19680         };
19681         
19682         if(this.inputType != 'radio'){
19683             var hidden =  {
19684                 tag: 'input',
19685                 type : 'hidden',
19686                 cls : 'roo-hidden-value',
19687                 value : this.checked ? this.valueOff : this.inputValue
19688             };
19689         }
19690         
19691             
19692         if (this.weight) { // Validity check?
19693             cfg.cls += " " + this.inputType + "-" + this.weight;
19694         }
19695         
19696         if (this.disabled) {
19697             input.disabled=true;
19698         }
19699         
19700         if(this.checked){
19701             input.checked = this.checked;
19702             
19703         }
19704         
19705         
19706         if (this.name) {
19707             
19708             input.name = this.name;
19709             
19710             if(this.inputType != 'radio'){
19711                 hidden.name = this.name;
19712                 input.name = '_hidden_' + this.name;
19713             }
19714         }
19715         
19716         if (this.size) {
19717             input.cls += ' input-' + this.size;
19718         }
19719         
19720         var settings=this;
19721         
19722         ['xs','sm','md','lg'].map(function(size){
19723             if (settings[size]) {
19724                 cfg.cls += ' col-' + size + '-' + settings[size];
19725             }
19726         });
19727         
19728         var inputblock = input;
19729          
19730         if (this.before || this.after) {
19731             
19732             inputblock = {
19733                 cls : 'input-group',
19734                 cn :  [] 
19735             };
19736             
19737             if (this.before) {
19738                 inputblock.cn.push({
19739                     tag :'span',
19740                     cls : 'input-group-addon',
19741                     html : this.before
19742                 });
19743             }
19744             
19745             inputblock.cn.push(input);
19746             
19747             if(this.inputType != 'radio'){
19748                 inputblock.cn.push(hidden);
19749             }
19750             
19751             if (this.after) {
19752                 inputblock.cn.push({
19753                     tag :'span',
19754                     cls : 'input-group-addon',
19755                     html : this.after
19756                 });
19757             }
19758             
19759         }
19760         
19761         if (align ==='left' && this.fieldLabel.length) {
19762 //                Roo.log("left and has label");
19763                 cfg.cn = [
19764                     
19765                     {
19766                         tag: 'label',
19767                         'for' :  id,
19768                         cls : 'control-label col-md-' + this.labelWidth,
19769                         html : this.fieldLabel
19770                         
19771                     },
19772                     {
19773                         cls : "col-md-" + (12 - this.labelWidth), 
19774                         cn: [
19775                             inputblock
19776                         ]
19777                     }
19778                     
19779                 ];
19780         } else if ( this.fieldLabel.length) {
19781 //                Roo.log(" label");
19782                 cfg.cn = [
19783                    
19784                     {
19785                         tag: this.boxLabel ? 'span' : 'label',
19786                         'for': id,
19787                         cls: 'control-label box-input-label',
19788                         //cls : 'input-group-addon',
19789                         html : this.fieldLabel
19790                         
19791                     },
19792                     
19793                     inputblock
19794                     
19795                 ];
19796
19797         } else {
19798             
19799 //                Roo.log(" no label && no align");
19800                 cfg.cn = [  inputblock ] ;
19801                 
19802                 
19803         }
19804         
19805         if(this.boxLabel){
19806              var boxLabelCfg = {
19807                 tag: 'label',
19808                 //'for': id, // box label is handled by onclick - so no for...
19809                 cls: 'box-label',
19810                 html: this.boxLabel
19811             };
19812             
19813             if(this.tooltip){
19814                 boxLabelCfg.tooltip = this.tooltip;
19815             }
19816              
19817             cfg.cn.push(boxLabelCfg);
19818         }
19819         
19820         if(this.inputType != 'radio'){
19821             cfg.cn.push(hidden);
19822         }
19823         
19824         return cfg;
19825         
19826     },
19827     
19828     /**
19829      * return the real input element.
19830      */
19831     inputEl: function ()
19832     {
19833         return this.el.select('input.roo-' + this.inputType,true).first();
19834     },
19835     hiddenEl: function ()
19836     {
19837         return this.el.select('input.roo-hidden-value',true).first();
19838     },
19839     
19840     labelEl: function()
19841     {
19842         return this.el.select('label.control-label',true).first();
19843     },
19844     /* depricated... */
19845     
19846     label: function()
19847     {
19848         return this.labelEl();
19849     },
19850     
19851     boxLabelEl: function()
19852     {
19853         return this.el.select('label.box-label',true).first();
19854     },
19855     
19856     initEvents : function()
19857     {
19858 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19859         
19860         this.inputEl().on('click', this.onClick,  this);
19861         
19862         if (this.boxLabel) { 
19863             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
19864         }
19865         
19866         this.startValue = this.getValue();
19867         
19868         if(this.groupId){
19869             Roo.bootstrap.CheckBox.register(this);
19870         }
19871     },
19872     
19873     onClick : function()
19874     {   
19875         this.setChecked(!this.checked);
19876     },
19877     
19878     setChecked : function(state,suppressEvent)
19879     {
19880         this.startValue = this.getValue();
19881
19882         if(this.inputType == 'radio'){
19883             
19884             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19885                 e.dom.checked = false;
19886             });
19887             
19888             this.inputEl().dom.checked = true;
19889             
19890             this.inputEl().dom.value = this.inputValue;
19891             
19892             if(suppressEvent !== true){
19893                 this.fireEvent('check', this, true);
19894             }
19895             
19896             this.validate();
19897             
19898             return;
19899         }
19900         
19901         this.checked = state;
19902         
19903         this.inputEl().dom.checked = state;
19904         
19905         
19906         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
19907         
19908         if(suppressEvent !== true){
19909             this.fireEvent('check', this, state);
19910         }
19911         
19912         this.validate();
19913     },
19914     
19915     getValue : function()
19916     {
19917         if(this.inputType == 'radio'){
19918             return this.getGroupValue();
19919         }
19920         
19921         return this.hiddenEl().dom.value;
19922         
19923     },
19924     
19925     getGroupValue : function()
19926     {
19927         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19928             return '';
19929         }
19930         
19931         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19932     },
19933     
19934     setValue : function(v,suppressEvent)
19935     {
19936         if(this.inputType == 'radio'){
19937             this.setGroupValue(v, suppressEvent);
19938             return;
19939         }
19940         
19941         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19942         
19943         this.validate();
19944     },
19945     
19946     setGroupValue : function(v, suppressEvent)
19947     {
19948         this.startValue = this.getValue();
19949         
19950         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19951             e.dom.checked = false;
19952             
19953             if(e.dom.value == v){
19954                 e.dom.checked = true;
19955             }
19956         });
19957         
19958         if(suppressEvent !== true){
19959             this.fireEvent('check', this, true);
19960         }
19961
19962         this.validate();
19963         
19964         return;
19965     },
19966     
19967     validate : function()
19968     {
19969         if(
19970                 this.disabled || 
19971                 (this.inputType == 'radio' && this.validateRadio()) ||
19972                 (this.inputType == 'checkbox' && this.validateCheckbox())
19973         ){
19974             this.markValid();
19975             return true;
19976         }
19977         
19978         this.markInvalid();
19979         return false;
19980     },
19981     
19982     validateRadio : function()
19983     {
19984         if(this.allowBlank){
19985             return true;
19986         }
19987         
19988         var valid = false;
19989         
19990         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19991             if(!e.dom.checked){
19992                 return;
19993             }
19994             
19995             valid = true;
19996             
19997             return false;
19998         });
19999         
20000         return valid;
20001     },
20002     
20003     validateCheckbox : function()
20004     {
20005         if(!this.groupId){
20006             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20007         }
20008         
20009         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20010         
20011         if(!group){
20012             return false;
20013         }
20014         
20015         var r = false;
20016         
20017         for(var i in group){
20018             if(r){
20019                 break;
20020             }
20021             
20022             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20023         }
20024         
20025         return r;
20026     },
20027     
20028     /**
20029      * Mark this field as valid
20030      */
20031     markValid : function()
20032     {
20033         var _this = this;
20034         
20035         this.fireEvent('valid', this);
20036         
20037         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20038         
20039         if(this.groupId){
20040             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20041         }
20042         
20043         if(label){
20044             label.markValid();
20045         }
20046
20047         if(this.inputType == 'radio'){
20048             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20049                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20050                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20051             });
20052             
20053             return;
20054         }
20055         
20056         if(!this.groupId){
20057             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20058             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20059             return;
20060         }
20061         
20062         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20063             
20064         if(!group){
20065             return;
20066         }
20067         
20068         for(var i in group){
20069             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20070             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20071         }
20072     },
20073     
20074      /**
20075      * Mark this field as invalid
20076      * @param {String} msg The validation message
20077      */
20078     markInvalid : function(msg)
20079     {
20080         if(this.allowBlank){
20081             return;
20082         }
20083         
20084         var _this = this;
20085         
20086         this.fireEvent('invalid', this, msg);
20087         
20088         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20089         
20090         if(this.groupId){
20091             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20092         }
20093         
20094         if(label){
20095             label.markInvalid();
20096         }
20097             
20098         if(this.inputType == 'radio'){
20099             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20100                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20101                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20102             });
20103             
20104             return;
20105         }
20106         
20107         if(!this.groupId){
20108             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20109             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20110             return;
20111         }
20112         
20113         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20114         
20115         if(!group){
20116             return;
20117         }
20118         
20119         for(var i in group){
20120             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20121             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20122         }
20123         
20124     },
20125     
20126     clearInvalid : function()
20127     {
20128         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20129         
20130         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20131         
20132         if (label) {
20133             label.iconEl.removeClass(label.validClass);
20134             label.iconEl.removeClass(label.invalidClass);
20135         }
20136     },
20137     
20138     disable : function()
20139     {
20140         if(this.inputType != 'radio'){
20141             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20142             return;
20143         }
20144         
20145         var _this = this;
20146         
20147         if(this.rendered){
20148             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20149                 _this.getActionEl().addClass(this.disabledClass);
20150                 e.dom.disabled = true;
20151             });
20152         }
20153         
20154         this.disabled = true;
20155         this.fireEvent("disable", this);
20156         return this;
20157     },
20158
20159     enable : function()
20160     {
20161         if(this.inputType != 'radio'){
20162             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20163             return;
20164         }
20165         
20166         var _this = this;
20167         
20168         if(this.rendered){
20169             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20170                 _this.getActionEl().removeClass(this.disabledClass);
20171                 e.dom.disabled = false;
20172             });
20173         }
20174         
20175         this.disabled = false;
20176         this.fireEvent("enable", this);
20177         return this;
20178     }
20179
20180 });
20181
20182 Roo.apply(Roo.bootstrap.CheckBox, {
20183     
20184     groups: {},
20185     
20186      /**
20187     * register a CheckBox Group
20188     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20189     */
20190     register : function(checkbox)
20191     {
20192         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20193             this.groups[checkbox.groupId] = {};
20194         }
20195         
20196         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20197             return;
20198         }
20199         
20200         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20201         
20202     },
20203     /**
20204     * fetch a CheckBox Group based on the group ID
20205     * @param {string} the group ID
20206     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20207     */
20208     get: function(groupId) {
20209         if (typeof(this.groups[groupId]) == 'undefined') {
20210             return false;
20211         }
20212         
20213         return this.groups[groupId] ;
20214     }
20215     
20216     
20217 });
20218 /*
20219  * - LGPL
20220  *
20221  * RadioItem
20222  * 
20223  */
20224
20225 /**
20226  * @class Roo.bootstrap.Radio
20227  * @extends Roo.bootstrap.Component
20228  * Bootstrap Radio class
20229  * @cfg {String} boxLabel - the label associated
20230  * @cfg {String} value - the value of radio
20231  * 
20232  * @constructor
20233  * Create a new Radio
20234  * @param {Object} config The config object
20235  */
20236 Roo.bootstrap.Radio = function(config){
20237     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20238     
20239 };
20240
20241 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20242     
20243     boxLabel : '',
20244     
20245     value : '',
20246     
20247     getAutoCreate : function()
20248     {
20249         var cfg = {
20250             tag : 'div',
20251             cls : 'form-group radio',
20252             cn : [
20253                 {
20254                     tag : 'label',
20255                     cls : 'box-label',
20256                     html : this.boxLabel
20257                 }
20258             ]
20259         };
20260         
20261         return cfg;
20262     },
20263     
20264     initEvents : function() 
20265     {
20266         this.parent().register(this);
20267         
20268         this.el.on('click', this.onClick, this);
20269         
20270     },
20271     
20272     onClick : function()
20273     {
20274         this.setChecked(true);
20275     },
20276     
20277     setChecked : function(state, suppressEvent)
20278     {
20279         this.parent().setValue(this.value, suppressEvent);
20280         
20281     }
20282     
20283 });
20284  
20285
20286  //<script type="text/javascript">
20287
20288 /*
20289  * Based  Ext JS Library 1.1.1
20290  * Copyright(c) 2006-2007, Ext JS, LLC.
20291  * LGPL
20292  *
20293  */
20294  
20295 /**
20296  * @class Roo.HtmlEditorCore
20297  * @extends Roo.Component
20298  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20299  *
20300  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20301  */
20302
20303 Roo.HtmlEditorCore = function(config){
20304     
20305     
20306     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20307     
20308     
20309     this.addEvents({
20310         /**
20311          * @event initialize
20312          * Fires when the editor is fully initialized (including the iframe)
20313          * @param {Roo.HtmlEditorCore} this
20314          */
20315         initialize: true,
20316         /**
20317          * @event activate
20318          * Fires when the editor is first receives the focus. Any insertion must wait
20319          * until after this event.
20320          * @param {Roo.HtmlEditorCore} this
20321          */
20322         activate: true,
20323          /**
20324          * @event beforesync
20325          * Fires before the textarea is updated with content from the editor iframe. Return false
20326          * to cancel the sync.
20327          * @param {Roo.HtmlEditorCore} this
20328          * @param {String} html
20329          */
20330         beforesync: true,
20331          /**
20332          * @event beforepush
20333          * Fires before the iframe editor is updated with content from the textarea. Return false
20334          * to cancel the push.
20335          * @param {Roo.HtmlEditorCore} this
20336          * @param {String} html
20337          */
20338         beforepush: true,
20339          /**
20340          * @event sync
20341          * Fires when the textarea is updated with content from the editor iframe.
20342          * @param {Roo.HtmlEditorCore} this
20343          * @param {String} html
20344          */
20345         sync: true,
20346          /**
20347          * @event push
20348          * Fires when the iframe editor is updated with content from the textarea.
20349          * @param {Roo.HtmlEditorCore} this
20350          * @param {String} html
20351          */
20352         push: true,
20353         
20354         /**
20355          * @event editorevent
20356          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20357          * @param {Roo.HtmlEditorCore} this
20358          */
20359         editorevent: true
20360         
20361     });
20362     
20363     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20364     
20365     // defaults : white / black...
20366     this.applyBlacklists();
20367     
20368     
20369     
20370 };
20371
20372
20373 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20374
20375
20376      /**
20377      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20378      */
20379     
20380     owner : false,
20381     
20382      /**
20383      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20384      *                        Roo.resizable.
20385      */
20386     resizable : false,
20387      /**
20388      * @cfg {Number} height (in pixels)
20389      */   
20390     height: 300,
20391    /**
20392      * @cfg {Number} width (in pixels)
20393      */   
20394     width: 500,
20395     
20396     /**
20397      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20398      * 
20399      */
20400     stylesheets: false,
20401     
20402     // id of frame..
20403     frameId: false,
20404     
20405     // private properties
20406     validationEvent : false,
20407     deferHeight: true,
20408     initialized : false,
20409     activated : false,
20410     sourceEditMode : false,
20411     onFocus : Roo.emptyFn,
20412     iframePad:3,
20413     hideMode:'offsets',
20414     
20415     clearUp: true,
20416     
20417     // blacklist + whitelisted elements..
20418     black: false,
20419     white: false,
20420      
20421     
20422
20423     /**
20424      * Protected method that will not generally be called directly. It
20425      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20426      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20427      */
20428     getDocMarkup : function(){
20429         // body styles..
20430         var st = '';
20431         
20432         // inherit styels from page...?? 
20433         if (this.stylesheets === false) {
20434             
20435             Roo.get(document.head).select('style').each(function(node) {
20436                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20437             });
20438             
20439             Roo.get(document.head).select('link').each(function(node) { 
20440                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20441             });
20442             
20443         } else if (!this.stylesheets.length) {
20444                 // simple..
20445                 st = '<style type="text/css">' +
20446                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20447                    '</style>';
20448         } else { 
20449             
20450         }
20451         
20452         st +=  '<style type="text/css">' +
20453             'IMG { cursor: pointer } ' +
20454         '</style>';
20455
20456         
20457         return '<html><head>' + st  +
20458             //<style type="text/css">' +
20459             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20460             //'</style>' +
20461             ' </head><body class="roo-htmleditor-body"></body></html>';
20462     },
20463
20464     // private
20465     onRender : function(ct, position)
20466     {
20467         var _t = this;
20468         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20469         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20470         
20471         
20472         this.el.dom.style.border = '0 none';
20473         this.el.dom.setAttribute('tabIndex', -1);
20474         this.el.addClass('x-hidden hide');
20475         
20476         
20477         
20478         if(Roo.isIE){ // fix IE 1px bogus margin
20479             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20480         }
20481        
20482         
20483         this.frameId = Roo.id();
20484         
20485          
20486         
20487         var iframe = this.owner.wrap.createChild({
20488             tag: 'iframe',
20489             cls: 'form-control', // bootstrap..
20490             id: this.frameId,
20491             name: this.frameId,
20492             frameBorder : 'no',
20493             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20494         }, this.el
20495         );
20496         
20497         
20498         this.iframe = iframe.dom;
20499
20500          this.assignDocWin();
20501         
20502         this.doc.designMode = 'on';
20503        
20504         this.doc.open();
20505         this.doc.write(this.getDocMarkup());
20506         this.doc.close();
20507
20508         
20509         var task = { // must defer to wait for browser to be ready
20510             run : function(){
20511                 //console.log("run task?" + this.doc.readyState);
20512                 this.assignDocWin();
20513                 if(this.doc.body || this.doc.readyState == 'complete'){
20514                     try {
20515                         this.doc.designMode="on";
20516                     } catch (e) {
20517                         return;
20518                     }
20519                     Roo.TaskMgr.stop(task);
20520                     this.initEditor.defer(10, this);
20521                 }
20522             },
20523             interval : 10,
20524             duration: 10000,
20525             scope: this
20526         };
20527         Roo.TaskMgr.start(task);
20528
20529     },
20530
20531     // private
20532     onResize : function(w, h)
20533     {
20534          Roo.log('resize: ' +w + ',' + h );
20535         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20536         if(!this.iframe){
20537             return;
20538         }
20539         if(typeof w == 'number'){
20540             
20541             this.iframe.style.width = w + 'px';
20542         }
20543         if(typeof h == 'number'){
20544             
20545             this.iframe.style.height = h + 'px';
20546             if(this.doc){
20547                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20548             }
20549         }
20550         
20551     },
20552
20553     /**
20554      * Toggles the editor between standard and source edit mode.
20555      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20556      */
20557     toggleSourceEdit : function(sourceEditMode){
20558         
20559         this.sourceEditMode = sourceEditMode === true;
20560         
20561         if(this.sourceEditMode){
20562  
20563             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20564             
20565         }else{
20566             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20567             //this.iframe.className = '';
20568             this.deferFocus();
20569         }
20570         //this.setSize(this.owner.wrap.getSize());
20571         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20572     },
20573
20574     
20575   
20576
20577     /**
20578      * Protected method that will not generally be called directly. If you need/want
20579      * custom HTML cleanup, this is the method you should override.
20580      * @param {String} html The HTML to be cleaned
20581      * return {String} The cleaned HTML
20582      */
20583     cleanHtml : function(html){
20584         html = String(html);
20585         if(html.length > 5){
20586             if(Roo.isSafari){ // strip safari nonsense
20587                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20588             }
20589         }
20590         if(html == '&nbsp;'){
20591             html = '';
20592         }
20593         return html;
20594     },
20595
20596     /**
20597      * HTML Editor -> Textarea
20598      * Protected method that will not generally be called directly. Syncs the contents
20599      * of the editor iframe with the textarea.
20600      */
20601     syncValue : function(){
20602         if(this.initialized){
20603             var bd = (this.doc.body || this.doc.documentElement);
20604             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20605             var html = bd.innerHTML;
20606             if(Roo.isSafari){
20607                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20608                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20609                 if(m && m[1]){
20610                     html = '<div style="'+m[0]+'">' + html + '</div>';
20611                 }
20612             }
20613             html = this.cleanHtml(html);
20614             // fix up the special chars.. normaly like back quotes in word...
20615             // however we do not want to do this with chinese..
20616             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20617                 var cc = b.charCodeAt();
20618                 if (
20619                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20620                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20621                     (cc >= 0xf900 && cc < 0xfb00 )
20622                 ) {
20623                         return b;
20624                 }
20625                 return "&#"+cc+";" 
20626             });
20627             if(this.owner.fireEvent('beforesync', this, html) !== false){
20628                 this.el.dom.value = html;
20629                 this.owner.fireEvent('sync', this, html);
20630             }
20631         }
20632     },
20633
20634     /**
20635      * Protected method that will not generally be called directly. Pushes the value of the textarea
20636      * into the iframe editor.
20637      */
20638     pushValue : function(){
20639         if(this.initialized){
20640             var v = this.el.dom.value.trim();
20641             
20642 //            if(v.length < 1){
20643 //                v = '&#160;';
20644 //            }
20645             
20646             if(this.owner.fireEvent('beforepush', this, v) !== false){
20647                 var d = (this.doc.body || this.doc.documentElement);
20648                 d.innerHTML = v;
20649                 this.cleanUpPaste();
20650                 this.el.dom.value = d.innerHTML;
20651                 this.owner.fireEvent('push', this, v);
20652             }
20653         }
20654     },
20655
20656     // private
20657     deferFocus : function(){
20658         this.focus.defer(10, this);
20659     },
20660
20661     // doc'ed in Field
20662     focus : function(){
20663         if(this.win && !this.sourceEditMode){
20664             this.win.focus();
20665         }else{
20666             this.el.focus();
20667         }
20668     },
20669     
20670     assignDocWin: function()
20671     {
20672         var iframe = this.iframe;
20673         
20674          if(Roo.isIE){
20675             this.doc = iframe.contentWindow.document;
20676             this.win = iframe.contentWindow;
20677         } else {
20678 //            if (!Roo.get(this.frameId)) {
20679 //                return;
20680 //            }
20681 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20682 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20683             
20684             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20685                 return;
20686             }
20687             
20688             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20689             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20690         }
20691     },
20692     
20693     // private
20694     initEditor : function(){
20695         //console.log("INIT EDITOR");
20696         this.assignDocWin();
20697         
20698         
20699         
20700         this.doc.designMode="on";
20701         this.doc.open();
20702         this.doc.write(this.getDocMarkup());
20703         this.doc.close();
20704         
20705         var dbody = (this.doc.body || this.doc.documentElement);
20706         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20707         // this copies styles from the containing element into thsi one..
20708         // not sure why we need all of this..
20709         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20710         
20711         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20712         //ss['background-attachment'] = 'fixed'; // w3c
20713         dbody.bgProperties = 'fixed'; // ie
20714         //Roo.DomHelper.applyStyles(dbody, ss);
20715         Roo.EventManager.on(this.doc, {
20716             //'mousedown': this.onEditorEvent,
20717             'mouseup': this.onEditorEvent,
20718             'dblclick': this.onEditorEvent,
20719             'click': this.onEditorEvent,
20720             'keyup': this.onEditorEvent,
20721             buffer:100,
20722             scope: this
20723         });
20724         if(Roo.isGecko){
20725             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20726         }
20727         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20728             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20729         }
20730         this.initialized = true;
20731
20732         this.owner.fireEvent('initialize', this);
20733         this.pushValue();
20734     },
20735
20736     // private
20737     onDestroy : function(){
20738         
20739         
20740         
20741         if(this.rendered){
20742             
20743             //for (var i =0; i < this.toolbars.length;i++) {
20744             //    // fixme - ask toolbars for heights?
20745             //    this.toolbars[i].onDestroy();
20746            // }
20747             
20748             //this.wrap.dom.innerHTML = '';
20749             //this.wrap.remove();
20750         }
20751     },
20752
20753     // private
20754     onFirstFocus : function(){
20755         
20756         this.assignDocWin();
20757         
20758         
20759         this.activated = true;
20760          
20761     
20762         if(Roo.isGecko){ // prevent silly gecko errors
20763             this.win.focus();
20764             var s = this.win.getSelection();
20765             if(!s.focusNode || s.focusNode.nodeType != 3){
20766                 var r = s.getRangeAt(0);
20767                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20768                 r.collapse(true);
20769                 this.deferFocus();
20770             }
20771             try{
20772                 this.execCmd('useCSS', true);
20773                 this.execCmd('styleWithCSS', false);
20774             }catch(e){}
20775         }
20776         this.owner.fireEvent('activate', this);
20777     },
20778
20779     // private
20780     adjustFont: function(btn){
20781         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20782         //if(Roo.isSafari){ // safari
20783         //    adjust *= 2;
20784        // }
20785         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20786         if(Roo.isSafari){ // safari
20787             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20788             v =  (v < 10) ? 10 : v;
20789             v =  (v > 48) ? 48 : v;
20790             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20791             
20792         }
20793         
20794         
20795         v = Math.max(1, v+adjust);
20796         
20797         this.execCmd('FontSize', v  );
20798     },
20799
20800     onEditorEvent : function(e)
20801     {
20802         this.owner.fireEvent('editorevent', this, e);
20803       //  this.updateToolbar();
20804         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20805     },
20806
20807     insertTag : function(tg)
20808     {
20809         // could be a bit smarter... -> wrap the current selected tRoo..
20810         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20811             
20812             range = this.createRange(this.getSelection());
20813             var wrappingNode = this.doc.createElement(tg.toLowerCase());
20814             wrappingNode.appendChild(range.extractContents());
20815             range.insertNode(wrappingNode);
20816
20817             return;
20818             
20819             
20820             
20821         }
20822         this.execCmd("formatblock",   tg);
20823         
20824     },
20825     
20826     insertText : function(txt)
20827     {
20828         
20829         
20830         var range = this.createRange();
20831         range.deleteContents();
20832                //alert(Sender.getAttribute('label'));
20833                
20834         range.insertNode(this.doc.createTextNode(txt));
20835     } ,
20836     
20837      
20838
20839     /**
20840      * Executes a Midas editor command on the editor document and performs necessary focus and
20841      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20842      * @param {String} cmd The Midas command
20843      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20844      */
20845     relayCmd : function(cmd, value){
20846         this.win.focus();
20847         this.execCmd(cmd, value);
20848         this.owner.fireEvent('editorevent', this);
20849         //this.updateToolbar();
20850         this.owner.deferFocus();
20851     },
20852
20853     /**
20854      * Executes a Midas editor command directly on the editor document.
20855      * For visual commands, you should use {@link #relayCmd} instead.
20856      * <b>This should only be called after the editor is initialized.</b>
20857      * @param {String} cmd The Midas command
20858      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20859      */
20860     execCmd : function(cmd, value){
20861         this.doc.execCommand(cmd, false, value === undefined ? null : value);
20862         this.syncValue();
20863     },
20864  
20865  
20866    
20867     /**
20868      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20869      * to insert tRoo.
20870      * @param {String} text | dom node.. 
20871      */
20872     insertAtCursor : function(text)
20873     {
20874         
20875         
20876         
20877         if(!this.activated){
20878             return;
20879         }
20880         /*
20881         if(Roo.isIE){
20882             this.win.focus();
20883             var r = this.doc.selection.createRange();
20884             if(r){
20885                 r.collapse(true);
20886                 r.pasteHTML(text);
20887                 this.syncValue();
20888                 this.deferFocus();
20889             
20890             }
20891             return;
20892         }
20893         */
20894         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20895             this.win.focus();
20896             
20897             
20898             // from jquery ui (MIT licenced)
20899             var range, node;
20900             var win = this.win;
20901             
20902             if (win.getSelection && win.getSelection().getRangeAt) {
20903                 range = win.getSelection().getRangeAt(0);
20904                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20905                 range.insertNode(node);
20906             } else if (win.document.selection && win.document.selection.createRange) {
20907                 // no firefox support
20908                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20909                 win.document.selection.createRange().pasteHTML(txt);
20910             } else {
20911                 // no firefox support
20912                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20913                 this.execCmd('InsertHTML', txt);
20914             } 
20915             
20916             this.syncValue();
20917             
20918             this.deferFocus();
20919         }
20920     },
20921  // private
20922     mozKeyPress : function(e){
20923         if(e.ctrlKey){
20924             var c = e.getCharCode(), cmd;
20925           
20926             if(c > 0){
20927                 c = String.fromCharCode(c).toLowerCase();
20928                 switch(c){
20929                     case 'b':
20930                         cmd = 'bold';
20931                         break;
20932                     case 'i':
20933                         cmd = 'italic';
20934                         break;
20935                     
20936                     case 'u':
20937                         cmd = 'underline';
20938                         break;
20939                     
20940                     case 'v':
20941                         this.cleanUpPaste.defer(100, this);
20942                         return;
20943                         
20944                 }
20945                 if(cmd){
20946                     this.win.focus();
20947                     this.execCmd(cmd);
20948                     this.deferFocus();
20949                     e.preventDefault();
20950                 }
20951                 
20952             }
20953         }
20954     },
20955
20956     // private
20957     fixKeys : function(){ // load time branching for fastest keydown performance
20958         if(Roo.isIE){
20959             return function(e){
20960                 var k = e.getKey(), r;
20961                 if(k == e.TAB){
20962                     e.stopEvent();
20963                     r = this.doc.selection.createRange();
20964                     if(r){
20965                         r.collapse(true);
20966                         r.pasteHTML('&#160;&#160;&#160;&#160;');
20967                         this.deferFocus();
20968                     }
20969                     return;
20970                 }
20971                 
20972                 if(k == e.ENTER){
20973                     r = this.doc.selection.createRange();
20974                     if(r){
20975                         var target = r.parentElement();
20976                         if(!target || target.tagName.toLowerCase() != 'li'){
20977                             e.stopEvent();
20978                             r.pasteHTML('<br />');
20979                             r.collapse(false);
20980                             r.select();
20981                         }
20982                     }
20983                 }
20984                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20985                     this.cleanUpPaste.defer(100, this);
20986                     return;
20987                 }
20988                 
20989                 
20990             };
20991         }else if(Roo.isOpera){
20992             return function(e){
20993                 var k = e.getKey();
20994                 if(k == e.TAB){
20995                     e.stopEvent();
20996                     this.win.focus();
20997                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
20998                     this.deferFocus();
20999                 }
21000                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21001                     this.cleanUpPaste.defer(100, this);
21002                     return;
21003                 }
21004                 
21005             };
21006         }else if(Roo.isSafari){
21007             return function(e){
21008                 var k = e.getKey();
21009                 
21010                 if(k == e.TAB){
21011                     e.stopEvent();
21012                     this.execCmd('InsertText','\t');
21013                     this.deferFocus();
21014                     return;
21015                 }
21016                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21017                     this.cleanUpPaste.defer(100, this);
21018                     return;
21019                 }
21020                 
21021              };
21022         }
21023     }(),
21024     
21025     getAllAncestors: function()
21026     {
21027         var p = this.getSelectedNode();
21028         var a = [];
21029         if (!p) {
21030             a.push(p); // push blank onto stack..
21031             p = this.getParentElement();
21032         }
21033         
21034         
21035         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21036             a.push(p);
21037             p = p.parentNode;
21038         }
21039         a.push(this.doc.body);
21040         return a;
21041     },
21042     lastSel : false,
21043     lastSelNode : false,
21044     
21045     
21046     getSelection : function() 
21047     {
21048         this.assignDocWin();
21049         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21050     },
21051     
21052     getSelectedNode: function() 
21053     {
21054         // this may only work on Gecko!!!
21055         
21056         // should we cache this!!!!
21057         
21058         
21059         
21060          
21061         var range = this.createRange(this.getSelection()).cloneRange();
21062         
21063         if (Roo.isIE) {
21064             var parent = range.parentElement();
21065             while (true) {
21066                 var testRange = range.duplicate();
21067                 testRange.moveToElementText(parent);
21068                 if (testRange.inRange(range)) {
21069                     break;
21070                 }
21071                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21072                     break;
21073                 }
21074                 parent = parent.parentElement;
21075             }
21076             return parent;
21077         }
21078         
21079         // is ancestor a text element.
21080         var ac =  range.commonAncestorContainer;
21081         if (ac.nodeType == 3) {
21082             ac = ac.parentNode;
21083         }
21084         
21085         var ar = ac.childNodes;
21086          
21087         var nodes = [];
21088         var other_nodes = [];
21089         var has_other_nodes = false;
21090         for (var i=0;i<ar.length;i++) {
21091             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21092                 continue;
21093             }
21094             // fullly contained node.
21095             
21096             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21097                 nodes.push(ar[i]);
21098                 continue;
21099             }
21100             
21101             // probably selected..
21102             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21103                 other_nodes.push(ar[i]);
21104                 continue;
21105             }
21106             // outer..
21107             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21108                 continue;
21109             }
21110             
21111             
21112             has_other_nodes = true;
21113         }
21114         if (!nodes.length && other_nodes.length) {
21115             nodes= other_nodes;
21116         }
21117         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21118             return false;
21119         }
21120         
21121         return nodes[0];
21122     },
21123     createRange: function(sel)
21124     {
21125         // this has strange effects when using with 
21126         // top toolbar - not sure if it's a great idea.
21127         //this.editor.contentWindow.focus();
21128         if (typeof sel != "undefined") {
21129             try {
21130                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21131             } catch(e) {
21132                 return this.doc.createRange();
21133             }
21134         } else {
21135             return this.doc.createRange();
21136         }
21137     },
21138     getParentElement: function()
21139     {
21140         
21141         this.assignDocWin();
21142         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21143         
21144         var range = this.createRange(sel);
21145          
21146         try {
21147             var p = range.commonAncestorContainer;
21148             while (p.nodeType == 3) { // text node
21149                 p = p.parentNode;
21150             }
21151             return p;
21152         } catch (e) {
21153             return null;
21154         }
21155     
21156     },
21157     /***
21158      *
21159      * Range intersection.. the hard stuff...
21160      *  '-1' = before
21161      *  '0' = hits..
21162      *  '1' = after.
21163      *         [ -- selected range --- ]
21164      *   [fail]                        [fail]
21165      *
21166      *    basically..
21167      *      if end is before start or  hits it. fail.
21168      *      if start is after end or hits it fail.
21169      *
21170      *   if either hits (but other is outside. - then it's not 
21171      *   
21172      *    
21173      **/
21174     
21175     
21176     // @see http://www.thismuchiknow.co.uk/?p=64.
21177     rangeIntersectsNode : function(range, node)
21178     {
21179         var nodeRange = node.ownerDocument.createRange();
21180         try {
21181             nodeRange.selectNode(node);
21182         } catch (e) {
21183             nodeRange.selectNodeContents(node);
21184         }
21185     
21186         var rangeStartRange = range.cloneRange();
21187         rangeStartRange.collapse(true);
21188     
21189         var rangeEndRange = range.cloneRange();
21190         rangeEndRange.collapse(false);
21191     
21192         var nodeStartRange = nodeRange.cloneRange();
21193         nodeStartRange.collapse(true);
21194     
21195         var nodeEndRange = nodeRange.cloneRange();
21196         nodeEndRange.collapse(false);
21197     
21198         return rangeStartRange.compareBoundaryPoints(
21199                  Range.START_TO_START, nodeEndRange) == -1 &&
21200                rangeEndRange.compareBoundaryPoints(
21201                  Range.START_TO_START, nodeStartRange) == 1;
21202         
21203          
21204     },
21205     rangeCompareNode : function(range, node)
21206     {
21207         var nodeRange = node.ownerDocument.createRange();
21208         try {
21209             nodeRange.selectNode(node);
21210         } catch (e) {
21211             nodeRange.selectNodeContents(node);
21212         }
21213         
21214         
21215         range.collapse(true);
21216     
21217         nodeRange.collapse(true);
21218      
21219         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21220         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21221          
21222         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21223         
21224         var nodeIsBefore   =  ss == 1;
21225         var nodeIsAfter    = ee == -1;
21226         
21227         if (nodeIsBefore && nodeIsAfter) {
21228             return 0; // outer
21229         }
21230         if (!nodeIsBefore && nodeIsAfter) {
21231             return 1; //right trailed.
21232         }
21233         
21234         if (nodeIsBefore && !nodeIsAfter) {
21235             return 2;  // left trailed.
21236         }
21237         // fully contined.
21238         return 3;
21239     },
21240
21241     // private? - in a new class?
21242     cleanUpPaste :  function()
21243     {
21244         // cleans up the whole document..
21245         Roo.log('cleanuppaste');
21246         
21247         this.cleanUpChildren(this.doc.body);
21248         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21249         if (clean != this.doc.body.innerHTML) {
21250             this.doc.body.innerHTML = clean;
21251         }
21252         
21253     },
21254     
21255     cleanWordChars : function(input) {// change the chars to hex code
21256         var he = Roo.HtmlEditorCore;
21257         
21258         var output = input;
21259         Roo.each(he.swapCodes, function(sw) { 
21260             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21261             
21262             output = output.replace(swapper, sw[1]);
21263         });
21264         
21265         return output;
21266     },
21267     
21268     
21269     cleanUpChildren : function (n)
21270     {
21271         if (!n.childNodes.length) {
21272             return;
21273         }
21274         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21275            this.cleanUpChild(n.childNodes[i]);
21276         }
21277     },
21278     
21279     
21280         
21281     
21282     cleanUpChild : function (node)
21283     {
21284         var ed = this;
21285         //console.log(node);
21286         if (node.nodeName == "#text") {
21287             // clean up silly Windows -- stuff?
21288             return; 
21289         }
21290         if (node.nodeName == "#comment") {
21291             node.parentNode.removeChild(node);
21292             // clean up silly Windows -- stuff?
21293             return; 
21294         }
21295         var lcname = node.tagName.toLowerCase();
21296         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21297         // whitelist of tags..
21298         
21299         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21300             // remove node.
21301             node.parentNode.removeChild(node);
21302             return;
21303             
21304         }
21305         
21306         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21307         
21308         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21309         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21310         
21311         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21312         //    remove_keep_children = true;
21313         //}
21314         
21315         if (remove_keep_children) {
21316             this.cleanUpChildren(node);
21317             // inserts everything just before this node...
21318             while (node.childNodes.length) {
21319                 var cn = node.childNodes[0];
21320                 node.removeChild(cn);
21321                 node.parentNode.insertBefore(cn, node);
21322             }
21323             node.parentNode.removeChild(node);
21324             return;
21325         }
21326         
21327         if (!node.attributes || !node.attributes.length) {
21328             this.cleanUpChildren(node);
21329             return;
21330         }
21331         
21332         function cleanAttr(n,v)
21333         {
21334             
21335             if (v.match(/^\./) || v.match(/^\//)) {
21336                 return;
21337             }
21338             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
21339                 return;
21340             }
21341             if (v.match(/^#/)) {
21342                 return;
21343             }
21344 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21345             node.removeAttribute(n);
21346             
21347         }
21348         
21349         var cwhite = this.cwhite;
21350         var cblack = this.cblack;
21351             
21352         function cleanStyle(n,v)
21353         {
21354             if (v.match(/expression/)) { //XSS?? should we even bother..
21355                 node.removeAttribute(n);
21356                 return;
21357             }
21358             
21359             var parts = v.split(/;/);
21360             var clean = [];
21361             
21362             Roo.each(parts, function(p) {
21363                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21364                 if (!p.length) {
21365                     return true;
21366                 }
21367                 var l = p.split(':').shift().replace(/\s+/g,'');
21368                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21369                 
21370                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21371 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21372                     //node.removeAttribute(n);
21373                     return true;
21374                 }
21375                 //Roo.log()
21376                 // only allow 'c whitelisted system attributes'
21377                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21378 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21379                     //node.removeAttribute(n);
21380                     return true;
21381                 }
21382                 
21383                 
21384                  
21385                 
21386                 clean.push(p);
21387                 return true;
21388             });
21389             if (clean.length) { 
21390                 node.setAttribute(n, clean.join(';'));
21391             } else {
21392                 node.removeAttribute(n);
21393             }
21394             
21395         }
21396         
21397         
21398         for (var i = node.attributes.length-1; i > -1 ; i--) {
21399             var a = node.attributes[i];
21400             //console.log(a);
21401             
21402             if (a.name.toLowerCase().substr(0,2)=='on')  {
21403                 node.removeAttribute(a.name);
21404                 continue;
21405             }
21406             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21407                 node.removeAttribute(a.name);
21408                 continue;
21409             }
21410             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21411                 cleanAttr(a.name,a.value); // fixme..
21412                 continue;
21413             }
21414             if (a.name == 'style') {
21415                 cleanStyle(a.name,a.value);
21416                 continue;
21417             }
21418             /// clean up MS crap..
21419             // tecnically this should be a list of valid class'es..
21420             
21421             
21422             if (a.name == 'class') {
21423                 if (a.value.match(/^Mso/)) {
21424                     node.className = '';
21425                 }
21426                 
21427                 if (a.value.match(/body/)) {
21428                     node.className = '';
21429                 }
21430                 continue;
21431             }
21432             
21433             // style cleanup!?
21434             // class cleanup?
21435             
21436         }
21437         
21438         
21439         this.cleanUpChildren(node);
21440         
21441         
21442     },
21443     
21444     /**
21445      * Clean up MS wordisms...
21446      */
21447     cleanWord : function(node)
21448     {
21449         
21450         
21451         if (!node) {
21452             this.cleanWord(this.doc.body);
21453             return;
21454         }
21455         if (node.nodeName == "#text") {
21456             // clean up silly Windows -- stuff?
21457             return; 
21458         }
21459         if (node.nodeName == "#comment") {
21460             node.parentNode.removeChild(node);
21461             // clean up silly Windows -- stuff?
21462             return; 
21463         }
21464         
21465         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21466             node.parentNode.removeChild(node);
21467             return;
21468         }
21469         
21470         // remove - but keep children..
21471         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21472             while (node.childNodes.length) {
21473                 var cn = node.childNodes[0];
21474                 node.removeChild(cn);
21475                 node.parentNode.insertBefore(cn, node);
21476             }
21477             node.parentNode.removeChild(node);
21478             this.iterateChildren(node, this.cleanWord);
21479             return;
21480         }
21481         // clean styles
21482         if (node.className.length) {
21483             
21484             var cn = node.className.split(/\W+/);
21485             var cna = [];
21486             Roo.each(cn, function(cls) {
21487                 if (cls.match(/Mso[a-zA-Z]+/)) {
21488                     return;
21489                 }
21490                 cna.push(cls);
21491             });
21492             node.className = cna.length ? cna.join(' ') : '';
21493             if (!cna.length) {
21494                 node.removeAttribute("class");
21495             }
21496         }
21497         
21498         if (node.hasAttribute("lang")) {
21499             node.removeAttribute("lang");
21500         }
21501         
21502         if (node.hasAttribute("style")) {
21503             
21504             var styles = node.getAttribute("style").split(";");
21505             var nstyle = [];
21506             Roo.each(styles, function(s) {
21507                 if (!s.match(/:/)) {
21508                     return;
21509                 }
21510                 var kv = s.split(":");
21511                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21512                     return;
21513                 }
21514                 // what ever is left... we allow.
21515                 nstyle.push(s);
21516             });
21517             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21518             if (!nstyle.length) {
21519                 node.removeAttribute('style');
21520             }
21521         }
21522         this.iterateChildren(node, this.cleanWord);
21523         
21524         
21525         
21526     },
21527     /**
21528      * iterateChildren of a Node, calling fn each time, using this as the scole..
21529      * @param {DomNode} node node to iterate children of.
21530      * @param {Function} fn method of this class to call on each item.
21531      */
21532     iterateChildren : function(node, fn)
21533     {
21534         if (!node.childNodes.length) {
21535                 return;
21536         }
21537         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21538            fn.call(this, node.childNodes[i])
21539         }
21540     },
21541     
21542     
21543     /**
21544      * cleanTableWidths.
21545      *
21546      * Quite often pasting from word etc.. results in tables with column and widths.
21547      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21548      *
21549      */
21550     cleanTableWidths : function(node)
21551     {
21552          
21553          
21554         if (!node) {
21555             this.cleanTableWidths(this.doc.body);
21556             return;
21557         }
21558         
21559         // ignore list...
21560         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21561             return; 
21562         }
21563         Roo.log(node.tagName);
21564         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21565             this.iterateChildren(node, this.cleanTableWidths);
21566             return;
21567         }
21568         if (node.hasAttribute('width')) {
21569             node.removeAttribute('width');
21570         }
21571         
21572          
21573         if (node.hasAttribute("style")) {
21574             // pretty basic...
21575             
21576             var styles = node.getAttribute("style").split(";");
21577             var nstyle = [];
21578             Roo.each(styles, function(s) {
21579                 if (!s.match(/:/)) {
21580                     return;
21581                 }
21582                 var kv = s.split(":");
21583                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21584                     return;
21585                 }
21586                 // what ever is left... we allow.
21587                 nstyle.push(s);
21588             });
21589             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21590             if (!nstyle.length) {
21591                 node.removeAttribute('style');
21592             }
21593         }
21594         
21595         this.iterateChildren(node, this.cleanTableWidths);
21596         
21597         
21598     },
21599     
21600     
21601     
21602     
21603     domToHTML : function(currentElement, depth, nopadtext) {
21604         
21605         depth = depth || 0;
21606         nopadtext = nopadtext || false;
21607     
21608         if (!currentElement) {
21609             return this.domToHTML(this.doc.body);
21610         }
21611         
21612         //Roo.log(currentElement);
21613         var j;
21614         var allText = false;
21615         var nodeName = currentElement.nodeName;
21616         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21617         
21618         if  (nodeName == '#text') {
21619             
21620             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21621         }
21622         
21623         
21624         var ret = '';
21625         if (nodeName != 'BODY') {
21626              
21627             var i = 0;
21628             // Prints the node tagName, such as <A>, <IMG>, etc
21629             if (tagName) {
21630                 var attr = [];
21631                 for(i = 0; i < currentElement.attributes.length;i++) {
21632                     // quoting?
21633                     var aname = currentElement.attributes.item(i).name;
21634                     if (!currentElement.attributes.item(i).value.length) {
21635                         continue;
21636                     }
21637                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21638                 }
21639                 
21640                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21641             } 
21642             else {
21643                 
21644                 // eack
21645             }
21646         } else {
21647             tagName = false;
21648         }
21649         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21650             return ret;
21651         }
21652         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21653             nopadtext = true;
21654         }
21655         
21656         
21657         // Traverse the tree
21658         i = 0;
21659         var currentElementChild = currentElement.childNodes.item(i);
21660         var allText = true;
21661         var innerHTML  = '';
21662         lastnode = '';
21663         while (currentElementChild) {
21664             // Formatting code (indent the tree so it looks nice on the screen)
21665             var nopad = nopadtext;
21666             if (lastnode == 'SPAN') {
21667                 nopad  = true;
21668             }
21669             // text
21670             if  (currentElementChild.nodeName == '#text') {
21671                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21672                 toadd = nopadtext ? toadd : toadd.trim();
21673                 if (!nopad && toadd.length > 80) {
21674                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21675                 }
21676                 innerHTML  += toadd;
21677                 
21678                 i++;
21679                 currentElementChild = currentElement.childNodes.item(i);
21680                 lastNode = '';
21681                 continue;
21682             }
21683             allText = false;
21684             
21685             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21686                 
21687             // Recursively traverse the tree structure of the child node
21688             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21689             lastnode = currentElementChild.nodeName;
21690             i++;
21691             currentElementChild=currentElement.childNodes.item(i);
21692         }
21693         
21694         ret += innerHTML;
21695         
21696         if (!allText) {
21697                 // The remaining code is mostly for formatting the tree
21698             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21699         }
21700         
21701         
21702         if (tagName) {
21703             ret+= "</"+tagName+">";
21704         }
21705         return ret;
21706         
21707     },
21708         
21709     applyBlacklists : function()
21710     {
21711         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
21712         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
21713         
21714         this.white = [];
21715         this.black = [];
21716         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21717             if (b.indexOf(tag) > -1) {
21718                 return;
21719             }
21720             this.white.push(tag);
21721             
21722         }, this);
21723         
21724         Roo.each(w, function(tag) {
21725             if (b.indexOf(tag) > -1) {
21726                 return;
21727             }
21728             if (this.white.indexOf(tag) > -1) {
21729                 return;
21730             }
21731             this.white.push(tag);
21732             
21733         }, this);
21734         
21735         
21736         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21737             if (w.indexOf(tag) > -1) {
21738                 return;
21739             }
21740             this.black.push(tag);
21741             
21742         }, this);
21743         
21744         Roo.each(b, function(tag) {
21745             if (w.indexOf(tag) > -1) {
21746                 return;
21747             }
21748             if (this.black.indexOf(tag) > -1) {
21749                 return;
21750             }
21751             this.black.push(tag);
21752             
21753         }, this);
21754         
21755         
21756         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
21757         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
21758         
21759         this.cwhite = [];
21760         this.cblack = [];
21761         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21762             if (b.indexOf(tag) > -1) {
21763                 return;
21764             }
21765             this.cwhite.push(tag);
21766             
21767         }, this);
21768         
21769         Roo.each(w, function(tag) {
21770             if (b.indexOf(tag) > -1) {
21771                 return;
21772             }
21773             if (this.cwhite.indexOf(tag) > -1) {
21774                 return;
21775             }
21776             this.cwhite.push(tag);
21777             
21778         }, this);
21779         
21780         
21781         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21782             if (w.indexOf(tag) > -1) {
21783                 return;
21784             }
21785             this.cblack.push(tag);
21786             
21787         }, this);
21788         
21789         Roo.each(b, function(tag) {
21790             if (w.indexOf(tag) > -1) {
21791                 return;
21792             }
21793             if (this.cblack.indexOf(tag) > -1) {
21794                 return;
21795             }
21796             this.cblack.push(tag);
21797             
21798         }, this);
21799     },
21800     
21801     setStylesheets : function(stylesheets)
21802     {
21803         if(typeof(stylesheets) == 'string'){
21804             Roo.get(this.iframe.contentDocument.head).createChild({
21805                 tag : 'link',
21806                 rel : 'stylesheet',
21807                 type : 'text/css',
21808                 href : stylesheets
21809             });
21810             
21811             return;
21812         }
21813         var _this = this;
21814      
21815         Roo.each(stylesheets, function(s) {
21816             if(!s.length){
21817                 return;
21818             }
21819             
21820             Roo.get(_this.iframe.contentDocument.head).createChild({
21821                 tag : 'link',
21822                 rel : 'stylesheet',
21823                 type : 'text/css',
21824                 href : s
21825             });
21826         });
21827
21828         
21829     },
21830     
21831     removeStylesheets : function()
21832     {
21833         var _this = this;
21834         
21835         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21836             s.remove();
21837         });
21838     }
21839     
21840     // hide stuff that is not compatible
21841     /**
21842      * @event blur
21843      * @hide
21844      */
21845     /**
21846      * @event change
21847      * @hide
21848      */
21849     /**
21850      * @event focus
21851      * @hide
21852      */
21853     /**
21854      * @event specialkey
21855      * @hide
21856      */
21857     /**
21858      * @cfg {String} fieldClass @hide
21859      */
21860     /**
21861      * @cfg {String} focusClass @hide
21862      */
21863     /**
21864      * @cfg {String} autoCreate @hide
21865      */
21866     /**
21867      * @cfg {String} inputType @hide
21868      */
21869     /**
21870      * @cfg {String} invalidClass @hide
21871      */
21872     /**
21873      * @cfg {String} invalidText @hide
21874      */
21875     /**
21876      * @cfg {String} msgFx @hide
21877      */
21878     /**
21879      * @cfg {String} validateOnBlur @hide
21880      */
21881 });
21882
21883 Roo.HtmlEditorCore.white = [
21884         'area', 'br', 'img', 'input', 'hr', 'wbr',
21885         
21886        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
21887        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
21888        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
21889        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
21890        'table',   'ul',         'xmp', 
21891        
21892        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
21893       'thead',   'tr', 
21894      
21895       'dir', 'menu', 'ol', 'ul', 'dl',
21896        
21897       'embed',  'object'
21898 ];
21899
21900
21901 Roo.HtmlEditorCore.black = [
21902     //    'embed',  'object', // enable - backend responsiblity to clean thiese
21903         'applet', // 
21904         'base',   'basefont', 'bgsound', 'blink',  'body', 
21905         'frame',  'frameset', 'head',    'html',   'ilayer', 
21906         'iframe', 'layer',  'link',     'meta',    'object',   
21907         'script', 'style' ,'title',  'xml' // clean later..
21908 ];
21909 Roo.HtmlEditorCore.clean = [
21910     'script', 'style', 'title', 'xml'
21911 ];
21912 Roo.HtmlEditorCore.remove = [
21913     'font'
21914 ];
21915 // attributes..
21916
21917 Roo.HtmlEditorCore.ablack = [
21918     'on'
21919 ];
21920     
21921 Roo.HtmlEditorCore.aclean = [ 
21922     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
21923 ];
21924
21925 // protocols..
21926 Roo.HtmlEditorCore.pwhite= [
21927         'http',  'https',  'mailto'
21928 ];
21929
21930 // white listed style attributes.
21931 Roo.HtmlEditorCore.cwhite= [
21932       //  'text-align', /// default is to allow most things..
21933       
21934          
21935 //        'font-size'//??
21936 ];
21937
21938 // black listed style attributes.
21939 Roo.HtmlEditorCore.cblack= [
21940       //  'font-size' -- this can be set by the project 
21941 ];
21942
21943
21944 Roo.HtmlEditorCore.swapCodes   =[ 
21945     [    8211, "--" ], 
21946     [    8212, "--" ], 
21947     [    8216,  "'" ],  
21948     [    8217, "'" ],  
21949     [    8220, '"' ],  
21950     [    8221, '"' ],  
21951     [    8226, "*" ],  
21952     [    8230, "..." ]
21953 ]; 
21954
21955     /*
21956  * - LGPL
21957  *
21958  * HtmlEditor
21959  * 
21960  */
21961
21962 /**
21963  * @class Roo.bootstrap.HtmlEditor
21964  * @extends Roo.bootstrap.TextArea
21965  * Bootstrap HtmlEditor class
21966
21967  * @constructor
21968  * Create a new HtmlEditor
21969  * @param {Object} config The config object
21970  */
21971
21972 Roo.bootstrap.HtmlEditor = function(config){
21973     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
21974     if (!this.toolbars) {
21975         this.toolbars = [];
21976     }
21977     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
21978     this.addEvents({
21979             /**
21980              * @event initialize
21981              * Fires when the editor is fully initialized (including the iframe)
21982              * @param {HtmlEditor} this
21983              */
21984             initialize: true,
21985             /**
21986              * @event activate
21987              * Fires when the editor is first receives the focus. Any insertion must wait
21988              * until after this event.
21989              * @param {HtmlEditor} this
21990              */
21991             activate: true,
21992              /**
21993              * @event beforesync
21994              * Fires before the textarea is updated with content from the editor iframe. Return false
21995              * to cancel the sync.
21996              * @param {HtmlEditor} this
21997              * @param {String} html
21998              */
21999             beforesync: true,
22000              /**
22001              * @event beforepush
22002              * Fires before the iframe editor is updated with content from the textarea. Return false
22003              * to cancel the push.
22004              * @param {HtmlEditor} this
22005              * @param {String} html
22006              */
22007             beforepush: true,
22008              /**
22009              * @event sync
22010              * Fires when the textarea is updated with content from the editor iframe.
22011              * @param {HtmlEditor} this
22012              * @param {String} html
22013              */
22014             sync: true,
22015              /**
22016              * @event push
22017              * Fires when the iframe editor is updated with content from the textarea.
22018              * @param {HtmlEditor} this
22019              * @param {String} html
22020              */
22021             push: true,
22022              /**
22023              * @event editmodechange
22024              * Fires when the editor switches edit modes
22025              * @param {HtmlEditor} this
22026              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22027              */
22028             editmodechange: true,
22029             /**
22030              * @event editorevent
22031              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22032              * @param {HtmlEditor} this
22033              */
22034             editorevent: true,
22035             /**
22036              * @event firstfocus
22037              * Fires when on first focus - needed by toolbars..
22038              * @param {HtmlEditor} this
22039              */
22040             firstfocus: true,
22041             /**
22042              * @event autosave
22043              * Auto save the htmlEditor value as a file into Events
22044              * @param {HtmlEditor} this
22045              */
22046             autosave: true,
22047             /**
22048              * @event savedpreview
22049              * preview the saved version of htmlEditor
22050              * @param {HtmlEditor} this
22051              */
22052             savedpreview: true
22053         });
22054 };
22055
22056
22057 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
22058     
22059     
22060       /**
22061      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22062      */
22063     toolbars : false,
22064    
22065      /**
22066      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22067      *                        Roo.resizable.
22068      */
22069     resizable : false,
22070      /**
22071      * @cfg {Number} height (in pixels)
22072      */   
22073     height: 300,
22074    /**
22075      * @cfg {Number} width (in pixels)
22076      */   
22077     width: false,
22078     
22079     /**
22080      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22081      * 
22082      */
22083     stylesheets: false,
22084     
22085     // id of frame..
22086     frameId: false,
22087     
22088     // private properties
22089     validationEvent : false,
22090     deferHeight: true,
22091     initialized : false,
22092     activated : false,
22093     
22094     onFocus : Roo.emptyFn,
22095     iframePad:3,
22096     hideMode:'offsets',
22097     
22098     
22099     tbContainer : false,
22100     
22101     toolbarContainer :function() {
22102         return this.wrap.select('.x-html-editor-tb',true).first();
22103     },
22104
22105     /**
22106      * Protected method that will not generally be called directly. It
22107      * is called when the editor creates its toolbar. Override this method if you need to
22108      * add custom toolbar buttons.
22109      * @param {HtmlEditor} editor
22110      */
22111     createToolbar : function(){
22112         
22113         Roo.log("create toolbars");
22114         
22115         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
22116         this.toolbars[0].render(this.toolbarContainer());
22117         
22118         return;
22119         
22120 //        if (!editor.toolbars || !editor.toolbars.length) {
22121 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
22122 //        }
22123 //        
22124 //        for (var i =0 ; i < editor.toolbars.length;i++) {
22125 //            editor.toolbars[i] = Roo.factory(
22126 //                    typeof(editor.toolbars[i]) == 'string' ?
22127 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
22128 //                Roo.bootstrap.HtmlEditor);
22129 //            editor.toolbars[i].init(editor);
22130 //        }
22131     },
22132
22133      
22134     // private
22135     onRender : function(ct, position)
22136     {
22137        // Roo.log("Call onRender: " + this.xtype);
22138         var _t = this;
22139         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
22140       
22141         this.wrap = this.inputEl().wrap({
22142             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22143         });
22144         
22145         this.editorcore.onRender(ct, position);
22146          
22147         if (this.resizable) {
22148             this.resizeEl = new Roo.Resizable(this.wrap, {
22149                 pinned : true,
22150                 wrap: true,
22151                 dynamic : true,
22152                 minHeight : this.height,
22153                 height: this.height,
22154                 handles : this.resizable,
22155                 width: this.width,
22156                 listeners : {
22157                     resize : function(r, w, h) {
22158                         _t.onResize(w,h); // -something
22159                     }
22160                 }
22161             });
22162             
22163         }
22164         this.createToolbar(this);
22165        
22166         
22167         if(!this.width && this.resizable){
22168             this.setSize(this.wrap.getSize());
22169         }
22170         if (this.resizeEl) {
22171             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22172             // should trigger onReize..
22173         }
22174         
22175     },
22176
22177     // private
22178     onResize : function(w, h)
22179     {
22180         Roo.log('resize: ' +w + ',' + h );
22181         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
22182         var ew = false;
22183         var eh = false;
22184         
22185         if(this.inputEl() ){
22186             if(typeof w == 'number'){
22187                 var aw = w - this.wrap.getFrameWidth('lr');
22188                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
22189                 ew = aw;
22190             }
22191             if(typeof h == 'number'){
22192                  var tbh = -11;  // fixme it needs to tool bar size!
22193                 for (var i =0; i < this.toolbars.length;i++) {
22194                     // fixme - ask toolbars for heights?
22195                     tbh += this.toolbars[i].el.getHeight();
22196                     //if (this.toolbars[i].footer) {
22197                     //    tbh += this.toolbars[i].footer.el.getHeight();
22198                     //}
22199                 }
22200               
22201                 
22202                 
22203                 
22204                 
22205                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22206                 ah -= 5; // knock a few pixes off for look..
22207                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
22208                 var eh = ah;
22209             }
22210         }
22211         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22212         this.editorcore.onResize(ew,eh);
22213         
22214     },
22215
22216     /**
22217      * Toggles the editor between standard and source edit mode.
22218      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22219      */
22220     toggleSourceEdit : function(sourceEditMode)
22221     {
22222         this.editorcore.toggleSourceEdit(sourceEditMode);
22223         
22224         if(this.editorcore.sourceEditMode){
22225             Roo.log('editor - showing textarea');
22226             
22227 //            Roo.log('in');
22228 //            Roo.log(this.syncValue());
22229             this.syncValue();
22230             this.inputEl().removeClass(['hide', 'x-hidden']);
22231             this.inputEl().dom.removeAttribute('tabIndex');
22232             this.inputEl().focus();
22233         }else{
22234             Roo.log('editor - hiding textarea');
22235 //            Roo.log('out')
22236 //            Roo.log(this.pushValue()); 
22237             this.pushValue();
22238             
22239             this.inputEl().addClass(['hide', 'x-hidden']);
22240             this.inputEl().dom.setAttribute('tabIndex', -1);
22241             //this.deferFocus();
22242         }
22243          
22244         if(this.resizable){
22245             this.setSize(this.wrap.getSize());
22246         }
22247         
22248         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22249     },
22250  
22251     // private (for BoxComponent)
22252     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22253
22254     // private (for BoxComponent)
22255     getResizeEl : function(){
22256         return this.wrap;
22257     },
22258
22259     // private (for BoxComponent)
22260     getPositionEl : function(){
22261         return this.wrap;
22262     },
22263
22264     // private
22265     initEvents : function(){
22266         this.originalValue = this.getValue();
22267     },
22268
22269 //    /**
22270 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22271 //     * @method
22272 //     */
22273 //    markInvalid : Roo.emptyFn,
22274 //    /**
22275 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22276 //     * @method
22277 //     */
22278 //    clearInvalid : Roo.emptyFn,
22279
22280     setValue : function(v){
22281         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
22282         this.editorcore.pushValue();
22283     },
22284
22285      
22286     // private
22287     deferFocus : function(){
22288         this.focus.defer(10, this);
22289     },
22290
22291     // doc'ed in Field
22292     focus : function(){
22293         this.editorcore.focus();
22294         
22295     },
22296       
22297
22298     // private
22299     onDestroy : function(){
22300         
22301         
22302         
22303         if(this.rendered){
22304             
22305             for (var i =0; i < this.toolbars.length;i++) {
22306                 // fixme - ask toolbars for heights?
22307                 this.toolbars[i].onDestroy();
22308             }
22309             
22310             this.wrap.dom.innerHTML = '';
22311             this.wrap.remove();
22312         }
22313     },
22314
22315     // private
22316     onFirstFocus : function(){
22317         //Roo.log("onFirstFocus");
22318         this.editorcore.onFirstFocus();
22319          for (var i =0; i < this.toolbars.length;i++) {
22320             this.toolbars[i].onFirstFocus();
22321         }
22322         
22323     },
22324     
22325     // private
22326     syncValue : function()
22327     {   
22328         this.editorcore.syncValue();
22329     },
22330     
22331     pushValue : function()
22332     {   
22333         this.editorcore.pushValue();
22334     }
22335      
22336     
22337     // hide stuff that is not compatible
22338     /**
22339      * @event blur
22340      * @hide
22341      */
22342     /**
22343      * @event change
22344      * @hide
22345      */
22346     /**
22347      * @event focus
22348      * @hide
22349      */
22350     /**
22351      * @event specialkey
22352      * @hide
22353      */
22354     /**
22355      * @cfg {String} fieldClass @hide
22356      */
22357     /**
22358      * @cfg {String} focusClass @hide
22359      */
22360     /**
22361      * @cfg {String} autoCreate @hide
22362      */
22363     /**
22364      * @cfg {String} inputType @hide
22365      */
22366     /**
22367      * @cfg {String} invalidClass @hide
22368      */
22369     /**
22370      * @cfg {String} invalidText @hide
22371      */
22372     /**
22373      * @cfg {String} msgFx @hide
22374      */
22375     /**
22376      * @cfg {String} validateOnBlur @hide
22377      */
22378 });
22379  
22380     
22381    
22382    
22383    
22384       
22385 Roo.namespace('Roo.bootstrap.htmleditor');
22386 /**
22387  * @class Roo.bootstrap.HtmlEditorToolbar1
22388  * Basic Toolbar
22389  * 
22390  * Usage:
22391  *
22392  new Roo.bootstrap.HtmlEditor({
22393     ....
22394     toolbars : [
22395         new Roo.bootstrap.HtmlEditorToolbar1({
22396             disable : { fonts: 1 , format: 1, ..., ... , ...],
22397             btns : [ .... ]
22398         })
22399     }
22400      
22401  * 
22402  * @cfg {Object} disable List of elements to disable..
22403  * @cfg {Array} btns List of additional buttons.
22404  * 
22405  * 
22406  * NEEDS Extra CSS? 
22407  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22408  */
22409  
22410 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
22411 {
22412     
22413     Roo.apply(this, config);
22414     
22415     // default disabled, based on 'good practice'..
22416     this.disable = this.disable || {};
22417     Roo.applyIf(this.disable, {
22418         fontSize : true,
22419         colors : true,
22420         specialElements : true
22421     });
22422     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
22423     
22424     this.editor = config.editor;
22425     this.editorcore = config.editor.editorcore;
22426     
22427     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
22428     
22429     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22430     // dont call parent... till later.
22431 }
22432 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
22433      
22434     bar : true,
22435     
22436     editor : false,
22437     editorcore : false,
22438     
22439     
22440     formats : [
22441         "p" ,  
22442         "h1","h2","h3","h4","h5","h6", 
22443         "pre", "code", 
22444         "abbr", "acronym", "address", "cite", "samp", "var",
22445         'div','span'
22446     ],
22447     
22448     onRender : function(ct, position)
22449     {
22450        // Roo.log("Call onRender: " + this.xtype);
22451         
22452        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
22453        Roo.log(this.el);
22454        this.el.dom.style.marginBottom = '0';
22455        var _this = this;
22456        var editorcore = this.editorcore;
22457        var editor= this.editor;
22458        
22459        var children = [];
22460        var btn = function(id,cmd , toggle, handler){
22461        
22462             var  event = toggle ? 'toggle' : 'click';
22463        
22464             var a = {
22465                 size : 'sm',
22466                 xtype: 'Button',
22467                 xns: Roo.bootstrap,
22468                 glyphicon : id,
22469                 cmd : id || cmd,
22470                 enableToggle:toggle !== false,
22471                 //html : 'submit'
22472                 pressed : toggle ? false : null,
22473                 listeners : {}
22474             };
22475             a.listeners[toggle ? 'toggle' : 'click'] = function() {
22476                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
22477             };
22478             children.push(a);
22479             return a;
22480        }
22481         
22482         var style = {
22483                 xtype: 'Button',
22484                 size : 'sm',
22485                 xns: Roo.bootstrap,
22486                 glyphicon : 'font',
22487                 //html : 'submit'
22488                 menu : {
22489                     xtype: 'Menu',
22490                     xns: Roo.bootstrap,
22491                     items:  []
22492                 }
22493         };
22494         Roo.each(this.formats, function(f) {
22495             style.menu.items.push({
22496                 xtype :'MenuItem',
22497                 xns: Roo.bootstrap,
22498                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
22499                 tagname : f,
22500                 listeners : {
22501                     click : function()
22502                     {
22503                         editorcore.insertTag(this.tagname);
22504                         editor.focus();
22505                     }
22506                 }
22507                 
22508             });
22509         });
22510          children.push(style);   
22511             
22512             
22513         btn('bold',false,true);
22514         btn('italic',false,true);
22515         btn('align-left', 'justifyleft',true);
22516         btn('align-center', 'justifycenter',true);
22517         btn('align-right' , 'justifyright',true);
22518         btn('link', false, false, function(btn) {
22519             //Roo.log("create link?");
22520             var url = prompt(this.createLinkText, this.defaultLinkValue);
22521             if(url && url != 'http:/'+'/'){
22522                 this.editorcore.relayCmd('createlink', url);
22523             }
22524         }),
22525         btn('list','insertunorderedlist',true);
22526         btn('pencil', false,true, function(btn){
22527                 Roo.log(this);
22528                 
22529                 this.toggleSourceEdit(btn.pressed);
22530         });
22531         /*
22532         var cog = {
22533                 xtype: 'Button',
22534                 size : 'sm',
22535                 xns: Roo.bootstrap,
22536                 glyphicon : 'cog',
22537                 //html : 'submit'
22538                 menu : {
22539                     xtype: 'Menu',
22540                     xns: Roo.bootstrap,
22541                     items:  []
22542                 }
22543         };
22544         
22545         cog.menu.items.push({
22546             xtype :'MenuItem',
22547             xns: Roo.bootstrap,
22548             html : Clean styles,
22549             tagname : f,
22550             listeners : {
22551                 click : function()
22552                 {
22553                     editorcore.insertTag(this.tagname);
22554                     editor.focus();
22555                 }
22556             }
22557             
22558         });
22559        */
22560         
22561          
22562        this.xtype = 'NavSimplebar';
22563         
22564         for(var i=0;i< children.length;i++) {
22565             
22566             this.buttons.add(this.addxtypeChild(children[i]));
22567             
22568         }
22569         
22570         editor.on('editorevent', this.updateToolbar, this);
22571     },
22572     onBtnClick : function(id)
22573     {
22574        this.editorcore.relayCmd(id);
22575        this.editorcore.focus();
22576     },
22577     
22578     /**
22579      * Protected method that will not generally be called directly. It triggers
22580      * a toolbar update by reading the markup state of the current selection in the editor.
22581      */
22582     updateToolbar: function(){
22583
22584         if(!this.editorcore.activated){
22585             this.editor.onFirstFocus(); // is this neeed?
22586             return;
22587         }
22588
22589         var btns = this.buttons; 
22590         var doc = this.editorcore.doc;
22591         btns.get('bold').setActive(doc.queryCommandState('bold'));
22592         btns.get('italic').setActive(doc.queryCommandState('italic'));
22593         //btns.get('underline').setActive(doc.queryCommandState('underline'));
22594         
22595         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
22596         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
22597         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
22598         
22599         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
22600         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
22601          /*
22602         
22603         var ans = this.editorcore.getAllAncestors();
22604         if (this.formatCombo) {
22605             
22606             
22607             var store = this.formatCombo.store;
22608             this.formatCombo.setValue("");
22609             for (var i =0; i < ans.length;i++) {
22610                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
22611                     // select it..
22612                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
22613                     break;
22614                 }
22615             }
22616         }
22617         
22618         
22619         
22620         // hides menus... - so this cant be on a menu...
22621         Roo.bootstrap.MenuMgr.hideAll();
22622         */
22623         Roo.bootstrap.MenuMgr.hideAll();
22624         //this.editorsyncValue();
22625     },
22626     onFirstFocus: function() {
22627         this.buttons.each(function(item){
22628            item.enable();
22629         });
22630     },
22631     toggleSourceEdit : function(sourceEditMode){
22632         
22633           
22634         if(sourceEditMode){
22635             Roo.log("disabling buttons");
22636            this.buttons.each( function(item){
22637                 if(item.cmd != 'pencil'){
22638                     item.disable();
22639                 }
22640             });
22641           
22642         }else{
22643             Roo.log("enabling buttons");
22644             if(this.editorcore.initialized){
22645                 this.buttons.each( function(item){
22646                     item.enable();
22647                 });
22648             }
22649             
22650         }
22651         Roo.log("calling toggole on editor");
22652         // tell the editor that it's been pressed..
22653         this.editor.toggleSourceEdit(sourceEditMode);
22654        
22655     }
22656 });
22657
22658
22659
22660
22661
22662 /**
22663  * @class Roo.bootstrap.Table.AbstractSelectionModel
22664  * @extends Roo.util.Observable
22665  * Abstract base class for grid SelectionModels.  It provides the interface that should be
22666  * implemented by descendant classes.  This class should not be directly instantiated.
22667  * @constructor
22668  */
22669 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22670     this.locked = false;
22671     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22672 };
22673
22674
22675 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
22676     /** @ignore Called by the grid automatically. Do not call directly. */
22677     init : function(grid){
22678         this.grid = grid;
22679         this.initEvents();
22680     },
22681
22682     /**
22683      * Locks the selections.
22684      */
22685     lock : function(){
22686         this.locked = true;
22687     },
22688
22689     /**
22690      * Unlocks the selections.
22691      */
22692     unlock : function(){
22693         this.locked = false;
22694     },
22695
22696     /**
22697      * Returns true if the selections are locked.
22698      * @return {Boolean}
22699      */
22700     isLocked : function(){
22701         return this.locked;
22702     }
22703 });
22704 /**
22705  * @extends Roo.bootstrap.Table.AbstractSelectionModel
22706  * @class Roo.bootstrap.Table.RowSelectionModel
22707  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22708  * It supports multiple selections and keyboard selection/navigation. 
22709  * @constructor
22710  * @param {Object} config
22711  */
22712
22713 Roo.bootstrap.Table.RowSelectionModel = function(config){
22714     Roo.apply(this, config);
22715     this.selections = new Roo.util.MixedCollection(false, function(o){
22716         return o.id;
22717     });
22718
22719     this.last = false;
22720     this.lastActive = false;
22721
22722     this.addEvents({
22723         /**
22724              * @event selectionchange
22725              * Fires when the selection changes
22726              * @param {SelectionModel} this
22727              */
22728             "selectionchange" : true,
22729         /**
22730              * @event afterselectionchange
22731              * Fires after the selection changes (eg. by key press or clicking)
22732              * @param {SelectionModel} this
22733              */
22734             "afterselectionchange" : true,
22735         /**
22736              * @event beforerowselect
22737              * Fires when a row is selected being selected, return false to cancel.
22738              * @param {SelectionModel} this
22739              * @param {Number} rowIndex The selected index
22740              * @param {Boolean} keepExisting False if other selections will be cleared
22741              */
22742             "beforerowselect" : true,
22743         /**
22744              * @event rowselect
22745              * Fires when a row is selected.
22746              * @param {SelectionModel} this
22747              * @param {Number} rowIndex The selected index
22748              * @param {Roo.data.Record} r The record
22749              */
22750             "rowselect" : true,
22751         /**
22752              * @event rowdeselect
22753              * Fires when a row is deselected.
22754              * @param {SelectionModel} this
22755              * @param {Number} rowIndex The selected index
22756              */
22757         "rowdeselect" : true
22758     });
22759     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22760     this.locked = false;
22761  };
22762
22763 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
22764     /**
22765      * @cfg {Boolean} singleSelect
22766      * True to allow selection of only one row at a time (defaults to false)
22767      */
22768     singleSelect : false,
22769
22770     // private
22771     initEvents : function()
22772     {
22773
22774         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22775         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
22776         //}else{ // allow click to work like normal
22777          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
22778         //}
22779         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
22780         this.grid.on("rowclick", this.handleMouseDown, this);
22781         
22782         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22783             "up" : function(e){
22784                 if(!e.shiftKey){
22785                     this.selectPrevious(e.shiftKey);
22786                 }else if(this.last !== false && this.lastActive !== false){
22787                     var last = this.last;
22788                     this.selectRange(this.last,  this.lastActive-1);
22789                     this.grid.getView().focusRow(this.lastActive);
22790                     if(last !== false){
22791                         this.last = last;
22792                     }
22793                 }else{
22794                     this.selectFirstRow();
22795                 }
22796                 this.fireEvent("afterselectionchange", this);
22797             },
22798             "down" : function(e){
22799                 if(!e.shiftKey){
22800                     this.selectNext(e.shiftKey);
22801                 }else if(this.last !== false && this.lastActive !== false){
22802                     var last = this.last;
22803                     this.selectRange(this.last,  this.lastActive+1);
22804                     this.grid.getView().focusRow(this.lastActive);
22805                     if(last !== false){
22806                         this.last = last;
22807                     }
22808                 }else{
22809                     this.selectFirstRow();
22810                 }
22811                 this.fireEvent("afterselectionchange", this);
22812             },
22813             scope: this
22814         });
22815         this.grid.store.on('load', function(){
22816             this.selections.clear();
22817         },this);
22818         /*
22819         var view = this.grid.view;
22820         view.on("refresh", this.onRefresh, this);
22821         view.on("rowupdated", this.onRowUpdated, this);
22822         view.on("rowremoved", this.onRemove, this);
22823         */
22824     },
22825
22826     // private
22827     onRefresh : function()
22828     {
22829         var ds = this.grid.store, i, v = this.grid.view;
22830         var s = this.selections;
22831         s.each(function(r){
22832             if((i = ds.indexOfId(r.id)) != -1){
22833                 v.onRowSelect(i);
22834             }else{
22835                 s.remove(r);
22836             }
22837         });
22838     },
22839
22840     // private
22841     onRemove : function(v, index, r){
22842         this.selections.remove(r);
22843     },
22844
22845     // private
22846     onRowUpdated : function(v, index, r){
22847         if(this.isSelected(r)){
22848             v.onRowSelect(index);
22849         }
22850     },
22851
22852     /**
22853      * Select records.
22854      * @param {Array} records The records to select
22855      * @param {Boolean} keepExisting (optional) True to keep existing selections
22856      */
22857     selectRecords : function(records, keepExisting)
22858     {
22859         if(!keepExisting){
22860             this.clearSelections();
22861         }
22862             var ds = this.grid.store;
22863         for(var i = 0, len = records.length; i < len; i++){
22864             this.selectRow(ds.indexOf(records[i]), true);
22865         }
22866     },
22867
22868     /**
22869      * Gets the number of selected rows.
22870      * @return {Number}
22871      */
22872     getCount : function(){
22873         return this.selections.length;
22874     },
22875
22876     /**
22877      * Selects the first row in the grid.
22878      */
22879     selectFirstRow : function(){
22880         this.selectRow(0);
22881     },
22882
22883     /**
22884      * Select the last row.
22885      * @param {Boolean} keepExisting (optional) True to keep existing selections
22886      */
22887     selectLastRow : function(keepExisting){
22888         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22889         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
22890     },
22891
22892     /**
22893      * Selects the row immediately following the last selected row.
22894      * @param {Boolean} keepExisting (optional) True to keep existing selections
22895      */
22896     selectNext : function(keepExisting)
22897     {
22898             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
22899             this.selectRow(this.last+1, keepExisting);
22900             this.grid.getView().focusRow(this.last);
22901         }
22902     },
22903
22904     /**
22905      * Selects the row that precedes the last selected row.
22906      * @param {Boolean} keepExisting (optional) True to keep existing selections
22907      */
22908     selectPrevious : function(keepExisting){
22909         if(this.last){
22910             this.selectRow(this.last-1, keepExisting);
22911             this.grid.getView().focusRow(this.last);
22912         }
22913     },
22914
22915     /**
22916      * Returns the selected records
22917      * @return {Array} Array of selected records
22918      */
22919     getSelections : function(){
22920         return [].concat(this.selections.items);
22921     },
22922
22923     /**
22924      * Returns the first selected record.
22925      * @return {Record}
22926      */
22927     getSelected : function(){
22928         return this.selections.itemAt(0);
22929     },
22930
22931
22932     /**
22933      * Clears all selections.
22934      */
22935     clearSelections : function(fast)
22936     {
22937         if(this.locked) {
22938             return;
22939         }
22940         if(fast !== true){
22941                 var ds = this.grid.store;
22942             var s = this.selections;
22943             s.each(function(r){
22944                 this.deselectRow(ds.indexOfId(r.id));
22945             }, this);
22946             s.clear();
22947         }else{
22948             this.selections.clear();
22949         }
22950         this.last = false;
22951     },
22952
22953
22954     /**
22955      * Selects all rows.
22956      */
22957     selectAll : function(){
22958         if(this.locked) {
22959             return;
22960         }
22961         this.selections.clear();
22962         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
22963             this.selectRow(i, true);
22964         }
22965     },
22966
22967     /**
22968      * Returns True if there is a selection.
22969      * @return {Boolean}
22970      */
22971     hasSelection : function(){
22972         return this.selections.length > 0;
22973     },
22974
22975     /**
22976      * Returns True if the specified row is selected.
22977      * @param {Number/Record} record The record or index of the record to check
22978      * @return {Boolean}
22979      */
22980     isSelected : function(index){
22981             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
22982         return (r && this.selections.key(r.id) ? true : false);
22983     },
22984
22985     /**
22986      * Returns True if the specified record id is selected.
22987      * @param {String} id The id of record to check
22988      * @return {Boolean}
22989      */
22990     isIdSelected : function(id){
22991         return (this.selections.key(id) ? true : false);
22992     },
22993
22994
22995     // private
22996     handleMouseDBClick : function(e, t){
22997         
22998     },
22999     // private
23000     handleMouseDown : function(e, t)
23001     {
23002             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23003         if(this.isLocked() || rowIndex < 0 ){
23004             return;
23005         };
23006         if(e.shiftKey && this.last !== false){
23007             var last = this.last;
23008             this.selectRange(last, rowIndex, e.ctrlKey);
23009             this.last = last; // reset the last
23010             t.focus();
23011     
23012         }else{
23013             var isSelected = this.isSelected(rowIndex);
23014             //Roo.log("select row:" + rowIndex);
23015             if(isSelected){
23016                 this.deselectRow(rowIndex);
23017             } else {
23018                         this.selectRow(rowIndex, true);
23019             }
23020     
23021             /*
23022                 if(e.button !== 0 && isSelected){
23023                 alert('rowIndex 2: ' + rowIndex);
23024                     view.focusRow(rowIndex);
23025                 }else if(e.ctrlKey && isSelected){
23026                     this.deselectRow(rowIndex);
23027                 }else if(!isSelected){
23028                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23029                     view.focusRow(rowIndex);
23030                 }
23031             */
23032         }
23033         this.fireEvent("afterselectionchange", this);
23034     },
23035     // private
23036     handleDragableRowClick :  function(grid, rowIndex, e) 
23037     {
23038         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23039             this.selectRow(rowIndex, false);
23040             grid.view.focusRow(rowIndex);
23041              this.fireEvent("afterselectionchange", this);
23042         }
23043     },
23044     
23045     /**
23046      * Selects multiple rows.
23047      * @param {Array} rows Array of the indexes of the row to select
23048      * @param {Boolean} keepExisting (optional) True to keep existing selections
23049      */
23050     selectRows : function(rows, keepExisting){
23051         if(!keepExisting){
23052             this.clearSelections();
23053         }
23054         for(var i = 0, len = rows.length; i < len; i++){
23055             this.selectRow(rows[i], true);
23056         }
23057     },
23058
23059     /**
23060      * Selects a range of rows. All rows in between startRow and endRow are also selected.
23061      * @param {Number} startRow The index of the first row in the range
23062      * @param {Number} endRow The index of the last row in the range
23063      * @param {Boolean} keepExisting (optional) True to retain existing selections
23064      */
23065     selectRange : function(startRow, endRow, keepExisting){
23066         if(this.locked) {
23067             return;
23068         }
23069         if(!keepExisting){
23070             this.clearSelections();
23071         }
23072         if(startRow <= endRow){
23073             for(var i = startRow; i <= endRow; i++){
23074                 this.selectRow(i, true);
23075             }
23076         }else{
23077             for(var i = startRow; i >= endRow; i--){
23078                 this.selectRow(i, true);
23079             }
23080         }
23081     },
23082
23083     /**
23084      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
23085      * @param {Number} startRow The index of the first row in the range
23086      * @param {Number} endRow The index of the last row in the range
23087      */
23088     deselectRange : function(startRow, endRow, preventViewNotify){
23089         if(this.locked) {
23090             return;
23091         }
23092         for(var i = startRow; i <= endRow; i++){
23093             this.deselectRow(i, preventViewNotify);
23094         }
23095     },
23096
23097     /**
23098      * Selects a row.
23099      * @param {Number} row The index of the row to select
23100      * @param {Boolean} keepExisting (optional) True to keep existing selections
23101      */
23102     selectRow : function(index, keepExisting, preventViewNotify)
23103     {
23104             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
23105             return;
23106         }
23107         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
23108             if(!keepExisting || this.singleSelect){
23109                 this.clearSelections();
23110             }
23111             
23112             var r = this.grid.store.getAt(index);
23113             //console.log('selectRow - record id :' + r.id);
23114             
23115             this.selections.add(r);
23116             this.last = this.lastActive = index;
23117             if(!preventViewNotify){
23118                 var proxy = new Roo.Element(
23119                                 this.grid.getRowDom(index)
23120                 );
23121                 proxy.addClass('bg-info info');
23122             }
23123             this.fireEvent("rowselect", this, index, r);
23124             this.fireEvent("selectionchange", this);
23125         }
23126     },
23127
23128     /**
23129      * Deselects a row.
23130      * @param {Number} row The index of the row to deselect
23131      */
23132     deselectRow : function(index, preventViewNotify)
23133     {
23134         if(this.locked) {
23135             return;
23136         }
23137         if(this.last == index){
23138             this.last = false;
23139         }
23140         if(this.lastActive == index){
23141             this.lastActive = false;
23142         }
23143         
23144         var r = this.grid.store.getAt(index);
23145         if (!r) {
23146             return;
23147         }
23148         
23149         this.selections.remove(r);
23150         //.console.log('deselectRow - record id :' + r.id);
23151         if(!preventViewNotify){
23152         
23153             var proxy = new Roo.Element(
23154                 this.grid.getRowDom(index)
23155             );
23156             proxy.removeClass('bg-info info');
23157         }
23158         this.fireEvent("rowdeselect", this, index);
23159         this.fireEvent("selectionchange", this);
23160     },
23161
23162     // private
23163     restoreLast : function(){
23164         if(this._last){
23165             this.last = this._last;
23166         }
23167     },
23168
23169     // private
23170     acceptsNav : function(row, col, cm){
23171         return !cm.isHidden(col) && cm.isCellEditable(col, row);
23172     },
23173
23174     // private
23175     onEditorKey : function(field, e){
23176         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
23177         if(k == e.TAB){
23178             e.stopEvent();
23179             ed.completeEdit();
23180             if(e.shiftKey){
23181                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
23182             }else{
23183                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
23184             }
23185         }else if(k == e.ENTER && !e.ctrlKey){
23186             e.stopEvent();
23187             ed.completeEdit();
23188             if(e.shiftKey){
23189                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
23190             }else{
23191                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
23192             }
23193         }else if(k == e.ESC){
23194             ed.cancelEdit();
23195         }
23196         if(newCell){
23197             g.startEditing(newCell[0], newCell[1]);
23198         }
23199     }
23200 });
23201 /*
23202  * Based on:
23203  * Ext JS Library 1.1.1
23204  * Copyright(c) 2006-2007, Ext JS, LLC.
23205  *
23206  * Originally Released Under LGPL - original licence link has changed is not relivant.
23207  *
23208  * Fork - LGPL
23209  * <script type="text/javascript">
23210  */
23211  
23212 /**
23213  * @class Roo.bootstrap.PagingToolbar
23214  * @extends Roo.bootstrap.NavSimplebar
23215  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
23216  * @constructor
23217  * Create a new PagingToolbar
23218  * @param {Object} config The config object
23219  * @param {Roo.data.Store} store
23220  */
23221 Roo.bootstrap.PagingToolbar = function(config)
23222 {
23223     // old args format still supported... - xtype is prefered..
23224         // created from xtype...
23225     
23226     this.ds = config.dataSource;
23227     
23228     if (config.store && !this.ds) {
23229         this.store= Roo.factory(config.store, Roo.data);
23230         this.ds = this.store;
23231         this.ds.xmodule = this.xmodule || false;
23232     }
23233     
23234     this.toolbarItems = [];
23235     if (config.items) {
23236         this.toolbarItems = config.items;
23237     }
23238     
23239     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
23240     
23241     this.cursor = 0;
23242     
23243     if (this.ds) { 
23244         this.bind(this.ds);
23245     }
23246     
23247     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
23248     
23249 };
23250
23251 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
23252     /**
23253      * @cfg {Roo.data.Store} dataSource
23254      * The underlying data store providing the paged data
23255      */
23256     /**
23257      * @cfg {String/HTMLElement/Element} container
23258      * container The id or element that will contain the toolbar
23259      */
23260     /**
23261      * @cfg {Boolean} displayInfo
23262      * True to display the displayMsg (defaults to false)
23263      */
23264     /**
23265      * @cfg {Number} pageSize
23266      * The number of records to display per page (defaults to 20)
23267      */
23268     pageSize: 20,
23269     /**
23270      * @cfg {String} displayMsg
23271      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
23272      */
23273     displayMsg : 'Displaying {0} - {1} of {2}',
23274     /**
23275      * @cfg {String} emptyMsg
23276      * The message to display when no records are found (defaults to "No data to display")
23277      */
23278     emptyMsg : 'No data to display',
23279     /**
23280      * Customizable piece of the default paging text (defaults to "Page")
23281      * @type String
23282      */
23283     beforePageText : "Page",
23284     /**
23285      * Customizable piece of the default paging text (defaults to "of %0")
23286      * @type String
23287      */
23288     afterPageText : "of {0}",
23289     /**
23290      * Customizable piece of the default paging text (defaults to "First Page")
23291      * @type String
23292      */
23293     firstText : "First Page",
23294     /**
23295      * Customizable piece of the default paging text (defaults to "Previous Page")
23296      * @type String
23297      */
23298     prevText : "Previous Page",
23299     /**
23300      * Customizable piece of the default paging text (defaults to "Next Page")
23301      * @type String
23302      */
23303     nextText : "Next Page",
23304     /**
23305      * Customizable piece of the default paging text (defaults to "Last Page")
23306      * @type String
23307      */
23308     lastText : "Last Page",
23309     /**
23310      * Customizable piece of the default paging text (defaults to "Refresh")
23311      * @type String
23312      */
23313     refreshText : "Refresh",
23314
23315     buttons : false,
23316     // private
23317     onRender : function(ct, position) 
23318     {
23319         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
23320         this.navgroup.parentId = this.id;
23321         this.navgroup.onRender(this.el, null);
23322         // add the buttons to the navgroup
23323         
23324         if(this.displayInfo){
23325             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
23326             this.displayEl = this.el.select('.x-paging-info', true).first();
23327 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
23328 //            this.displayEl = navel.el.select('span',true).first();
23329         }
23330         
23331         var _this = this;
23332         
23333         if(this.buttons){
23334             Roo.each(_this.buttons, function(e){ // this might need to use render????
23335                Roo.factory(e).onRender(_this.el, null);
23336             });
23337         }
23338             
23339         Roo.each(_this.toolbarItems, function(e) {
23340             _this.navgroup.addItem(e);
23341         });
23342         
23343         
23344         this.first = this.navgroup.addItem({
23345             tooltip: this.firstText,
23346             cls: "prev",
23347             icon : 'fa fa-backward',
23348             disabled: true,
23349             preventDefault: true,
23350             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
23351         });
23352         
23353         this.prev =  this.navgroup.addItem({
23354             tooltip: this.prevText,
23355             cls: "prev",
23356             icon : 'fa fa-step-backward',
23357             disabled: true,
23358             preventDefault: true,
23359             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
23360         });
23361     //this.addSeparator();
23362         
23363         
23364         var field = this.navgroup.addItem( {
23365             tagtype : 'span',
23366             cls : 'x-paging-position',
23367             
23368             html : this.beforePageText  +
23369                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
23370                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
23371          } ); //?? escaped?
23372         
23373         this.field = field.el.select('input', true).first();
23374         this.field.on("keydown", this.onPagingKeydown, this);
23375         this.field.on("focus", function(){this.dom.select();});
23376     
23377     
23378         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
23379         //this.field.setHeight(18);
23380         //this.addSeparator();
23381         this.next = this.navgroup.addItem({
23382             tooltip: this.nextText,
23383             cls: "next",
23384             html : ' <i class="fa fa-step-forward">',
23385             disabled: true,
23386             preventDefault: true,
23387             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
23388         });
23389         this.last = this.navgroup.addItem({
23390             tooltip: this.lastText,
23391             icon : 'fa fa-forward',
23392             cls: "next",
23393             disabled: true,
23394             preventDefault: true,
23395             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
23396         });
23397     //this.addSeparator();
23398         this.loading = this.navgroup.addItem({
23399             tooltip: this.refreshText,
23400             icon: 'fa fa-refresh',
23401             preventDefault: true,
23402             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
23403         });
23404         
23405     },
23406
23407     // private
23408     updateInfo : function(){
23409         if(this.displayEl){
23410             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
23411             var msg = count == 0 ?
23412                 this.emptyMsg :
23413                 String.format(
23414                     this.displayMsg,
23415                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
23416                 );
23417             this.displayEl.update(msg);
23418         }
23419     },
23420
23421     // private
23422     onLoad : function(ds, r, o){
23423        this.cursor = o.params ? o.params.start : 0;
23424        var d = this.getPageData(),
23425             ap = d.activePage,
23426             ps = d.pages;
23427         
23428        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
23429        this.field.dom.value = ap;
23430        this.first.setDisabled(ap == 1);
23431        this.prev.setDisabled(ap == 1);
23432        this.next.setDisabled(ap == ps);
23433        this.last.setDisabled(ap == ps);
23434        this.loading.enable();
23435        this.updateInfo();
23436     },
23437
23438     // private
23439     getPageData : function(){
23440         var total = this.ds.getTotalCount();
23441         return {
23442             total : total,
23443             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
23444             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
23445         };
23446     },
23447
23448     // private
23449     onLoadError : function(){
23450         this.loading.enable();
23451     },
23452
23453     // private
23454     onPagingKeydown : function(e){
23455         var k = e.getKey();
23456         var d = this.getPageData();
23457         if(k == e.RETURN){
23458             var v = this.field.dom.value, pageNum;
23459             if(!v || isNaN(pageNum = parseInt(v, 10))){
23460                 this.field.dom.value = d.activePage;
23461                 return;
23462             }
23463             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
23464             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23465             e.stopEvent();
23466         }
23467         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))
23468         {
23469           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
23470           this.field.dom.value = pageNum;
23471           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
23472           e.stopEvent();
23473         }
23474         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
23475         {
23476           var v = this.field.dom.value, pageNum; 
23477           var increment = (e.shiftKey) ? 10 : 1;
23478           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
23479                 increment *= -1;
23480           }
23481           if(!v || isNaN(pageNum = parseInt(v, 10))) {
23482             this.field.dom.value = d.activePage;
23483             return;
23484           }
23485           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
23486           {
23487             this.field.dom.value = parseInt(v, 10) + increment;
23488             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
23489             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23490           }
23491           e.stopEvent();
23492         }
23493     },
23494
23495     // private
23496     beforeLoad : function(){
23497         if(this.loading){
23498             this.loading.disable();
23499         }
23500     },
23501
23502     // private
23503     onClick : function(which){
23504         
23505         var ds = this.ds;
23506         if (!ds) {
23507             return;
23508         }
23509         
23510         switch(which){
23511             case "first":
23512                 ds.load({params:{start: 0, limit: this.pageSize}});
23513             break;
23514             case "prev":
23515                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
23516             break;
23517             case "next":
23518                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
23519             break;
23520             case "last":
23521                 var total = ds.getTotalCount();
23522                 var extra = total % this.pageSize;
23523                 var lastStart = extra ? (total - extra) : total-this.pageSize;
23524                 ds.load({params:{start: lastStart, limit: this.pageSize}});
23525             break;
23526             case "refresh":
23527                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
23528             break;
23529         }
23530     },
23531
23532     /**
23533      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
23534      * @param {Roo.data.Store} store The data store to unbind
23535      */
23536     unbind : function(ds){
23537         ds.un("beforeload", this.beforeLoad, this);
23538         ds.un("load", this.onLoad, this);
23539         ds.un("loadexception", this.onLoadError, this);
23540         ds.un("remove", this.updateInfo, this);
23541         ds.un("add", this.updateInfo, this);
23542         this.ds = undefined;
23543     },
23544
23545     /**
23546      * Binds the paging toolbar to the specified {@link Roo.data.Store}
23547      * @param {Roo.data.Store} store The data store to bind
23548      */
23549     bind : function(ds){
23550         ds.on("beforeload", this.beforeLoad, this);
23551         ds.on("load", this.onLoad, this);
23552         ds.on("loadexception", this.onLoadError, this);
23553         ds.on("remove", this.updateInfo, this);
23554         ds.on("add", this.updateInfo, this);
23555         this.ds = ds;
23556     }
23557 });/*
23558  * - LGPL
23559  *
23560  * element
23561  * 
23562  */
23563
23564 /**
23565  * @class Roo.bootstrap.MessageBar
23566  * @extends Roo.bootstrap.Component
23567  * Bootstrap MessageBar class
23568  * @cfg {String} html contents of the MessageBar
23569  * @cfg {String} weight (info | success | warning | danger) default info
23570  * @cfg {String} beforeClass insert the bar before the given class
23571  * @cfg {Boolean} closable (true | false) default false
23572  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
23573  * 
23574  * @constructor
23575  * Create a new Element
23576  * @param {Object} config The config object
23577  */
23578
23579 Roo.bootstrap.MessageBar = function(config){
23580     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
23581 };
23582
23583 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
23584     
23585     html: '',
23586     weight: 'info',
23587     closable: false,
23588     fixed: false,
23589     beforeClass: 'bootstrap-sticky-wrap',
23590     
23591     getAutoCreate : function(){
23592         
23593         var cfg = {
23594             tag: 'div',
23595             cls: 'alert alert-dismissable alert-' + this.weight,
23596             cn: [
23597                 {
23598                     tag: 'span',
23599                     cls: 'message',
23600                     html: this.html || ''
23601                 }
23602             ]
23603         };
23604         
23605         if(this.fixed){
23606             cfg.cls += ' alert-messages-fixed';
23607         }
23608         
23609         if(this.closable){
23610             cfg.cn.push({
23611                 tag: 'button',
23612                 cls: 'close',
23613                 html: 'x'
23614             });
23615         }
23616         
23617         return cfg;
23618     },
23619     
23620     onRender : function(ct, position)
23621     {
23622         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
23623         
23624         if(!this.el){
23625             var cfg = Roo.apply({},  this.getAutoCreate());
23626             cfg.id = Roo.id();
23627             
23628             if (this.cls) {
23629                 cfg.cls += ' ' + this.cls;
23630             }
23631             if (this.style) {
23632                 cfg.style = this.style;
23633             }
23634             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
23635             
23636             this.el.setVisibilityMode(Roo.Element.DISPLAY);
23637         }
23638         
23639         this.el.select('>button.close').on('click', this.hide, this);
23640         
23641     },
23642     
23643     show : function()
23644     {
23645         if (!this.rendered) {
23646             this.render();
23647         }
23648         
23649         this.el.show();
23650         
23651         this.fireEvent('show', this);
23652         
23653     },
23654     
23655     hide : function()
23656     {
23657         if (!this.rendered) {
23658             this.render();
23659         }
23660         
23661         this.el.hide();
23662         
23663         this.fireEvent('hide', this);
23664     },
23665     
23666     update : function()
23667     {
23668 //        var e = this.el.dom.firstChild;
23669 //        
23670 //        if(this.closable){
23671 //            e = e.nextSibling;
23672 //        }
23673 //        
23674 //        e.data = this.html || '';
23675
23676         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
23677     }
23678    
23679 });
23680
23681  
23682
23683      /*
23684  * - LGPL
23685  *
23686  * Graph
23687  * 
23688  */
23689
23690
23691 /**
23692  * @class Roo.bootstrap.Graph
23693  * @extends Roo.bootstrap.Component
23694  * Bootstrap Graph class
23695 > Prameters
23696  -sm {number} sm 4
23697  -md {number} md 5
23698  @cfg {String} graphtype  bar | vbar | pie
23699  @cfg {number} g_x coodinator | centre x (pie)
23700  @cfg {number} g_y coodinator | centre y (pie)
23701  @cfg {number} g_r radius (pie)
23702  @cfg {number} g_height height of the chart (respected by all elements in the set)
23703  @cfg {number} g_width width of the chart (respected by all elements in the set)
23704  @cfg {Object} title The title of the chart
23705     
23706  -{Array}  values
23707  -opts (object) options for the chart 
23708      o {
23709      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
23710      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
23711      o vgutter (number)
23712      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.
23713      o stacked (boolean) whether or not to tread values as in a stacked bar chart
23714      o to
23715      o stretch (boolean)
23716      o }
23717  -opts (object) options for the pie
23718      o{
23719      o cut
23720      o startAngle (number)
23721      o endAngle (number)
23722      } 
23723  *
23724  * @constructor
23725  * Create a new Input
23726  * @param {Object} config The config object
23727  */
23728
23729 Roo.bootstrap.Graph = function(config){
23730     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
23731     
23732     this.addEvents({
23733         // img events
23734         /**
23735          * @event click
23736          * The img click event for the img.
23737          * @param {Roo.EventObject} e
23738          */
23739         "click" : true
23740     });
23741 };
23742
23743 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
23744     
23745     sm: 4,
23746     md: 5,
23747     graphtype: 'bar',
23748     g_height: 250,
23749     g_width: 400,
23750     g_x: 50,
23751     g_y: 50,
23752     g_r: 30,
23753     opts:{
23754         //g_colors: this.colors,
23755         g_type: 'soft',
23756         g_gutter: '20%'
23757
23758     },
23759     title : false,
23760
23761     getAutoCreate : function(){
23762         
23763         var cfg = {
23764             tag: 'div',
23765             html : null
23766         };
23767         
23768         
23769         return  cfg;
23770     },
23771
23772     onRender : function(ct,position){
23773         
23774         
23775         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23776         
23777         if (typeof(Raphael) == 'undefined') {
23778             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23779             return;
23780         }
23781         
23782         this.raphael = Raphael(this.el.dom);
23783         
23784                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23785                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23786                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23787                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23788                 /*
23789                 r.text(160, 10, "Single Series Chart").attr(txtattr);
23790                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23791                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23792                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23793                 
23794                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23795                 r.barchart(330, 10, 300, 220, data1);
23796                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23797                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23798                 */
23799                 
23800                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23801                 // r.barchart(30, 30, 560, 250,  xdata, {
23802                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23803                 //     axis : "0 0 1 1",
23804                 //     axisxlabels :  xdata
23805                 //     //yvalues : cols,
23806                    
23807                 // });
23808 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23809 //        
23810 //        this.load(null,xdata,{
23811 //                axis : "0 0 1 1",
23812 //                axisxlabels :  xdata
23813 //                });
23814
23815     },
23816
23817     load : function(graphtype,xdata,opts)
23818     {
23819         this.raphael.clear();
23820         if(!graphtype) {
23821             graphtype = this.graphtype;
23822         }
23823         if(!opts){
23824             opts = this.opts;
23825         }
23826         var r = this.raphael,
23827             fin = function () {
23828                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23829             },
23830             fout = function () {
23831                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23832             },
23833             pfin = function() {
23834                 this.sector.stop();
23835                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23836
23837                 if (this.label) {
23838                     this.label[0].stop();
23839                     this.label[0].attr({ r: 7.5 });
23840                     this.label[1].attr({ "font-weight": 800 });
23841                 }
23842             },
23843             pfout = function() {
23844                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23845
23846                 if (this.label) {
23847                     this.label[0].animate({ r: 5 }, 500, "bounce");
23848                     this.label[1].attr({ "font-weight": 400 });
23849                 }
23850             };
23851
23852         switch(graphtype){
23853             case 'bar':
23854                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23855                 break;
23856             case 'hbar':
23857                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23858                 break;
23859             case 'pie':
23860 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
23861 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23862 //            
23863                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23864                 
23865                 break;
23866
23867         }
23868         
23869         if(this.title){
23870             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23871         }
23872         
23873     },
23874     
23875     setTitle: function(o)
23876     {
23877         this.title = o;
23878     },
23879     
23880     initEvents: function() {
23881         
23882         if(!this.href){
23883             this.el.on('click', this.onClick, this);
23884         }
23885     },
23886     
23887     onClick : function(e)
23888     {
23889         Roo.log('img onclick');
23890         this.fireEvent('click', this, e);
23891     }
23892    
23893 });
23894
23895  
23896 /*
23897  * - LGPL
23898  *
23899  * numberBox
23900  * 
23901  */
23902 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23903
23904 /**
23905  * @class Roo.bootstrap.dash.NumberBox
23906  * @extends Roo.bootstrap.Component
23907  * Bootstrap NumberBox class
23908  * @cfg {String} headline Box headline
23909  * @cfg {String} content Box content
23910  * @cfg {String} icon Box icon
23911  * @cfg {String} footer Footer text
23912  * @cfg {String} fhref Footer href
23913  * 
23914  * @constructor
23915  * Create a new NumberBox
23916  * @param {Object} config The config object
23917  */
23918
23919
23920 Roo.bootstrap.dash.NumberBox = function(config){
23921     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23922     
23923 };
23924
23925 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
23926     
23927     headline : '',
23928     content : '',
23929     icon : '',
23930     footer : '',
23931     fhref : '',
23932     ficon : '',
23933     
23934     getAutoCreate : function(){
23935         
23936         var cfg = {
23937             tag : 'div',
23938             cls : 'small-box ',
23939             cn : [
23940                 {
23941                     tag : 'div',
23942                     cls : 'inner',
23943                     cn :[
23944                         {
23945                             tag : 'h3',
23946                             cls : 'roo-headline',
23947                             html : this.headline
23948                         },
23949                         {
23950                             tag : 'p',
23951                             cls : 'roo-content',
23952                             html : this.content
23953                         }
23954                     ]
23955                 }
23956             ]
23957         };
23958         
23959         if(this.icon){
23960             cfg.cn.push({
23961                 tag : 'div',
23962                 cls : 'icon',
23963                 cn :[
23964                     {
23965                         tag : 'i',
23966                         cls : 'ion ' + this.icon
23967                     }
23968                 ]
23969             });
23970         }
23971         
23972         if(this.footer){
23973             var footer = {
23974                 tag : 'a',
23975                 cls : 'small-box-footer',
23976                 href : this.fhref || '#',
23977                 html : this.footer
23978             };
23979             
23980             cfg.cn.push(footer);
23981             
23982         }
23983         
23984         return  cfg;
23985     },
23986
23987     onRender : function(ct,position){
23988         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
23989
23990
23991        
23992                 
23993     },
23994
23995     setHeadline: function (value)
23996     {
23997         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
23998     },
23999     
24000     setFooter: function (value, href)
24001     {
24002         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24003         
24004         if(href){
24005             this.el.select('a.small-box-footer',true).first().attr('href', href);
24006         }
24007         
24008     },
24009
24010     setContent: function (value)
24011     {
24012         this.el.select('.roo-content',true).first().dom.innerHTML = value;
24013     },
24014
24015     initEvents: function() 
24016     {   
24017         
24018     }
24019     
24020 });
24021
24022  
24023 /*
24024  * - LGPL
24025  *
24026  * TabBox
24027  * 
24028  */
24029 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24030
24031 /**
24032  * @class Roo.bootstrap.dash.TabBox
24033  * @extends Roo.bootstrap.Component
24034  * Bootstrap TabBox class
24035  * @cfg {String} title Title of the TabBox
24036  * @cfg {String} icon Icon of the TabBox
24037  * @cfg {Boolean} showtabs (true|false) show the tabs default true
24038  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24039  * 
24040  * @constructor
24041  * Create a new TabBox
24042  * @param {Object} config The config object
24043  */
24044
24045
24046 Roo.bootstrap.dash.TabBox = function(config){
24047     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24048     this.addEvents({
24049         // raw events
24050         /**
24051          * @event addpane
24052          * When a pane is added
24053          * @param {Roo.bootstrap.dash.TabPane} pane
24054          */
24055         "addpane" : true,
24056         /**
24057          * @event activatepane
24058          * When a pane is activated
24059          * @param {Roo.bootstrap.dash.TabPane} pane
24060          */
24061         "activatepane" : true
24062         
24063          
24064     });
24065     
24066     this.panes = [];
24067 };
24068
24069 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
24070
24071     title : '',
24072     icon : false,
24073     showtabs : true,
24074     tabScrollable : false,
24075     
24076     getChildContainer : function()
24077     {
24078         return this.el.select('.tab-content', true).first();
24079     },
24080     
24081     getAutoCreate : function(){
24082         
24083         var header = {
24084             tag: 'li',
24085             cls: 'pull-left header',
24086             html: this.title,
24087             cn : []
24088         };
24089         
24090         if(this.icon){
24091             header.cn.push({
24092                 tag: 'i',
24093                 cls: 'fa ' + this.icon
24094             });
24095         }
24096         
24097         var h = {
24098             tag: 'ul',
24099             cls: 'nav nav-tabs pull-right',
24100             cn: [
24101                 header
24102             ]
24103         };
24104         
24105         if(this.tabScrollable){
24106             h = {
24107                 tag: 'div',
24108                 cls: 'tab-header',
24109                 cn: [
24110                     {
24111                         tag: 'ul',
24112                         cls: 'nav nav-tabs pull-right',
24113                         cn: [
24114                             header
24115                         ]
24116                     }
24117                 ]
24118             };
24119         }
24120         
24121         var cfg = {
24122             tag: 'div',
24123             cls: 'nav-tabs-custom',
24124             cn: [
24125                 h,
24126                 {
24127                     tag: 'div',
24128                     cls: 'tab-content no-padding',
24129                     cn: []
24130                 }
24131             ]
24132         };
24133
24134         return  cfg;
24135     },
24136     initEvents : function()
24137     {
24138         //Roo.log('add add pane handler');
24139         this.on('addpane', this.onAddPane, this);
24140     },
24141      /**
24142      * Updates the box title
24143      * @param {String} html to set the title to.
24144      */
24145     setTitle : function(value)
24146     {
24147         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
24148     },
24149     onAddPane : function(pane)
24150     {
24151         this.panes.push(pane);
24152         //Roo.log('addpane');
24153         //Roo.log(pane);
24154         // tabs are rendere left to right..
24155         if(!this.showtabs){
24156             return;
24157         }
24158         
24159         var ctr = this.el.select('.nav-tabs', true).first();
24160          
24161          
24162         var existing = ctr.select('.nav-tab',true);
24163         var qty = existing.getCount();;
24164         
24165         
24166         var tab = ctr.createChild({
24167             tag : 'li',
24168             cls : 'nav-tab' + (qty ? '' : ' active'),
24169             cn : [
24170                 {
24171                     tag : 'a',
24172                     href:'#',
24173                     html : pane.title
24174                 }
24175             ]
24176         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
24177         pane.tab = tab;
24178         
24179         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
24180         if (!qty) {
24181             pane.el.addClass('active');
24182         }
24183         
24184                 
24185     },
24186     onTabClick : function(ev,un,ob,pane)
24187     {
24188         //Roo.log('tab - prev default');
24189         ev.preventDefault();
24190         
24191         
24192         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
24193         pane.tab.addClass('active');
24194         //Roo.log(pane.title);
24195         this.getChildContainer().select('.tab-pane',true).removeClass('active');
24196         // technically we should have a deactivate event.. but maybe add later.
24197         // and it should not de-activate the selected tab...
24198         this.fireEvent('activatepane', pane);
24199         pane.el.addClass('active');
24200         pane.fireEvent('activate');
24201         
24202         
24203     },
24204     
24205     getActivePane : function()
24206     {
24207         var r = false;
24208         Roo.each(this.panes, function(p) {
24209             if(p.el.hasClass('active')){
24210                 r = p;
24211                 return false;
24212             }
24213             
24214             return;
24215         });
24216         
24217         return r;
24218     }
24219     
24220     
24221 });
24222
24223  
24224 /*
24225  * - LGPL
24226  *
24227  * Tab pane
24228  * 
24229  */
24230 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24231 /**
24232  * @class Roo.bootstrap.TabPane
24233  * @extends Roo.bootstrap.Component
24234  * Bootstrap TabPane class
24235  * @cfg {Boolean} active (false | true) Default false
24236  * @cfg {String} title title of panel
24237
24238  * 
24239  * @constructor
24240  * Create a new TabPane
24241  * @param {Object} config The config object
24242  */
24243
24244 Roo.bootstrap.dash.TabPane = function(config){
24245     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
24246     
24247     this.addEvents({
24248         // raw events
24249         /**
24250          * @event activate
24251          * When a pane is activated
24252          * @param {Roo.bootstrap.dash.TabPane} pane
24253          */
24254         "activate" : true
24255          
24256     });
24257 };
24258
24259 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
24260     
24261     active : false,
24262     title : '',
24263     
24264     // the tabBox that this is attached to.
24265     tab : false,
24266      
24267     getAutoCreate : function() 
24268     {
24269         var cfg = {
24270             tag: 'div',
24271             cls: 'tab-pane'
24272         };
24273         
24274         if(this.active){
24275             cfg.cls += ' active';
24276         }
24277         
24278         return cfg;
24279     },
24280     initEvents  : function()
24281     {
24282         //Roo.log('trigger add pane handler');
24283         this.parent().fireEvent('addpane', this)
24284     },
24285     
24286      /**
24287      * Updates the tab title 
24288      * @param {String} html to set the title to.
24289      */
24290     setTitle: function(str)
24291     {
24292         if (!this.tab) {
24293             return;
24294         }
24295         this.title = str;
24296         this.tab.select('a', true).first().dom.innerHTML = str;
24297         
24298     }
24299     
24300     
24301     
24302 });
24303
24304  
24305
24306
24307  /*
24308  * - LGPL
24309  *
24310  * menu
24311  * 
24312  */
24313 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24314
24315 /**
24316  * @class Roo.bootstrap.menu.Menu
24317  * @extends Roo.bootstrap.Component
24318  * Bootstrap Menu class - container for Menu
24319  * @cfg {String} html Text of the menu
24320  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
24321  * @cfg {String} icon Font awesome icon
24322  * @cfg {String} pos Menu align to (top | bottom) default bottom
24323  * 
24324  * 
24325  * @constructor
24326  * Create a new Menu
24327  * @param {Object} config The config object
24328  */
24329
24330
24331 Roo.bootstrap.menu.Menu = function(config){
24332     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
24333     
24334     this.addEvents({
24335         /**
24336          * @event beforeshow
24337          * Fires before this menu is displayed
24338          * @param {Roo.bootstrap.menu.Menu} this
24339          */
24340         beforeshow : true,
24341         /**
24342          * @event beforehide
24343          * Fires before this menu is hidden
24344          * @param {Roo.bootstrap.menu.Menu} this
24345          */
24346         beforehide : true,
24347         /**
24348          * @event show
24349          * Fires after this menu is displayed
24350          * @param {Roo.bootstrap.menu.Menu} this
24351          */
24352         show : true,
24353         /**
24354          * @event hide
24355          * Fires after this menu is hidden
24356          * @param {Roo.bootstrap.menu.Menu} this
24357          */
24358         hide : true,
24359         /**
24360          * @event click
24361          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
24362          * @param {Roo.bootstrap.menu.Menu} this
24363          * @param {Roo.EventObject} e
24364          */
24365         click : true
24366     });
24367     
24368 };
24369
24370 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
24371     
24372     submenu : false,
24373     html : '',
24374     weight : 'default',
24375     icon : false,
24376     pos : 'bottom',
24377     
24378     
24379     getChildContainer : function() {
24380         if(this.isSubMenu){
24381             return this.el;
24382         }
24383         
24384         return this.el.select('ul.dropdown-menu', true).first();  
24385     },
24386     
24387     getAutoCreate : function()
24388     {
24389         var text = [
24390             {
24391                 tag : 'span',
24392                 cls : 'roo-menu-text',
24393                 html : this.html
24394             }
24395         ];
24396         
24397         if(this.icon){
24398             text.unshift({
24399                 tag : 'i',
24400                 cls : 'fa ' + this.icon
24401             })
24402         }
24403         
24404         
24405         var cfg = {
24406             tag : 'div',
24407             cls : 'btn-group',
24408             cn : [
24409                 {
24410                     tag : 'button',
24411                     cls : 'dropdown-button btn btn-' + this.weight,
24412                     cn : text
24413                 },
24414                 {
24415                     tag : 'button',
24416                     cls : 'dropdown-toggle btn btn-' + this.weight,
24417                     cn : [
24418                         {
24419                             tag : 'span',
24420                             cls : 'caret'
24421                         }
24422                     ]
24423                 },
24424                 {
24425                     tag : 'ul',
24426                     cls : 'dropdown-menu'
24427                 }
24428             ]
24429             
24430         };
24431         
24432         if(this.pos == 'top'){
24433             cfg.cls += ' dropup';
24434         }
24435         
24436         if(this.isSubMenu){
24437             cfg = {
24438                 tag : 'ul',
24439                 cls : 'dropdown-menu'
24440             }
24441         }
24442         
24443         return cfg;
24444     },
24445     
24446     onRender : function(ct, position)
24447     {
24448         this.isSubMenu = ct.hasClass('dropdown-submenu');
24449         
24450         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
24451     },
24452     
24453     initEvents : function() 
24454     {
24455         if(this.isSubMenu){
24456             return;
24457         }
24458         
24459         this.hidden = true;
24460         
24461         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
24462         this.triggerEl.on('click', this.onTriggerPress, this);
24463         
24464         this.buttonEl = this.el.select('button.dropdown-button', true).first();
24465         this.buttonEl.on('click', this.onClick, this);
24466         
24467     },
24468     
24469     list : function()
24470     {
24471         if(this.isSubMenu){
24472             return this.el;
24473         }
24474         
24475         return this.el.select('ul.dropdown-menu', true).first();
24476     },
24477     
24478     onClick : function(e)
24479     {
24480         this.fireEvent("click", this, e);
24481     },
24482     
24483     onTriggerPress  : function(e)
24484     {   
24485         if (this.isVisible()) {
24486             this.hide();
24487         } else {
24488             this.show();
24489         }
24490     },
24491     
24492     isVisible : function(){
24493         return !this.hidden;
24494     },
24495     
24496     show : function()
24497     {
24498         this.fireEvent("beforeshow", this);
24499         
24500         this.hidden = false;
24501         this.el.addClass('open');
24502         
24503         Roo.get(document).on("mouseup", this.onMouseUp, this);
24504         
24505         this.fireEvent("show", this);
24506         
24507         
24508     },
24509     
24510     hide : function()
24511     {
24512         this.fireEvent("beforehide", this);
24513         
24514         this.hidden = true;
24515         this.el.removeClass('open');
24516         
24517         Roo.get(document).un("mouseup", this.onMouseUp);
24518         
24519         this.fireEvent("hide", this);
24520     },
24521     
24522     onMouseUp : function()
24523     {
24524         this.hide();
24525     }
24526     
24527 });
24528
24529  
24530  /*
24531  * - LGPL
24532  *
24533  * menu item
24534  * 
24535  */
24536 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24537
24538 /**
24539  * @class Roo.bootstrap.menu.Item
24540  * @extends Roo.bootstrap.Component
24541  * Bootstrap MenuItem class
24542  * @cfg {Boolean} submenu (true | false) default false
24543  * @cfg {String} html text of the item
24544  * @cfg {String} href the link
24545  * @cfg {Boolean} disable (true | false) default false
24546  * @cfg {Boolean} preventDefault (true | false) default true
24547  * @cfg {String} icon Font awesome icon
24548  * @cfg {String} pos Submenu align to (left | right) default right 
24549  * 
24550  * 
24551  * @constructor
24552  * Create a new Item
24553  * @param {Object} config The config object
24554  */
24555
24556
24557 Roo.bootstrap.menu.Item = function(config){
24558     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
24559     this.addEvents({
24560         /**
24561          * @event mouseover
24562          * Fires when the mouse is hovering over this menu
24563          * @param {Roo.bootstrap.menu.Item} this
24564          * @param {Roo.EventObject} e
24565          */
24566         mouseover : true,
24567         /**
24568          * @event mouseout
24569          * Fires when the mouse exits this menu
24570          * @param {Roo.bootstrap.menu.Item} this
24571          * @param {Roo.EventObject} e
24572          */
24573         mouseout : true,
24574         // raw events
24575         /**
24576          * @event click
24577          * The raw click event for the entire grid.
24578          * @param {Roo.EventObject} e
24579          */
24580         click : true
24581     });
24582 };
24583
24584 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
24585     
24586     submenu : false,
24587     href : '',
24588     html : '',
24589     preventDefault: true,
24590     disable : false,
24591     icon : false,
24592     pos : 'right',
24593     
24594     getAutoCreate : function()
24595     {
24596         var text = [
24597             {
24598                 tag : 'span',
24599                 cls : 'roo-menu-item-text',
24600                 html : this.html
24601             }
24602         ];
24603         
24604         if(this.icon){
24605             text.unshift({
24606                 tag : 'i',
24607                 cls : 'fa ' + this.icon
24608             })
24609         }
24610         
24611         var cfg = {
24612             tag : 'li',
24613             cn : [
24614                 {
24615                     tag : 'a',
24616                     href : this.href || '#',
24617                     cn : text
24618                 }
24619             ]
24620         };
24621         
24622         if(this.disable){
24623             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
24624         }
24625         
24626         if(this.submenu){
24627             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
24628             
24629             if(this.pos == 'left'){
24630                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
24631             }
24632         }
24633         
24634         return cfg;
24635     },
24636     
24637     initEvents : function() 
24638     {
24639         this.el.on('mouseover', this.onMouseOver, this);
24640         this.el.on('mouseout', this.onMouseOut, this);
24641         
24642         this.el.select('a', true).first().on('click', this.onClick, this);
24643         
24644     },
24645     
24646     onClick : function(e)
24647     {
24648         if(this.preventDefault){
24649             e.preventDefault();
24650         }
24651         
24652         this.fireEvent("click", this, e);
24653     },
24654     
24655     onMouseOver : function(e)
24656     {
24657         if(this.submenu && this.pos == 'left'){
24658             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
24659         }
24660         
24661         this.fireEvent("mouseover", this, e);
24662     },
24663     
24664     onMouseOut : function(e)
24665     {
24666         this.fireEvent("mouseout", this, e);
24667     }
24668 });
24669
24670  
24671
24672  /*
24673  * - LGPL
24674  *
24675  * menu separator
24676  * 
24677  */
24678 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24679
24680 /**
24681  * @class Roo.bootstrap.menu.Separator
24682  * @extends Roo.bootstrap.Component
24683  * Bootstrap Separator class
24684  * 
24685  * @constructor
24686  * Create a new Separator
24687  * @param {Object} config The config object
24688  */
24689
24690
24691 Roo.bootstrap.menu.Separator = function(config){
24692     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
24693 };
24694
24695 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
24696     
24697     getAutoCreate : function(){
24698         var cfg = {
24699             tag : 'li',
24700             cls: 'divider'
24701         };
24702         
24703         return cfg;
24704     }
24705    
24706 });
24707
24708  
24709
24710  /*
24711  * - LGPL
24712  *
24713  * Tooltip
24714  * 
24715  */
24716
24717 /**
24718  * @class Roo.bootstrap.Tooltip
24719  * Bootstrap Tooltip class
24720  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
24721  * to determine which dom element triggers the tooltip.
24722  * 
24723  * It needs to add support for additional attributes like tooltip-position
24724  * 
24725  * @constructor
24726  * Create a new Toolti
24727  * @param {Object} config The config object
24728  */
24729
24730 Roo.bootstrap.Tooltip = function(config){
24731     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
24732 };
24733
24734 Roo.apply(Roo.bootstrap.Tooltip, {
24735     /**
24736      * @function init initialize tooltip monitoring.
24737      * @static
24738      */
24739     currentEl : false,
24740     currentTip : false,
24741     currentRegion : false,
24742     
24743     //  init : delay?
24744     
24745     init : function()
24746     {
24747         Roo.get(document).on('mouseover', this.enter ,this);
24748         Roo.get(document).on('mouseout', this.leave, this);
24749          
24750         
24751         this.currentTip = new Roo.bootstrap.Tooltip();
24752     },
24753     
24754     enter : function(ev)
24755     {
24756         var dom = ev.getTarget();
24757         
24758         //Roo.log(['enter',dom]);
24759         var el = Roo.fly(dom);
24760         if (this.currentEl) {
24761             //Roo.log(dom);
24762             //Roo.log(this.currentEl);
24763             //Roo.log(this.currentEl.contains(dom));
24764             if (this.currentEl == el) {
24765                 return;
24766             }
24767             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24768                 return;
24769             }
24770
24771         }
24772         
24773         if (this.currentTip.el) {
24774             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24775         }    
24776         //Roo.log(ev);
24777         
24778         if(!el || el.dom == document){
24779             return;
24780         }
24781         
24782         var bindEl = el;
24783         
24784         // you can not look for children, as if el is the body.. then everythign is the child..
24785         if (!el.attr('tooltip')) { //
24786             if (!el.select("[tooltip]").elements.length) {
24787                 return;
24788             }
24789             // is the mouse over this child...?
24790             bindEl = el.select("[tooltip]").first();
24791             var xy = ev.getXY();
24792             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24793                 //Roo.log("not in region.");
24794                 return;
24795             }
24796             //Roo.log("child element over..");
24797             
24798         }
24799         this.currentEl = bindEl;
24800         this.currentTip.bind(bindEl);
24801         this.currentRegion = Roo.lib.Region.getRegion(dom);
24802         this.currentTip.enter();
24803         
24804     },
24805     leave : function(ev)
24806     {
24807         var dom = ev.getTarget();
24808         //Roo.log(['leave',dom]);
24809         if (!this.currentEl) {
24810             return;
24811         }
24812         
24813         
24814         if (dom != this.currentEl.dom) {
24815             return;
24816         }
24817         var xy = ev.getXY();
24818         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
24819             return;
24820         }
24821         // only activate leave if mouse cursor is outside... bounding box..
24822         
24823         
24824         
24825         
24826         if (this.currentTip) {
24827             this.currentTip.leave();
24828         }
24829         //Roo.log('clear currentEl');
24830         this.currentEl = false;
24831         
24832         
24833     },
24834     alignment : {
24835         'left' : ['r-l', [-2,0], 'right'],
24836         'right' : ['l-r', [2,0], 'left'],
24837         'bottom' : ['t-b', [0,2], 'top'],
24838         'top' : [ 'b-t', [0,-2], 'bottom']
24839     }
24840     
24841 });
24842
24843
24844 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
24845     
24846     
24847     bindEl : false,
24848     
24849     delay : null, // can be { show : 300 , hide: 500}
24850     
24851     timeout : null,
24852     
24853     hoverState : null, //???
24854     
24855     placement : 'bottom', 
24856     
24857     getAutoCreate : function(){
24858     
24859         var cfg = {
24860            cls : 'tooltip',
24861            role : 'tooltip',
24862            cn : [
24863                 {
24864                     cls : 'tooltip-arrow'
24865                 },
24866                 {
24867                     cls : 'tooltip-inner'
24868                 }
24869            ]
24870         };
24871         
24872         return cfg;
24873     },
24874     bind : function(el)
24875     {
24876         this.bindEl = el;
24877     },
24878       
24879     
24880     enter : function () {
24881        
24882         if (this.timeout != null) {
24883             clearTimeout(this.timeout);
24884         }
24885         
24886         this.hoverState = 'in';
24887          //Roo.log("enter - show");
24888         if (!this.delay || !this.delay.show) {
24889             this.show();
24890             return;
24891         }
24892         var _t = this;
24893         this.timeout = setTimeout(function () {
24894             if (_t.hoverState == 'in') {
24895                 _t.show();
24896             }
24897         }, this.delay.show);
24898     },
24899     leave : function()
24900     {
24901         clearTimeout(this.timeout);
24902     
24903         this.hoverState = 'out';
24904          if (!this.delay || !this.delay.hide) {
24905             this.hide();
24906             return;
24907         }
24908        
24909         var _t = this;
24910         this.timeout = setTimeout(function () {
24911             //Roo.log("leave - timeout");
24912             
24913             if (_t.hoverState == 'out') {
24914                 _t.hide();
24915                 Roo.bootstrap.Tooltip.currentEl = false;
24916             }
24917         }, delay);
24918     },
24919     
24920     show : function (msg)
24921     {
24922         if (!this.el) {
24923             this.render(document.body);
24924         }
24925         // set content.
24926         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24927         
24928         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24929         
24930         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24931         
24932         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24933         
24934         var placement = typeof this.placement == 'function' ?
24935             this.placement.call(this, this.el, on_el) :
24936             this.placement;
24937             
24938         var autoToken = /\s?auto?\s?/i;
24939         var autoPlace = autoToken.test(placement);
24940         if (autoPlace) {
24941             placement = placement.replace(autoToken, '') || 'top';
24942         }
24943         
24944         //this.el.detach()
24945         //this.el.setXY([0,0]);
24946         this.el.show();
24947         //this.el.dom.style.display='block';
24948         
24949         //this.el.appendTo(on_el);
24950         
24951         var p = this.getPosition();
24952         var box = this.el.getBox();
24953         
24954         if (autoPlace) {
24955             // fixme..
24956         }
24957         
24958         var align = this.alignment ? this.alignment[placement] : Roo.bootstrap.Tooltip.alignment[placement];
24959         
24960         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
24961         
24962         if(placement == 'top' || placement == 'bottom'){
24963             if(xy[0] < 0){
24964                 placement = 'right';
24965             }
24966             
24967             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
24968                 placement = 'left';
24969             }
24970             
24971             var scroll = Roo.select('body', true).first().getScroll();
24972             
24973             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
24974                 placement = 'top';
24975             }
24976             
24977         }
24978         
24979         this.el.alignTo(this.bindEl, align[0],align[1]);
24980         //var arrow = this.el.select('.arrow',true).first();
24981         //arrow.set(align[2], 
24982         
24983         this.el.addClass(placement);
24984         
24985         this.el.addClass('in fade');
24986         
24987         this.hoverState = null;
24988         
24989         if (this.el.hasClass('fade')) {
24990             // fade it?
24991         }
24992         
24993     },
24994     hide : function()
24995     {
24996          
24997         if (!this.el) {
24998             return;
24999         }
25000         //this.el.setXY([0,0]);
25001         this.el.removeClass('in');
25002         //this.el.hide();
25003         
25004     }
25005     
25006 });
25007  
25008
25009  /*
25010  * - LGPL
25011  *
25012  * Location Picker
25013  * 
25014  */
25015
25016 /**
25017  * @class Roo.bootstrap.LocationPicker
25018  * @extends Roo.bootstrap.Component
25019  * Bootstrap LocationPicker class
25020  * @cfg {Number} latitude Position when init default 0
25021  * @cfg {Number} longitude Position when init default 0
25022  * @cfg {Number} zoom default 15
25023  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25024  * @cfg {Boolean} mapTypeControl default false
25025  * @cfg {Boolean} disableDoubleClickZoom default false
25026  * @cfg {Boolean} scrollwheel default true
25027  * @cfg {Boolean} streetViewControl default false
25028  * @cfg {Number} radius default 0
25029  * @cfg {String} locationName
25030  * @cfg {Boolean} draggable default true
25031  * @cfg {Boolean} enableAutocomplete default false
25032  * @cfg {Boolean} enableReverseGeocode default true
25033  * @cfg {String} markerTitle
25034  * 
25035  * @constructor
25036  * Create a new LocationPicker
25037  * @param {Object} config The config object
25038  */
25039
25040
25041 Roo.bootstrap.LocationPicker = function(config){
25042     
25043     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25044     
25045     this.addEvents({
25046         /**
25047          * @event initial
25048          * Fires when the picker initialized.
25049          * @param {Roo.bootstrap.LocationPicker} this
25050          * @param {Google Location} location
25051          */
25052         initial : true,
25053         /**
25054          * @event positionchanged
25055          * Fires when the picker position changed.
25056          * @param {Roo.bootstrap.LocationPicker} this
25057          * @param {Google Location} location
25058          */
25059         positionchanged : true,
25060         /**
25061          * @event resize
25062          * Fires when the map resize.
25063          * @param {Roo.bootstrap.LocationPicker} this
25064          */
25065         resize : true,
25066         /**
25067          * @event show
25068          * Fires when the map show.
25069          * @param {Roo.bootstrap.LocationPicker} this
25070          */
25071         show : true,
25072         /**
25073          * @event hide
25074          * Fires when the map hide.
25075          * @param {Roo.bootstrap.LocationPicker} this
25076          */
25077         hide : true,
25078         /**
25079          * @event mapClick
25080          * Fires when click the map.
25081          * @param {Roo.bootstrap.LocationPicker} this
25082          * @param {Map event} e
25083          */
25084         mapClick : true,
25085         /**
25086          * @event mapRightClick
25087          * Fires when right click the map.
25088          * @param {Roo.bootstrap.LocationPicker} this
25089          * @param {Map event} e
25090          */
25091         mapRightClick : true,
25092         /**
25093          * @event markerClick
25094          * Fires when click the marker.
25095          * @param {Roo.bootstrap.LocationPicker} this
25096          * @param {Map event} e
25097          */
25098         markerClick : true,
25099         /**
25100          * @event markerRightClick
25101          * Fires when right click the marker.
25102          * @param {Roo.bootstrap.LocationPicker} this
25103          * @param {Map event} e
25104          */
25105         markerRightClick : true,
25106         /**
25107          * @event OverlayViewDraw
25108          * Fires when OverlayView Draw
25109          * @param {Roo.bootstrap.LocationPicker} this
25110          */
25111         OverlayViewDraw : true,
25112         /**
25113          * @event OverlayViewOnAdd
25114          * Fires when OverlayView Draw
25115          * @param {Roo.bootstrap.LocationPicker} this
25116          */
25117         OverlayViewOnAdd : true,
25118         /**
25119          * @event OverlayViewOnRemove
25120          * Fires when OverlayView Draw
25121          * @param {Roo.bootstrap.LocationPicker} this
25122          */
25123         OverlayViewOnRemove : true,
25124         /**
25125          * @event OverlayViewShow
25126          * Fires when OverlayView Draw
25127          * @param {Roo.bootstrap.LocationPicker} this
25128          * @param {Pixel} cpx
25129          */
25130         OverlayViewShow : true,
25131         /**
25132          * @event OverlayViewHide
25133          * Fires when OverlayView Draw
25134          * @param {Roo.bootstrap.LocationPicker} this
25135          */
25136         OverlayViewHide : true,
25137         /**
25138          * @event loadexception
25139          * Fires when load google lib failed.
25140          * @param {Roo.bootstrap.LocationPicker} this
25141          */
25142         loadexception : true
25143     });
25144         
25145 };
25146
25147 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
25148     
25149     gMapContext: false,
25150     
25151     latitude: 0,
25152     longitude: 0,
25153     zoom: 15,
25154     mapTypeId: false,
25155     mapTypeControl: false,
25156     disableDoubleClickZoom: false,
25157     scrollwheel: true,
25158     streetViewControl: false,
25159     radius: 0,
25160     locationName: '',
25161     draggable: true,
25162     enableAutocomplete: false,
25163     enableReverseGeocode: true,
25164     markerTitle: '',
25165     
25166     getAutoCreate: function()
25167     {
25168
25169         var cfg = {
25170             tag: 'div',
25171             cls: 'roo-location-picker'
25172         };
25173         
25174         return cfg
25175     },
25176     
25177     initEvents: function(ct, position)
25178     {       
25179         if(!this.el.getWidth() || this.isApplied()){
25180             return;
25181         }
25182         
25183         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25184         
25185         this.initial();
25186     },
25187     
25188     initial: function()
25189     {
25190         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
25191             this.fireEvent('loadexception', this);
25192             return;
25193         }
25194         
25195         if(!this.mapTypeId){
25196             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
25197         }
25198         
25199         this.gMapContext = this.GMapContext();
25200         
25201         this.initOverlayView();
25202         
25203         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
25204         
25205         var _this = this;
25206                 
25207         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
25208             _this.setPosition(_this.gMapContext.marker.position);
25209         });
25210         
25211         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
25212             _this.fireEvent('mapClick', this, event);
25213             
25214         });
25215
25216         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
25217             _this.fireEvent('mapRightClick', this, event);
25218             
25219         });
25220         
25221         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
25222             _this.fireEvent('markerClick', this, event);
25223             
25224         });
25225
25226         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
25227             _this.fireEvent('markerRightClick', this, event);
25228             
25229         });
25230         
25231         this.setPosition(this.gMapContext.location);
25232         
25233         this.fireEvent('initial', this, this.gMapContext.location);
25234     },
25235     
25236     initOverlayView: function()
25237     {
25238         var _this = this;
25239         
25240         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
25241             
25242             draw: function()
25243             {
25244                 _this.fireEvent('OverlayViewDraw', _this);
25245             },
25246             
25247             onAdd: function()
25248             {
25249                 _this.fireEvent('OverlayViewOnAdd', _this);
25250             },
25251             
25252             onRemove: function()
25253             {
25254                 _this.fireEvent('OverlayViewOnRemove', _this);
25255             },
25256             
25257             show: function(cpx)
25258             {
25259                 _this.fireEvent('OverlayViewShow', _this, cpx);
25260             },
25261             
25262             hide: function()
25263             {
25264                 _this.fireEvent('OverlayViewHide', _this);
25265             }
25266             
25267         });
25268     },
25269     
25270     fromLatLngToContainerPixel: function(event)
25271     {
25272         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
25273     },
25274     
25275     isApplied: function() 
25276     {
25277         return this.getGmapContext() == false ? false : true;
25278     },
25279     
25280     getGmapContext: function() 
25281     {
25282         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
25283     },
25284     
25285     GMapContext: function() 
25286     {
25287         var position = new google.maps.LatLng(this.latitude, this.longitude);
25288         
25289         var _map = new google.maps.Map(this.el.dom, {
25290             center: position,
25291             zoom: this.zoom,
25292             mapTypeId: this.mapTypeId,
25293             mapTypeControl: this.mapTypeControl,
25294             disableDoubleClickZoom: this.disableDoubleClickZoom,
25295             scrollwheel: this.scrollwheel,
25296             streetViewControl: this.streetViewControl,
25297             locationName: this.locationName,
25298             draggable: this.draggable,
25299             enableAutocomplete: this.enableAutocomplete,
25300             enableReverseGeocode: this.enableReverseGeocode
25301         });
25302         
25303         var _marker = new google.maps.Marker({
25304             position: position,
25305             map: _map,
25306             title: this.markerTitle,
25307             draggable: this.draggable
25308         });
25309         
25310         return {
25311             map: _map,
25312             marker: _marker,
25313             circle: null,
25314             location: position,
25315             radius: this.radius,
25316             locationName: this.locationName,
25317             addressComponents: {
25318                 formatted_address: null,
25319                 addressLine1: null,
25320                 addressLine2: null,
25321                 streetName: null,
25322                 streetNumber: null,
25323                 city: null,
25324                 district: null,
25325                 state: null,
25326                 stateOrProvince: null
25327             },
25328             settings: this,
25329             domContainer: this.el.dom,
25330             geodecoder: new google.maps.Geocoder()
25331         };
25332     },
25333     
25334     drawCircle: function(center, radius, options) 
25335     {
25336         if (this.gMapContext.circle != null) {
25337             this.gMapContext.circle.setMap(null);
25338         }
25339         if (radius > 0) {
25340             radius *= 1;
25341             options = Roo.apply({}, options, {
25342                 strokeColor: "#0000FF",
25343                 strokeOpacity: .35,
25344                 strokeWeight: 2,
25345                 fillColor: "#0000FF",
25346                 fillOpacity: .2
25347             });
25348             
25349             options.map = this.gMapContext.map;
25350             options.radius = radius;
25351             options.center = center;
25352             this.gMapContext.circle = new google.maps.Circle(options);
25353             return this.gMapContext.circle;
25354         }
25355         
25356         return null;
25357     },
25358     
25359     setPosition: function(location) 
25360     {
25361         this.gMapContext.location = location;
25362         this.gMapContext.marker.setPosition(location);
25363         this.gMapContext.map.panTo(location);
25364         this.drawCircle(location, this.gMapContext.radius, {});
25365         
25366         var _this = this;
25367         
25368         if (this.gMapContext.settings.enableReverseGeocode) {
25369             this.gMapContext.geodecoder.geocode({
25370                 latLng: this.gMapContext.location
25371             }, function(results, status) {
25372                 
25373                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
25374                     _this.gMapContext.locationName = results[0].formatted_address;
25375                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
25376                     
25377                     _this.fireEvent('positionchanged', this, location);
25378                 }
25379             });
25380             
25381             return;
25382         }
25383         
25384         this.fireEvent('positionchanged', this, location);
25385     },
25386     
25387     resize: function()
25388     {
25389         google.maps.event.trigger(this.gMapContext.map, "resize");
25390         
25391         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
25392         
25393         this.fireEvent('resize', this);
25394     },
25395     
25396     setPositionByLatLng: function(latitude, longitude)
25397     {
25398         this.setPosition(new google.maps.LatLng(latitude, longitude));
25399     },
25400     
25401     getCurrentPosition: function() 
25402     {
25403         return {
25404             latitude: this.gMapContext.location.lat(),
25405             longitude: this.gMapContext.location.lng()
25406         };
25407     },
25408     
25409     getAddressName: function() 
25410     {
25411         return this.gMapContext.locationName;
25412     },
25413     
25414     getAddressComponents: function() 
25415     {
25416         return this.gMapContext.addressComponents;
25417     },
25418     
25419     address_component_from_google_geocode: function(address_components) 
25420     {
25421         var result = {};
25422         
25423         for (var i = 0; i < address_components.length; i++) {
25424             var component = address_components[i];
25425             if (component.types.indexOf("postal_code") >= 0) {
25426                 result.postalCode = component.short_name;
25427             } else if (component.types.indexOf("street_number") >= 0) {
25428                 result.streetNumber = component.short_name;
25429             } else if (component.types.indexOf("route") >= 0) {
25430                 result.streetName = component.short_name;
25431             } else if (component.types.indexOf("neighborhood") >= 0) {
25432                 result.city = component.short_name;
25433             } else if (component.types.indexOf("locality") >= 0) {
25434                 result.city = component.short_name;
25435             } else if (component.types.indexOf("sublocality") >= 0) {
25436                 result.district = component.short_name;
25437             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
25438                 result.stateOrProvince = component.short_name;
25439             } else if (component.types.indexOf("country") >= 0) {
25440                 result.country = component.short_name;
25441             }
25442         }
25443         
25444         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
25445         result.addressLine2 = "";
25446         return result;
25447     },
25448     
25449     setZoomLevel: function(zoom)
25450     {
25451         this.gMapContext.map.setZoom(zoom);
25452     },
25453     
25454     show: function()
25455     {
25456         if(!this.el){
25457             return;
25458         }
25459         
25460         this.el.show();
25461         
25462         this.resize();
25463         
25464         this.fireEvent('show', this);
25465     },
25466     
25467     hide: function()
25468     {
25469         if(!this.el){
25470             return;
25471         }
25472         
25473         this.el.hide();
25474         
25475         this.fireEvent('hide', this);
25476     }
25477     
25478 });
25479
25480 Roo.apply(Roo.bootstrap.LocationPicker, {
25481     
25482     OverlayView : function(map, options)
25483     {
25484         options = options || {};
25485         
25486         this.setMap(map);
25487     }
25488     
25489     
25490 });/*
25491  * - LGPL
25492  *
25493  * Alert
25494  * 
25495  */
25496
25497 /**
25498  * @class Roo.bootstrap.Alert
25499  * @extends Roo.bootstrap.Component
25500  * Bootstrap Alert class
25501  * @cfg {String} title The title of alert
25502  * @cfg {String} html The content of alert
25503  * @cfg {String} weight (  success | info | warning | danger )
25504  * @cfg {String} faicon font-awesomeicon
25505  * 
25506  * @constructor
25507  * Create a new alert
25508  * @param {Object} config The config object
25509  */
25510
25511
25512 Roo.bootstrap.Alert = function(config){
25513     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
25514     
25515 };
25516
25517 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
25518     
25519     title: '',
25520     html: '',
25521     weight: false,
25522     faicon: false,
25523     
25524     getAutoCreate : function()
25525     {
25526         
25527         var cfg = {
25528             tag : 'div',
25529             cls : 'alert',
25530             cn : [
25531                 {
25532                     tag : 'i',
25533                     cls : 'roo-alert-icon'
25534                     
25535                 },
25536                 {
25537                     tag : 'b',
25538                     cls : 'roo-alert-title',
25539                     html : this.title
25540                 },
25541                 {
25542                     tag : 'span',
25543                     cls : 'roo-alert-text',
25544                     html : this.html
25545                 }
25546             ]
25547         };
25548         
25549         if(this.faicon){
25550             cfg.cn[0].cls += ' fa ' + this.faicon;
25551         }
25552         
25553         if(this.weight){
25554             cfg.cls += ' alert-' + this.weight;
25555         }
25556         
25557         return cfg;
25558     },
25559     
25560     initEvents: function() 
25561     {
25562         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25563     },
25564     
25565     setTitle : function(str)
25566     {
25567         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
25568     },
25569     
25570     setText : function(str)
25571     {
25572         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
25573     },
25574     
25575     setWeight : function(weight)
25576     {
25577         if(this.weight){
25578             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
25579         }
25580         
25581         this.weight = weight;
25582         
25583         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
25584     },
25585     
25586     setIcon : function(icon)
25587     {
25588         if(this.faicon){
25589             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
25590         }
25591         
25592         this.faicon = icon;
25593         
25594         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
25595     },
25596     
25597     hide: function() 
25598     {
25599         this.el.hide();   
25600     },
25601     
25602     show: function() 
25603     {  
25604         this.el.show();   
25605     }
25606     
25607 });
25608
25609  
25610 /*
25611 * Licence: LGPL
25612 */
25613
25614 /**
25615  * @class Roo.bootstrap.UploadCropbox
25616  * @extends Roo.bootstrap.Component
25617  * Bootstrap UploadCropbox class
25618  * @cfg {String} emptyText show when image has been loaded
25619  * @cfg {String} rotateNotify show when image too small to rotate
25620  * @cfg {Number} errorTimeout default 3000
25621  * @cfg {Number} minWidth default 300
25622  * @cfg {Number} minHeight default 300
25623  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
25624  * @cfg {Boolean} isDocument (true|false) default false
25625  * @cfg {String} url action url
25626  * @cfg {String} paramName default 'imageUpload'
25627  * @cfg {String} method default POST
25628  * @cfg {Boolean} loadMask (true|false) default true
25629  * @cfg {Boolean} loadingText default 'Loading...'
25630  * 
25631  * @constructor
25632  * Create a new UploadCropbox
25633  * @param {Object} config The config object
25634  */
25635
25636 Roo.bootstrap.UploadCropbox = function(config){
25637     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
25638     
25639     this.addEvents({
25640         /**
25641          * @event beforeselectfile
25642          * Fire before select file
25643          * @param {Roo.bootstrap.UploadCropbox} this
25644          */
25645         "beforeselectfile" : true,
25646         /**
25647          * @event initial
25648          * Fire after initEvent
25649          * @param {Roo.bootstrap.UploadCropbox} this
25650          */
25651         "initial" : true,
25652         /**
25653          * @event crop
25654          * Fire after initEvent
25655          * @param {Roo.bootstrap.UploadCropbox} this
25656          * @param {String} data
25657          */
25658         "crop" : true,
25659         /**
25660          * @event prepare
25661          * Fire when preparing the file data
25662          * @param {Roo.bootstrap.UploadCropbox} this
25663          * @param {Object} file
25664          */
25665         "prepare" : true,
25666         /**
25667          * @event exception
25668          * Fire when get exception
25669          * @param {Roo.bootstrap.UploadCropbox} this
25670          * @param {XMLHttpRequest} xhr
25671          */
25672         "exception" : true,
25673         /**
25674          * @event beforeloadcanvas
25675          * Fire before load the canvas
25676          * @param {Roo.bootstrap.UploadCropbox} this
25677          * @param {String} src
25678          */
25679         "beforeloadcanvas" : true,
25680         /**
25681          * @event trash
25682          * Fire when trash image
25683          * @param {Roo.bootstrap.UploadCropbox} this
25684          */
25685         "trash" : true,
25686         /**
25687          * @event download
25688          * Fire when download the image
25689          * @param {Roo.bootstrap.UploadCropbox} this
25690          */
25691         "download" : true,
25692         /**
25693          * @event footerbuttonclick
25694          * Fire when footerbuttonclick
25695          * @param {Roo.bootstrap.UploadCropbox} this
25696          * @param {String} type
25697          */
25698         "footerbuttonclick" : true,
25699         /**
25700          * @event resize
25701          * Fire when resize
25702          * @param {Roo.bootstrap.UploadCropbox} this
25703          */
25704         "resize" : true,
25705         /**
25706          * @event rotate
25707          * Fire when rotate the image
25708          * @param {Roo.bootstrap.UploadCropbox} this
25709          * @param {String} pos
25710          */
25711         "rotate" : true,
25712         /**
25713          * @event inspect
25714          * Fire when inspect the file
25715          * @param {Roo.bootstrap.UploadCropbox} this
25716          * @param {Object} file
25717          */
25718         "inspect" : true,
25719         /**
25720          * @event upload
25721          * Fire when xhr upload the file
25722          * @param {Roo.bootstrap.UploadCropbox} this
25723          * @param {Object} data
25724          */
25725         "upload" : true,
25726         /**
25727          * @event arrange
25728          * Fire when arrange the file data
25729          * @param {Roo.bootstrap.UploadCropbox} this
25730          * @param {Object} formData
25731          */
25732         "arrange" : true
25733     });
25734     
25735     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25736 };
25737
25738 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
25739     
25740     emptyText : 'Click to upload image',
25741     rotateNotify : 'Image is too small to rotate',
25742     errorTimeout : 3000,
25743     scale : 0,
25744     baseScale : 1,
25745     rotate : 0,
25746     dragable : false,
25747     pinching : false,
25748     mouseX : 0,
25749     mouseY : 0,
25750     cropData : false,
25751     minWidth : 300,
25752     minHeight : 300,
25753     file : false,
25754     exif : {},
25755     baseRotate : 1,
25756     cropType : 'image/jpeg',
25757     buttons : false,
25758     canvasLoaded : false,
25759     isDocument : false,
25760     method : 'POST',
25761     paramName : 'imageUpload',
25762     loadMask : true,
25763     loadingText : 'Loading...',
25764     maskEl : false,
25765     
25766     getAutoCreate : function()
25767     {
25768         var cfg = {
25769             tag : 'div',
25770             cls : 'roo-upload-cropbox',
25771             cn : [
25772                 {
25773                     tag : 'input',
25774                     cls : 'roo-upload-cropbox-selector',
25775                     type : 'file'
25776                 },
25777                 {
25778                     tag : 'div',
25779                     cls : 'roo-upload-cropbox-body',
25780                     style : 'cursor:pointer',
25781                     cn : [
25782                         {
25783                             tag : 'div',
25784                             cls : 'roo-upload-cropbox-preview'
25785                         },
25786                         {
25787                             tag : 'div',
25788                             cls : 'roo-upload-cropbox-thumb'
25789                         },
25790                         {
25791                             tag : 'div',
25792                             cls : 'roo-upload-cropbox-empty-notify',
25793                             html : this.emptyText
25794                         },
25795                         {
25796                             tag : 'div',
25797                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25798                             html : this.rotateNotify
25799                         }
25800                     ]
25801                 },
25802                 {
25803                     tag : 'div',
25804                     cls : 'roo-upload-cropbox-footer',
25805                     cn : {
25806                         tag : 'div',
25807                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25808                         cn : []
25809                     }
25810                 }
25811             ]
25812         };
25813         
25814         return cfg;
25815     },
25816     
25817     onRender : function(ct, position)
25818     {
25819         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25820         
25821         if (this.buttons.length) {
25822             
25823             Roo.each(this.buttons, function(bb) {
25824                 
25825                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25826                 
25827                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25828                 
25829             }, this);
25830         }
25831         
25832         if(this.loadMask){
25833             this.maskEl = this.el;
25834         }
25835     },
25836     
25837     initEvents : function()
25838     {
25839         this.urlAPI = (window.createObjectURL && window) || 
25840                                 (window.URL && URL.revokeObjectURL && URL) || 
25841                                 (window.webkitURL && webkitURL);
25842                         
25843         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25844         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25845         
25846         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25847         this.selectorEl.hide();
25848         
25849         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25850         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25851         
25852         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25853         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25854         this.thumbEl.hide();
25855         
25856         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25857         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25858         
25859         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25860         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25861         this.errorEl.hide();
25862         
25863         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25864         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25865         this.footerEl.hide();
25866         
25867         this.setThumbBoxSize();
25868         
25869         this.bind();
25870         
25871         this.resize();
25872         
25873         this.fireEvent('initial', this);
25874     },
25875
25876     bind : function()
25877     {
25878         var _this = this;
25879         
25880         window.addEventListener("resize", function() { _this.resize(); } );
25881         
25882         this.bodyEl.on('click', this.beforeSelectFile, this);
25883         
25884         if(Roo.isTouch){
25885             this.bodyEl.on('touchstart', this.onTouchStart, this);
25886             this.bodyEl.on('touchmove', this.onTouchMove, this);
25887             this.bodyEl.on('touchend', this.onTouchEnd, this);
25888         }
25889         
25890         if(!Roo.isTouch){
25891             this.bodyEl.on('mousedown', this.onMouseDown, this);
25892             this.bodyEl.on('mousemove', this.onMouseMove, this);
25893             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25894             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25895             Roo.get(document).on('mouseup', this.onMouseUp, this);
25896         }
25897         
25898         this.selectorEl.on('change', this.onFileSelected, this);
25899     },
25900     
25901     reset : function()
25902     {    
25903         this.scale = 0;
25904         this.baseScale = 1;
25905         this.rotate = 0;
25906         this.baseRotate = 1;
25907         this.dragable = false;
25908         this.pinching = false;
25909         this.mouseX = 0;
25910         this.mouseY = 0;
25911         this.cropData = false;
25912         this.notifyEl.dom.innerHTML = this.emptyText;
25913         
25914         this.selectorEl.dom.value = '';
25915         
25916     },
25917     
25918     resize : function()
25919     {
25920         if(this.fireEvent('resize', this) != false){
25921             this.setThumbBoxPosition();
25922             this.setCanvasPosition();
25923         }
25924     },
25925     
25926     onFooterButtonClick : function(e, el, o, type)
25927     {
25928         switch (type) {
25929             case 'rotate-left' :
25930                 this.onRotateLeft(e);
25931                 break;
25932             case 'rotate-right' :
25933                 this.onRotateRight(e);
25934                 break;
25935             case 'picture' :
25936                 this.beforeSelectFile(e);
25937                 break;
25938             case 'trash' :
25939                 this.trash(e);
25940                 break;
25941             case 'crop' :
25942                 this.crop(e);
25943                 break;
25944             case 'download' :
25945                 this.download(e);
25946                 break;
25947             default :
25948                 break;
25949         }
25950         
25951         this.fireEvent('footerbuttonclick', this, type);
25952     },
25953     
25954     beforeSelectFile : function(e)
25955     {
25956         e.preventDefault();
25957         
25958         if(this.fireEvent('beforeselectfile', this) != false){
25959             this.selectorEl.dom.click();
25960         }
25961     },
25962     
25963     onFileSelected : function(e)
25964     {
25965         e.preventDefault();
25966         
25967         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25968             return;
25969         }
25970         
25971         var file = this.selectorEl.dom.files[0];
25972         
25973         if(this.fireEvent('inspect', this, file) != false){
25974             this.prepare(file);
25975         }
25976         
25977     },
25978     
25979     trash : function(e)
25980     {
25981         this.fireEvent('trash', this);
25982     },
25983     
25984     download : function(e)
25985     {
25986         this.fireEvent('download', this);
25987     },
25988     
25989     loadCanvas : function(src)
25990     {   
25991         if(this.fireEvent('beforeloadcanvas', this, src) != false){
25992             
25993             this.reset();
25994             
25995             this.imageEl = document.createElement('img');
25996             
25997             var _this = this;
25998             
25999             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26000             
26001             this.imageEl.src = src;
26002         }
26003     },
26004     
26005     onLoadCanvas : function()
26006     {   
26007         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26008         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26009         
26010         this.bodyEl.un('click', this.beforeSelectFile, this);
26011         
26012         this.notifyEl.hide();
26013         this.thumbEl.show();
26014         this.footerEl.show();
26015         
26016         this.baseRotateLevel();
26017         
26018         if(this.isDocument){
26019             this.setThumbBoxSize();
26020         }
26021         
26022         this.setThumbBoxPosition();
26023         
26024         this.baseScaleLevel();
26025         
26026         this.draw();
26027         
26028         this.resize();
26029         
26030         this.canvasLoaded = true;
26031         
26032         if(this.loadMask){
26033             this.maskEl.unmask();
26034         }
26035         
26036     },
26037     
26038     setCanvasPosition : function()
26039     {   
26040         if(!this.canvasEl){
26041             return;
26042         }
26043         
26044         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26045         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26046         
26047         this.previewEl.setLeft(pw);
26048         this.previewEl.setTop(ph);
26049         
26050     },
26051     
26052     onMouseDown : function(e)
26053     {   
26054         e.stopEvent();
26055         
26056         this.dragable = true;
26057         this.pinching = false;
26058         
26059         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26060             this.dragable = false;
26061             return;
26062         }
26063         
26064         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26065         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26066         
26067     },
26068     
26069     onMouseMove : function(e)
26070     {   
26071         e.stopEvent();
26072         
26073         if(!this.canvasLoaded){
26074             return;
26075         }
26076         
26077         if (!this.dragable){
26078             return;
26079         }
26080         
26081         var minX = Math.ceil(this.thumbEl.getLeft(true));
26082         var minY = Math.ceil(this.thumbEl.getTop(true));
26083         
26084         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
26085         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
26086         
26087         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26088         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26089         
26090         x = x - this.mouseX;
26091         y = y - this.mouseY;
26092         
26093         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
26094         var bgY = Math.ceil(y + this.previewEl.getTop(true));
26095         
26096         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
26097         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
26098         
26099         this.previewEl.setLeft(bgX);
26100         this.previewEl.setTop(bgY);
26101         
26102         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26103         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26104     },
26105     
26106     onMouseUp : function(e)
26107     {   
26108         e.stopEvent();
26109         
26110         this.dragable = false;
26111     },
26112     
26113     onMouseWheel : function(e)
26114     {   
26115         e.stopEvent();
26116         
26117         this.startScale = this.scale;
26118         
26119         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
26120         
26121         if(!this.zoomable()){
26122             this.scale = this.startScale;
26123             return;
26124         }
26125         
26126         this.draw();
26127         
26128         return;
26129     },
26130     
26131     zoomable : function()
26132     {
26133         var minScale = this.thumbEl.getWidth() / this.minWidth;
26134         
26135         if(this.minWidth < this.minHeight){
26136             minScale = this.thumbEl.getHeight() / this.minHeight;
26137         }
26138         
26139         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
26140         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
26141         
26142         if(
26143                 this.isDocument &&
26144                 (this.rotate == 0 || this.rotate == 180) && 
26145                 (
26146                     width > this.imageEl.OriginWidth || 
26147                     height > this.imageEl.OriginHeight ||
26148                     (width < this.minWidth && height < this.minHeight)
26149                 )
26150         ){
26151             return false;
26152         }
26153         
26154         if(
26155                 this.isDocument &&
26156                 (this.rotate == 90 || this.rotate == 270) && 
26157                 (
26158                     width > this.imageEl.OriginWidth || 
26159                     height > this.imageEl.OriginHeight ||
26160                     (width < this.minHeight && height < this.minWidth)
26161                 )
26162         ){
26163             return false;
26164         }
26165         
26166         if(
26167                 !this.isDocument &&
26168                 (this.rotate == 0 || this.rotate == 180) && 
26169                 (
26170                     width < this.minWidth || 
26171                     width > this.imageEl.OriginWidth || 
26172                     height < this.minHeight || 
26173                     height > this.imageEl.OriginHeight
26174                 )
26175         ){
26176             return false;
26177         }
26178         
26179         if(
26180                 !this.isDocument &&
26181                 (this.rotate == 90 || this.rotate == 270) && 
26182                 (
26183                     width < this.minHeight || 
26184                     width > this.imageEl.OriginWidth || 
26185                     height < this.minWidth || 
26186                     height > this.imageEl.OriginHeight
26187                 )
26188         ){
26189             return false;
26190         }
26191         
26192         return true;
26193         
26194     },
26195     
26196     onRotateLeft : function(e)
26197     {   
26198         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26199             
26200             var minScale = this.thumbEl.getWidth() / this.minWidth;
26201             
26202             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26203             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26204             
26205             this.startScale = this.scale;
26206             
26207             while (this.getScaleLevel() < minScale){
26208             
26209                 this.scale = this.scale + 1;
26210                 
26211                 if(!this.zoomable()){
26212                     break;
26213                 }
26214                 
26215                 if(
26216                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26217                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26218                 ){
26219                     continue;
26220                 }
26221                 
26222                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26223
26224                 this.draw();
26225                 
26226                 return;
26227             }
26228             
26229             this.scale = this.startScale;
26230             
26231             this.onRotateFail();
26232             
26233             return false;
26234         }
26235         
26236         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26237
26238         if(this.isDocument){
26239             this.setThumbBoxSize();
26240             this.setThumbBoxPosition();
26241             this.setCanvasPosition();
26242         }
26243         
26244         this.draw();
26245         
26246         this.fireEvent('rotate', this, 'left');
26247         
26248     },
26249     
26250     onRotateRight : function(e)
26251     {
26252         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26253             
26254             var minScale = this.thumbEl.getWidth() / this.minWidth;
26255         
26256             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26257             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26258             
26259             this.startScale = this.scale;
26260             
26261             while (this.getScaleLevel() < minScale){
26262             
26263                 this.scale = this.scale + 1;
26264                 
26265                 if(!this.zoomable()){
26266                     break;
26267                 }
26268                 
26269                 if(
26270                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26271                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26272                 ){
26273                     continue;
26274                 }
26275                 
26276                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26277
26278                 this.draw();
26279                 
26280                 return;
26281             }
26282             
26283             this.scale = this.startScale;
26284             
26285             this.onRotateFail();
26286             
26287             return false;
26288         }
26289         
26290         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26291
26292         if(this.isDocument){
26293             this.setThumbBoxSize();
26294             this.setThumbBoxPosition();
26295             this.setCanvasPosition();
26296         }
26297         
26298         this.draw();
26299         
26300         this.fireEvent('rotate', this, 'right');
26301     },
26302     
26303     onRotateFail : function()
26304     {
26305         this.errorEl.show(true);
26306         
26307         var _this = this;
26308         
26309         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
26310     },
26311     
26312     draw : function()
26313     {
26314         this.previewEl.dom.innerHTML = '';
26315         
26316         var canvasEl = document.createElement("canvas");
26317         
26318         var contextEl = canvasEl.getContext("2d");
26319         
26320         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26321         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26322         var center = this.imageEl.OriginWidth / 2;
26323         
26324         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
26325             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26326             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26327             center = this.imageEl.OriginHeight / 2;
26328         }
26329         
26330         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
26331         
26332         contextEl.translate(center, center);
26333         contextEl.rotate(this.rotate * Math.PI / 180);
26334
26335         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26336         
26337         this.canvasEl = document.createElement("canvas");
26338         
26339         this.contextEl = this.canvasEl.getContext("2d");
26340         
26341         switch (this.rotate) {
26342             case 0 :
26343                 
26344                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26345                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26346                 
26347                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26348                 
26349                 break;
26350             case 90 : 
26351                 
26352                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26353                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26354                 
26355                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26356                     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);
26357                     break;
26358                 }
26359                 
26360                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26361                 
26362                 break;
26363             case 180 :
26364                 
26365                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26366                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26367                 
26368                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26369                     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);
26370                     break;
26371                 }
26372                 
26373                 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);
26374                 
26375                 break;
26376             case 270 :
26377                 
26378                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26379                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26380         
26381                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26382                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26383                     break;
26384                 }
26385                 
26386                 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);
26387                 
26388                 break;
26389             default : 
26390                 break;
26391         }
26392         
26393         this.previewEl.appendChild(this.canvasEl);
26394         
26395         this.setCanvasPosition();
26396     },
26397     
26398     crop : function()
26399     {
26400         if(!this.canvasLoaded){
26401             return;
26402         }
26403         
26404         var imageCanvas = document.createElement("canvas");
26405         
26406         var imageContext = imageCanvas.getContext("2d");
26407         
26408         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26409         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26410         
26411         var center = imageCanvas.width / 2;
26412         
26413         imageContext.translate(center, center);
26414         
26415         imageContext.rotate(this.rotate * Math.PI / 180);
26416         
26417         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26418         
26419         var canvas = document.createElement("canvas");
26420         
26421         var context = canvas.getContext("2d");
26422                 
26423         canvas.width = this.minWidth;
26424         canvas.height = this.minHeight;
26425
26426         switch (this.rotate) {
26427             case 0 :
26428                 
26429                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26430                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26431                 
26432                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26433                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26434                 
26435                 var targetWidth = this.minWidth - 2 * x;
26436                 var targetHeight = this.minHeight - 2 * y;
26437                 
26438                 var scale = 1;
26439                 
26440                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26441                     scale = targetWidth / width;
26442                 }
26443                 
26444                 if(x > 0 && y == 0){
26445                     scale = targetHeight / height;
26446                 }
26447                 
26448                 if(x > 0 && y > 0){
26449                     scale = targetWidth / width;
26450                     
26451                     if(width < height){
26452                         scale = targetHeight / height;
26453                     }
26454                 }
26455                 
26456                 context.scale(scale, scale);
26457                 
26458                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26459                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26460
26461                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26462                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26463
26464                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26465                 
26466                 break;
26467             case 90 : 
26468                 
26469                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26470                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26471                 
26472                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26473                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26474                 
26475                 var targetWidth = this.minWidth - 2 * x;
26476                 var targetHeight = this.minHeight - 2 * y;
26477                 
26478                 var scale = 1;
26479                 
26480                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26481                     scale = targetWidth / width;
26482                 }
26483                 
26484                 if(x > 0 && y == 0){
26485                     scale = targetHeight / height;
26486                 }
26487                 
26488                 if(x > 0 && y > 0){
26489                     scale = targetWidth / width;
26490                     
26491                     if(width < height){
26492                         scale = targetHeight / height;
26493                     }
26494                 }
26495                 
26496                 context.scale(scale, scale);
26497                 
26498                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26499                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26500
26501                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26502                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26503                 
26504                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26505                 
26506                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26507                 
26508                 break;
26509             case 180 :
26510                 
26511                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26512                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26513                 
26514                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26515                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26516                 
26517                 var targetWidth = this.minWidth - 2 * x;
26518                 var targetHeight = this.minHeight - 2 * y;
26519                 
26520                 var scale = 1;
26521                 
26522                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26523                     scale = targetWidth / width;
26524                 }
26525                 
26526                 if(x > 0 && y == 0){
26527                     scale = targetHeight / height;
26528                 }
26529                 
26530                 if(x > 0 && y > 0){
26531                     scale = targetWidth / width;
26532                     
26533                     if(width < height){
26534                         scale = targetHeight / height;
26535                     }
26536                 }
26537                 
26538                 context.scale(scale, scale);
26539                 
26540                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26541                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26542
26543                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26544                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26545
26546                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26547                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26548                 
26549                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26550                 
26551                 break;
26552             case 270 :
26553                 
26554                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26555                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26556                 
26557                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26558                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26559                 
26560                 var targetWidth = this.minWidth - 2 * x;
26561                 var targetHeight = this.minHeight - 2 * y;
26562                 
26563                 var scale = 1;
26564                 
26565                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26566                     scale = targetWidth / width;
26567                 }
26568                 
26569                 if(x > 0 && y == 0){
26570                     scale = targetHeight / height;
26571                 }
26572                 
26573                 if(x > 0 && y > 0){
26574                     scale = targetWidth / width;
26575                     
26576                     if(width < height){
26577                         scale = targetHeight / height;
26578                     }
26579                 }
26580                 
26581                 context.scale(scale, scale);
26582                 
26583                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26584                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26585
26586                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26587                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26588                 
26589                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26590                 
26591                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26592                 
26593                 break;
26594             default : 
26595                 break;
26596         }
26597         
26598         this.cropData = canvas.toDataURL(this.cropType);
26599         
26600         if(this.fireEvent('crop', this, this.cropData) !== false){
26601             this.process(this.file, this.cropData);
26602         }
26603         
26604         return;
26605         
26606     },
26607     
26608     setThumbBoxSize : function()
26609     {
26610         var width, height;
26611         
26612         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
26613             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
26614             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
26615             
26616             this.minWidth = width;
26617             this.minHeight = height;
26618             
26619             if(this.rotate == 90 || this.rotate == 270){
26620                 this.minWidth = height;
26621                 this.minHeight = width;
26622             }
26623         }
26624         
26625         height = 300;
26626         width = Math.ceil(this.minWidth * height / this.minHeight);
26627         
26628         if(this.minWidth > this.minHeight){
26629             width = 300;
26630             height = Math.ceil(this.minHeight * width / this.minWidth);
26631         }
26632         
26633         this.thumbEl.setStyle({
26634             width : width + 'px',
26635             height : height + 'px'
26636         });
26637
26638         return;
26639             
26640     },
26641     
26642     setThumbBoxPosition : function()
26643     {
26644         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
26645         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
26646         
26647         this.thumbEl.setLeft(x);
26648         this.thumbEl.setTop(y);
26649         
26650     },
26651     
26652     baseRotateLevel : function()
26653     {
26654         this.baseRotate = 1;
26655         
26656         if(
26657                 typeof(this.exif) != 'undefined' &&
26658                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
26659                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
26660         ){
26661             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
26662         }
26663         
26664         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
26665         
26666     },
26667     
26668     baseScaleLevel : function()
26669     {
26670         var width, height;
26671         
26672         if(this.isDocument){
26673             
26674             if(this.baseRotate == 6 || this.baseRotate == 8){
26675             
26676                 height = this.thumbEl.getHeight();
26677                 this.baseScale = height / this.imageEl.OriginWidth;
26678
26679                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
26680                     width = this.thumbEl.getWidth();
26681                     this.baseScale = width / this.imageEl.OriginHeight;
26682                 }
26683
26684                 return;
26685             }
26686
26687             height = this.thumbEl.getHeight();
26688             this.baseScale = height / this.imageEl.OriginHeight;
26689
26690             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
26691                 width = this.thumbEl.getWidth();
26692                 this.baseScale = width / this.imageEl.OriginWidth;
26693             }
26694
26695             return;
26696         }
26697         
26698         if(this.baseRotate == 6 || this.baseRotate == 8){
26699             
26700             width = this.thumbEl.getHeight();
26701             this.baseScale = width / this.imageEl.OriginHeight;
26702             
26703             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
26704                 height = this.thumbEl.getWidth();
26705                 this.baseScale = height / this.imageEl.OriginHeight;
26706             }
26707             
26708             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26709                 height = this.thumbEl.getWidth();
26710                 this.baseScale = height / this.imageEl.OriginHeight;
26711                 
26712                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
26713                     width = this.thumbEl.getHeight();
26714                     this.baseScale = width / this.imageEl.OriginWidth;
26715                 }
26716             }
26717             
26718             return;
26719         }
26720         
26721         width = this.thumbEl.getWidth();
26722         this.baseScale = width / this.imageEl.OriginWidth;
26723         
26724         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
26725             height = this.thumbEl.getHeight();
26726             this.baseScale = height / this.imageEl.OriginHeight;
26727         }
26728         
26729         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26730             
26731             height = this.thumbEl.getHeight();
26732             this.baseScale = height / this.imageEl.OriginHeight;
26733             
26734             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26735                 width = this.thumbEl.getWidth();
26736                 this.baseScale = width / this.imageEl.OriginWidth;
26737             }
26738             
26739         }
26740         
26741         return;
26742     },
26743     
26744     getScaleLevel : function()
26745     {
26746         return this.baseScale * Math.pow(1.1, this.scale);
26747     },
26748     
26749     onTouchStart : function(e)
26750     {
26751         if(!this.canvasLoaded){
26752             this.beforeSelectFile(e);
26753             return;
26754         }
26755         
26756         var touches = e.browserEvent.touches;
26757         
26758         if(!touches){
26759             return;
26760         }
26761         
26762         if(touches.length == 1){
26763             this.onMouseDown(e);
26764             return;
26765         }
26766         
26767         if(touches.length != 2){
26768             return;
26769         }
26770         
26771         var coords = [];
26772         
26773         for(var i = 0, finger; finger = touches[i]; i++){
26774             coords.push(finger.pageX, finger.pageY);
26775         }
26776         
26777         var x = Math.pow(coords[0] - coords[2], 2);
26778         var y = Math.pow(coords[1] - coords[3], 2);
26779         
26780         this.startDistance = Math.sqrt(x + y);
26781         
26782         this.startScale = this.scale;
26783         
26784         this.pinching = true;
26785         this.dragable = false;
26786         
26787     },
26788     
26789     onTouchMove : function(e)
26790     {
26791         if(!this.pinching && !this.dragable){
26792             return;
26793         }
26794         
26795         var touches = e.browserEvent.touches;
26796         
26797         if(!touches){
26798             return;
26799         }
26800         
26801         if(this.dragable){
26802             this.onMouseMove(e);
26803             return;
26804         }
26805         
26806         var coords = [];
26807         
26808         for(var i = 0, finger; finger = touches[i]; i++){
26809             coords.push(finger.pageX, finger.pageY);
26810         }
26811         
26812         var x = Math.pow(coords[0] - coords[2], 2);
26813         var y = Math.pow(coords[1] - coords[3], 2);
26814         
26815         this.endDistance = Math.sqrt(x + y);
26816         
26817         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26818         
26819         if(!this.zoomable()){
26820             this.scale = this.startScale;
26821             return;
26822         }
26823         
26824         this.draw();
26825         
26826     },
26827     
26828     onTouchEnd : function(e)
26829     {
26830         this.pinching = false;
26831         this.dragable = false;
26832         
26833     },
26834     
26835     process : function(file, crop)
26836     {
26837         if(this.loadMask){
26838             this.maskEl.mask(this.loadingText);
26839         }
26840         
26841         this.xhr = new XMLHttpRequest();
26842         
26843         file.xhr = this.xhr;
26844
26845         this.xhr.open(this.method, this.url, true);
26846         
26847         var headers = {
26848             "Accept": "application/json",
26849             "Cache-Control": "no-cache",
26850             "X-Requested-With": "XMLHttpRequest"
26851         };
26852         
26853         for (var headerName in headers) {
26854             var headerValue = headers[headerName];
26855             if (headerValue) {
26856                 this.xhr.setRequestHeader(headerName, headerValue);
26857             }
26858         }
26859         
26860         var _this = this;
26861         
26862         this.xhr.onload = function()
26863         {
26864             _this.xhrOnLoad(_this.xhr);
26865         }
26866         
26867         this.xhr.onerror = function()
26868         {
26869             _this.xhrOnError(_this.xhr);
26870         }
26871         
26872         var formData = new FormData();
26873
26874         formData.append('returnHTML', 'NO');
26875         
26876         if(crop){
26877             formData.append('crop', crop);
26878         }
26879         
26880         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26881             formData.append(this.paramName, file, file.name);
26882         }
26883         
26884         if(typeof(file.filename) != 'undefined'){
26885             formData.append('filename', file.filename);
26886         }
26887         
26888         if(typeof(file.mimetype) != 'undefined'){
26889             formData.append('mimetype', file.mimetype);
26890         }
26891         
26892         if(this.fireEvent('arrange', this, formData) != false){
26893             this.xhr.send(formData);
26894         };
26895     },
26896     
26897     xhrOnLoad : function(xhr)
26898     {
26899         if(this.loadMask){
26900             this.maskEl.unmask();
26901         }
26902         
26903         if (xhr.readyState !== 4) {
26904             this.fireEvent('exception', this, xhr);
26905             return;
26906         }
26907
26908         var response = Roo.decode(xhr.responseText);
26909         
26910         if(!response.success){
26911             this.fireEvent('exception', this, xhr);
26912             return;
26913         }
26914         
26915         var response = Roo.decode(xhr.responseText);
26916         
26917         this.fireEvent('upload', this, response);
26918         
26919     },
26920     
26921     xhrOnError : function()
26922     {
26923         if(this.loadMask){
26924             this.maskEl.unmask();
26925         }
26926         
26927         Roo.log('xhr on error');
26928         
26929         var response = Roo.decode(xhr.responseText);
26930           
26931         Roo.log(response);
26932         
26933     },
26934     
26935     prepare : function(file)
26936     {   
26937         if(this.loadMask){
26938             this.maskEl.mask(this.loadingText);
26939         }
26940         
26941         this.file = false;
26942         this.exif = {};
26943         
26944         if(typeof(file) === 'string'){
26945             this.loadCanvas(file);
26946             return;
26947         }
26948         
26949         if(!file || !this.urlAPI){
26950             return;
26951         }
26952         
26953         this.file = file;
26954         this.cropType = file.type;
26955         
26956         var _this = this;
26957         
26958         if(this.fireEvent('prepare', this, this.file) != false){
26959             
26960             var reader = new FileReader();
26961             
26962             reader.onload = function (e) {
26963                 if (e.target.error) {
26964                     Roo.log(e.target.error);
26965                     return;
26966                 }
26967                 
26968                 var buffer = e.target.result,
26969                     dataView = new DataView(buffer),
26970                     offset = 2,
26971                     maxOffset = dataView.byteLength - 4,
26972                     markerBytes,
26973                     markerLength;
26974                 
26975                 if (dataView.getUint16(0) === 0xffd8) {
26976                     while (offset < maxOffset) {
26977                         markerBytes = dataView.getUint16(offset);
26978                         
26979                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
26980                             markerLength = dataView.getUint16(offset + 2) + 2;
26981                             if (offset + markerLength > dataView.byteLength) {
26982                                 Roo.log('Invalid meta data: Invalid segment size.');
26983                                 break;
26984                             }
26985                             
26986                             if(markerBytes == 0xffe1){
26987                                 _this.parseExifData(
26988                                     dataView,
26989                                     offset,
26990                                     markerLength
26991                                 );
26992                             }
26993                             
26994                             offset += markerLength;
26995                             
26996                             continue;
26997                         }
26998                         
26999                         break;
27000                     }
27001                     
27002                 }
27003                 
27004                 var url = _this.urlAPI.createObjectURL(_this.file);
27005                 
27006                 _this.loadCanvas(url);
27007                 
27008                 return;
27009             }
27010             
27011             reader.readAsArrayBuffer(this.file);
27012             
27013         }
27014         
27015     },
27016     
27017     parseExifData : function(dataView, offset, length)
27018     {
27019         var tiffOffset = offset + 10,
27020             littleEndian,
27021             dirOffset;
27022     
27023         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27024             // No Exif data, might be XMP data instead
27025             return;
27026         }
27027         
27028         // Check for the ASCII code for "Exif" (0x45786966):
27029         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27030             // No Exif data, might be XMP data instead
27031             return;
27032         }
27033         if (tiffOffset + 8 > dataView.byteLength) {
27034             Roo.log('Invalid Exif data: Invalid segment size.');
27035             return;
27036         }
27037         // Check for the two null bytes:
27038         if (dataView.getUint16(offset + 8) !== 0x0000) {
27039             Roo.log('Invalid Exif data: Missing byte alignment offset.');
27040             return;
27041         }
27042         // Check the byte alignment:
27043         switch (dataView.getUint16(tiffOffset)) {
27044         case 0x4949:
27045             littleEndian = true;
27046             break;
27047         case 0x4D4D:
27048             littleEndian = false;
27049             break;
27050         default:
27051             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27052             return;
27053         }
27054         // Check for the TIFF tag marker (0x002A):
27055         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27056             Roo.log('Invalid Exif data: Missing TIFF marker.');
27057             return;
27058         }
27059         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27060         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27061         
27062         this.parseExifTags(
27063             dataView,
27064             tiffOffset,
27065             tiffOffset + dirOffset,
27066             littleEndian
27067         );
27068     },
27069     
27070     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27071     {
27072         var tagsNumber,
27073             dirEndOffset,
27074             i;
27075         if (dirOffset + 6 > dataView.byteLength) {
27076             Roo.log('Invalid Exif data: Invalid directory offset.');
27077             return;
27078         }
27079         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
27080         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
27081         if (dirEndOffset + 4 > dataView.byteLength) {
27082             Roo.log('Invalid Exif data: Invalid directory size.');
27083             return;
27084         }
27085         for (i = 0; i < tagsNumber; i += 1) {
27086             this.parseExifTag(
27087                 dataView,
27088                 tiffOffset,
27089                 dirOffset + 2 + 12 * i, // tag offset
27090                 littleEndian
27091             );
27092         }
27093         // Return the offset to the next directory:
27094         return dataView.getUint32(dirEndOffset, littleEndian);
27095     },
27096     
27097     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
27098     {
27099         var tag = dataView.getUint16(offset, littleEndian);
27100         
27101         this.exif[tag] = this.getExifValue(
27102             dataView,
27103             tiffOffset,
27104             offset,
27105             dataView.getUint16(offset + 2, littleEndian), // tag type
27106             dataView.getUint32(offset + 4, littleEndian), // tag length
27107             littleEndian
27108         );
27109     },
27110     
27111     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
27112     {
27113         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
27114             tagSize,
27115             dataOffset,
27116             values,
27117             i,
27118             str,
27119             c;
27120     
27121         if (!tagType) {
27122             Roo.log('Invalid Exif data: Invalid tag type.');
27123             return;
27124         }
27125         
27126         tagSize = tagType.size * length;
27127         // Determine if the value is contained in the dataOffset bytes,
27128         // or if the value at the dataOffset is a pointer to the actual data:
27129         dataOffset = tagSize > 4 ?
27130                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
27131         if (dataOffset + tagSize > dataView.byteLength) {
27132             Roo.log('Invalid Exif data: Invalid data offset.');
27133             return;
27134         }
27135         if (length === 1) {
27136             return tagType.getValue(dataView, dataOffset, littleEndian);
27137         }
27138         values = [];
27139         for (i = 0; i < length; i += 1) {
27140             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
27141         }
27142         
27143         if (tagType.ascii) {
27144             str = '';
27145             // Concatenate the chars:
27146             for (i = 0; i < values.length; i += 1) {
27147                 c = values[i];
27148                 // Ignore the terminating NULL byte(s):
27149                 if (c === '\u0000') {
27150                     break;
27151                 }
27152                 str += c;
27153             }
27154             return str;
27155         }
27156         return values;
27157     }
27158     
27159 });
27160
27161 Roo.apply(Roo.bootstrap.UploadCropbox, {
27162     tags : {
27163         'Orientation': 0x0112
27164     },
27165     
27166     Orientation: {
27167             1: 0, //'top-left',
27168 //            2: 'top-right',
27169             3: 180, //'bottom-right',
27170 //            4: 'bottom-left',
27171 //            5: 'left-top',
27172             6: 90, //'right-top',
27173 //            7: 'right-bottom',
27174             8: 270 //'left-bottom'
27175     },
27176     
27177     exifTagTypes : {
27178         // byte, 8-bit unsigned int:
27179         1: {
27180             getValue: function (dataView, dataOffset) {
27181                 return dataView.getUint8(dataOffset);
27182             },
27183             size: 1
27184         },
27185         // ascii, 8-bit byte:
27186         2: {
27187             getValue: function (dataView, dataOffset) {
27188                 return String.fromCharCode(dataView.getUint8(dataOffset));
27189             },
27190             size: 1,
27191             ascii: true
27192         },
27193         // short, 16 bit int:
27194         3: {
27195             getValue: function (dataView, dataOffset, littleEndian) {
27196                 return dataView.getUint16(dataOffset, littleEndian);
27197             },
27198             size: 2
27199         },
27200         // long, 32 bit int:
27201         4: {
27202             getValue: function (dataView, dataOffset, littleEndian) {
27203                 return dataView.getUint32(dataOffset, littleEndian);
27204             },
27205             size: 4
27206         },
27207         // rational = two long values, first is numerator, second is denominator:
27208         5: {
27209             getValue: function (dataView, dataOffset, littleEndian) {
27210                 return dataView.getUint32(dataOffset, littleEndian) /
27211                     dataView.getUint32(dataOffset + 4, littleEndian);
27212             },
27213             size: 8
27214         },
27215         // slong, 32 bit signed int:
27216         9: {
27217             getValue: function (dataView, dataOffset, littleEndian) {
27218                 return dataView.getInt32(dataOffset, littleEndian);
27219             },
27220             size: 4
27221         },
27222         // srational, two slongs, first is numerator, second is denominator:
27223         10: {
27224             getValue: function (dataView, dataOffset, littleEndian) {
27225                 return dataView.getInt32(dataOffset, littleEndian) /
27226                     dataView.getInt32(dataOffset + 4, littleEndian);
27227             },
27228             size: 8
27229         }
27230     },
27231     
27232     footer : {
27233         STANDARD : [
27234             {
27235                 tag : 'div',
27236                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27237                 action : 'rotate-left',
27238                 cn : [
27239                     {
27240                         tag : 'button',
27241                         cls : 'btn btn-default',
27242                         html : '<i class="fa fa-undo"></i>'
27243                     }
27244                 ]
27245             },
27246             {
27247                 tag : 'div',
27248                 cls : 'btn-group roo-upload-cropbox-picture',
27249                 action : 'picture',
27250                 cn : [
27251                     {
27252                         tag : 'button',
27253                         cls : 'btn btn-default',
27254                         html : '<i class="fa fa-picture-o"></i>'
27255                     }
27256                 ]
27257             },
27258             {
27259                 tag : 'div',
27260                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27261                 action : 'rotate-right',
27262                 cn : [
27263                     {
27264                         tag : 'button',
27265                         cls : 'btn btn-default',
27266                         html : '<i class="fa fa-repeat"></i>'
27267                     }
27268                 ]
27269             }
27270         ],
27271         DOCUMENT : [
27272             {
27273                 tag : 'div',
27274                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27275                 action : 'rotate-left',
27276                 cn : [
27277                     {
27278                         tag : 'button',
27279                         cls : 'btn btn-default',
27280                         html : '<i class="fa fa-undo"></i>'
27281                     }
27282                 ]
27283             },
27284             {
27285                 tag : 'div',
27286                 cls : 'btn-group roo-upload-cropbox-download',
27287                 action : 'download',
27288                 cn : [
27289                     {
27290                         tag : 'button',
27291                         cls : 'btn btn-default',
27292                         html : '<i class="fa fa-download"></i>'
27293                     }
27294                 ]
27295             },
27296             {
27297                 tag : 'div',
27298                 cls : 'btn-group roo-upload-cropbox-crop',
27299                 action : 'crop',
27300                 cn : [
27301                     {
27302                         tag : 'button',
27303                         cls : 'btn btn-default',
27304                         html : '<i class="fa fa-crop"></i>'
27305                     }
27306                 ]
27307             },
27308             {
27309                 tag : 'div',
27310                 cls : 'btn-group roo-upload-cropbox-trash',
27311                 action : 'trash',
27312                 cn : [
27313                     {
27314                         tag : 'button',
27315                         cls : 'btn btn-default',
27316                         html : '<i class="fa fa-trash"></i>'
27317                     }
27318                 ]
27319             },
27320             {
27321                 tag : 'div',
27322                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27323                 action : 'rotate-right',
27324                 cn : [
27325                     {
27326                         tag : 'button',
27327                         cls : 'btn btn-default',
27328                         html : '<i class="fa fa-repeat"></i>'
27329                     }
27330                 ]
27331             }
27332         ],
27333         ROTATOR : [
27334             {
27335                 tag : 'div',
27336                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27337                 action : 'rotate-left',
27338                 cn : [
27339                     {
27340                         tag : 'button',
27341                         cls : 'btn btn-default',
27342                         html : '<i class="fa fa-undo"></i>'
27343                     }
27344                 ]
27345             },
27346             {
27347                 tag : 'div',
27348                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27349                 action : 'rotate-right',
27350                 cn : [
27351                     {
27352                         tag : 'button',
27353                         cls : 'btn btn-default',
27354                         html : '<i class="fa fa-repeat"></i>'
27355                     }
27356                 ]
27357             }
27358         ]
27359     }
27360 });
27361
27362 /*
27363 * Licence: LGPL
27364 */
27365
27366 /**
27367  * @class Roo.bootstrap.DocumentManager
27368  * @extends Roo.bootstrap.Component
27369  * Bootstrap DocumentManager class
27370  * @cfg {String} paramName default 'imageUpload'
27371  * @cfg {String} toolTipName default 'filename'
27372  * @cfg {String} method default POST
27373  * @cfg {String} url action url
27374  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
27375  * @cfg {Boolean} multiple multiple upload default true
27376  * @cfg {Number} thumbSize default 300
27377  * @cfg {String} fieldLabel
27378  * @cfg {Number} labelWidth default 4
27379  * @cfg {String} labelAlign (left|top) default left
27380  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
27381  * 
27382  * @constructor
27383  * Create a new DocumentManager
27384  * @param {Object} config The config object
27385  */
27386
27387 Roo.bootstrap.DocumentManager = function(config){
27388     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
27389     
27390     this.files = [];
27391     this.delegates = [];
27392     
27393     this.addEvents({
27394         /**
27395          * @event initial
27396          * Fire when initial the DocumentManager
27397          * @param {Roo.bootstrap.DocumentManager} this
27398          */
27399         "initial" : true,
27400         /**
27401          * @event inspect
27402          * inspect selected file
27403          * @param {Roo.bootstrap.DocumentManager} this
27404          * @param {File} file
27405          */
27406         "inspect" : true,
27407         /**
27408          * @event exception
27409          * Fire when xhr load exception
27410          * @param {Roo.bootstrap.DocumentManager} this
27411          * @param {XMLHttpRequest} xhr
27412          */
27413         "exception" : true,
27414         /**
27415          * @event afterupload
27416          * Fire when xhr load exception
27417          * @param {Roo.bootstrap.DocumentManager} this
27418          * @param {XMLHttpRequest} xhr
27419          */
27420         "afterupload" : true,
27421         /**
27422          * @event prepare
27423          * prepare the form data
27424          * @param {Roo.bootstrap.DocumentManager} this
27425          * @param {Object} formData
27426          */
27427         "prepare" : true,
27428         /**
27429          * @event remove
27430          * Fire when remove the file
27431          * @param {Roo.bootstrap.DocumentManager} this
27432          * @param {Object} file
27433          */
27434         "remove" : true,
27435         /**
27436          * @event refresh
27437          * Fire after refresh the file
27438          * @param {Roo.bootstrap.DocumentManager} this
27439          */
27440         "refresh" : true,
27441         /**
27442          * @event click
27443          * Fire after click the image
27444          * @param {Roo.bootstrap.DocumentManager} this
27445          * @param {Object} file
27446          */
27447         "click" : true,
27448         /**
27449          * @event edit
27450          * Fire when upload a image and editable set to true
27451          * @param {Roo.bootstrap.DocumentManager} this
27452          * @param {Object} file
27453          */
27454         "edit" : true,
27455         /**
27456          * @event beforeselectfile
27457          * Fire before select file
27458          * @param {Roo.bootstrap.DocumentManager} this
27459          */
27460         "beforeselectfile" : true,
27461         /**
27462          * @event process
27463          * Fire before process file
27464          * @param {Roo.bootstrap.DocumentManager} this
27465          * @param {Object} file
27466          */
27467         "process" : true
27468         
27469     });
27470 };
27471
27472 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
27473     
27474     boxes : 0,
27475     inputName : '',
27476     thumbSize : 300,
27477     multiple : true,
27478     files : false,
27479     method : 'POST',
27480     url : '',
27481     paramName : 'imageUpload',
27482     toolTipName : 'filename',
27483     fieldLabel : '',
27484     labelWidth : 4,
27485     labelAlign : 'left',
27486     editable : true,
27487     delegates : false,
27488     xhr : false, 
27489     
27490     getAutoCreate : function()
27491     {   
27492         var managerWidget = {
27493             tag : 'div',
27494             cls : 'roo-document-manager',
27495             cn : [
27496                 {
27497                     tag : 'input',
27498                     cls : 'roo-document-manager-selector',
27499                     type : 'file'
27500                 },
27501                 {
27502                     tag : 'div',
27503                     cls : 'roo-document-manager-uploader',
27504                     cn : [
27505                         {
27506                             tag : 'div',
27507                             cls : 'roo-document-manager-upload-btn',
27508                             html : '<i class="fa fa-plus"></i>'
27509                         }
27510                     ]
27511                     
27512                 }
27513             ]
27514         };
27515         
27516         var content = [
27517             {
27518                 tag : 'div',
27519                 cls : 'column col-md-12',
27520                 cn : managerWidget
27521             }
27522         ];
27523         
27524         if(this.fieldLabel.length){
27525             
27526             content = [
27527                 {
27528                     tag : 'div',
27529                     cls : 'column col-md-12',
27530                     html : this.fieldLabel
27531                 },
27532                 {
27533                     tag : 'div',
27534                     cls : 'column col-md-12',
27535                     cn : managerWidget
27536                 }
27537             ];
27538
27539             if(this.labelAlign == 'left'){
27540                 content = [
27541                     {
27542                         tag : 'div',
27543                         cls : 'column col-md-' + this.labelWidth,
27544                         html : this.fieldLabel
27545                     },
27546                     {
27547                         tag : 'div',
27548                         cls : 'column col-md-' + (12 - this.labelWidth),
27549                         cn : managerWidget
27550                     }
27551                 ];
27552                 
27553             }
27554         }
27555         
27556         var cfg = {
27557             tag : 'div',
27558             cls : 'row clearfix',
27559             cn : content
27560         };
27561         
27562         return cfg;
27563         
27564     },
27565     
27566     initEvents : function()
27567     {
27568         this.managerEl = this.el.select('.roo-document-manager', true).first();
27569         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27570         
27571         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
27572         this.selectorEl.hide();
27573         
27574         if(this.multiple){
27575             this.selectorEl.attr('multiple', 'multiple');
27576         }
27577         
27578         this.selectorEl.on('change', this.onFileSelected, this);
27579         
27580         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
27581         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27582         
27583         this.uploader.on('click', this.onUploaderClick, this);
27584         
27585         this.renderProgressDialog();
27586         
27587         var _this = this;
27588         
27589         window.addEventListener("resize", function() { _this.refresh(); } );
27590         
27591         this.fireEvent('initial', this);
27592     },
27593     
27594     renderProgressDialog : function()
27595     {
27596         var _this = this;
27597         
27598         this.progressDialog = new Roo.bootstrap.Modal({
27599             cls : 'roo-document-manager-progress-dialog',
27600             allow_close : false,
27601             title : '',
27602             buttons : [
27603                 {
27604                     name  :'cancel',
27605                     weight : 'danger',
27606                     html : 'Cancel'
27607                 }
27608             ], 
27609             listeners : { 
27610                 btnclick : function() {
27611                     _this.uploadCancel();
27612                     this.hide();
27613                 }
27614             }
27615         });
27616          
27617         this.progressDialog.render(Roo.get(document.body));
27618          
27619         this.progress = new Roo.bootstrap.Progress({
27620             cls : 'roo-document-manager-progress',
27621             active : true,
27622             striped : true
27623         });
27624         
27625         this.progress.render(this.progressDialog.getChildContainer());
27626         
27627         this.progressBar = new Roo.bootstrap.ProgressBar({
27628             cls : 'roo-document-manager-progress-bar',
27629             aria_valuenow : 0,
27630             aria_valuemin : 0,
27631             aria_valuemax : 12,
27632             panel : 'success'
27633         });
27634         
27635         this.progressBar.render(this.progress.getChildContainer());
27636     },
27637     
27638     onUploaderClick : function(e)
27639     {
27640         e.preventDefault();
27641      
27642         if(this.fireEvent('beforeselectfile', this) != false){
27643             this.selectorEl.dom.click();
27644         }
27645         
27646     },
27647     
27648     onFileSelected : function(e)
27649     {
27650         e.preventDefault();
27651         
27652         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27653             return;
27654         }
27655         
27656         Roo.each(this.selectorEl.dom.files, function(file){
27657             if(this.fireEvent('inspect', this, file) != false){
27658                 this.files.push(file);
27659             }
27660         }, this);
27661         
27662         this.queue();
27663         
27664     },
27665     
27666     queue : function()
27667     {
27668         this.selectorEl.dom.value = '';
27669         
27670         if(!this.files.length){
27671             return;
27672         }
27673         
27674         if(this.boxes > 0 && this.files.length > this.boxes){
27675             this.files = this.files.slice(0, this.boxes);
27676         }
27677         
27678         this.uploader.show();
27679         
27680         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27681             this.uploader.hide();
27682         }
27683         
27684         var _this = this;
27685         
27686         var files = [];
27687         
27688         var docs = [];
27689         
27690         Roo.each(this.files, function(file){
27691             
27692             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27693                 var f = this.renderPreview(file);
27694                 files.push(f);
27695                 return;
27696             }
27697             
27698             if(file.type.indexOf('image') != -1){
27699                 this.delegates.push(
27700                     (function(){
27701                         _this.process(file);
27702                     }).createDelegate(this)
27703                 );
27704         
27705                 return;
27706             }
27707             
27708             docs.push(
27709                 (function(){
27710                     _this.process(file);
27711                 }).createDelegate(this)
27712             );
27713             
27714         }, this);
27715         
27716         this.files = files;
27717         
27718         this.delegates = this.delegates.concat(docs);
27719         
27720         if(!this.delegates.length){
27721             this.refresh();
27722             return;
27723         }
27724         
27725         this.progressBar.aria_valuemax = this.delegates.length;
27726         
27727         this.arrange();
27728         
27729         return;
27730     },
27731     
27732     arrange : function()
27733     {
27734         if(!this.delegates.length){
27735             this.progressDialog.hide();
27736             this.refresh();
27737             return;
27738         }
27739         
27740         var delegate = this.delegates.shift();
27741         
27742         this.progressDialog.show();
27743         
27744         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27745         
27746         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27747         
27748         delegate();
27749     },
27750     
27751     refresh : function()
27752     {
27753         this.uploader.show();
27754         
27755         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27756             this.uploader.hide();
27757         }
27758         
27759         Roo.isTouch ? this.closable(false) : this.closable(true);
27760         
27761         this.fireEvent('refresh', this);
27762     },
27763     
27764     onRemove : function(e, el, o)
27765     {
27766         e.preventDefault();
27767         
27768         this.fireEvent('remove', this, o);
27769         
27770     },
27771     
27772     remove : function(o)
27773     {
27774         var files = [];
27775         
27776         Roo.each(this.files, function(file){
27777             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27778                 files.push(file);
27779                 return;
27780             }
27781
27782             o.target.remove();
27783
27784         }, this);
27785         
27786         this.files = files;
27787         
27788         this.refresh();
27789     },
27790     
27791     clear : function()
27792     {
27793         Roo.each(this.files, function(file){
27794             if(!file.target){
27795                 return;
27796             }
27797             
27798             file.target.remove();
27799
27800         }, this);
27801         
27802         this.files = [];
27803         
27804         this.refresh();
27805     },
27806     
27807     onClick : function(e, el, o)
27808     {
27809         e.preventDefault();
27810         
27811         this.fireEvent('click', this, o);
27812         
27813     },
27814     
27815     closable : function(closable)
27816     {
27817         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27818             
27819             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27820             
27821             if(closable){
27822                 el.show();
27823                 return;
27824             }
27825             
27826             el.hide();
27827             
27828         }, this);
27829     },
27830     
27831     xhrOnLoad : function(xhr)
27832     {
27833         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27834             el.remove();
27835         }, this);
27836         
27837         if (xhr.readyState !== 4) {
27838             this.arrange();
27839             this.fireEvent('exception', this, xhr);
27840             return;
27841         }
27842
27843         var response = Roo.decode(xhr.responseText);
27844         
27845         if(!response.success){
27846             this.arrange();
27847             this.fireEvent('exception', this, xhr);
27848             return;
27849         }
27850         
27851         var file = this.renderPreview(response.data);
27852         
27853         this.files.push(file);
27854         
27855         this.arrange();
27856         
27857         this.fireEvent('afterupload', this, xhr);
27858         
27859     },
27860     
27861     xhrOnError : function(xhr)
27862     {
27863         Roo.log('xhr on error');
27864         
27865         var response = Roo.decode(xhr.responseText);
27866           
27867         Roo.log(response);
27868         
27869         this.arrange();
27870     },
27871     
27872     process : function(file)
27873     {
27874         if(this.fireEvent('process', this, file) !== false){
27875             if(this.editable && file.type.indexOf('image') != -1){
27876                 this.fireEvent('edit', this, file);
27877                 return;
27878             }
27879
27880             this.uploadStart(file, false);
27881
27882             return;
27883         }
27884         
27885     },
27886     
27887     uploadStart : function(file, crop)
27888     {
27889         this.xhr = new XMLHttpRequest();
27890         
27891         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27892             this.arrange();
27893             return;
27894         }
27895         
27896         file.xhr = this.xhr;
27897             
27898         this.managerEl.createChild({
27899             tag : 'div',
27900             cls : 'roo-document-manager-loading',
27901             cn : [
27902                 {
27903                     tag : 'div',
27904                     tooltip : file.name,
27905                     cls : 'roo-document-manager-thumb',
27906                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27907                 }
27908             ]
27909
27910         });
27911
27912         this.xhr.open(this.method, this.url, true);
27913         
27914         var headers = {
27915             "Accept": "application/json",
27916             "Cache-Control": "no-cache",
27917             "X-Requested-With": "XMLHttpRequest"
27918         };
27919         
27920         for (var headerName in headers) {
27921             var headerValue = headers[headerName];
27922             if (headerValue) {
27923                 this.xhr.setRequestHeader(headerName, headerValue);
27924             }
27925         }
27926         
27927         var _this = this;
27928         
27929         this.xhr.onload = function()
27930         {
27931             _this.xhrOnLoad(_this.xhr);
27932         }
27933         
27934         this.xhr.onerror = function()
27935         {
27936             _this.xhrOnError(_this.xhr);
27937         }
27938         
27939         var formData = new FormData();
27940
27941         formData.append('returnHTML', 'NO');
27942         
27943         if(crop){
27944             formData.append('crop', crop);
27945         }
27946         
27947         formData.append(this.paramName, file, file.name);
27948         
27949         var options = {
27950             file : file, 
27951             manually : false
27952         };
27953         
27954         if(this.fireEvent('prepare', this, formData, options) != false){
27955             
27956             if(options.manually){
27957                 return;
27958             }
27959             
27960             this.xhr.send(formData);
27961             return;
27962         };
27963         
27964         this.uploadCancel();
27965     },
27966     
27967     uploadCancel : function()
27968     {
27969         if (this.xhr) {
27970             this.xhr.abort();
27971         }
27972         
27973         this.delegates = [];
27974         
27975         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27976             el.remove();
27977         }, this);
27978         
27979         this.arrange();
27980     },
27981     
27982     renderPreview : function(file)
27983     {
27984         if(typeof(file.target) != 'undefined' && file.target){
27985             return file;
27986         }
27987         
27988         var previewEl = this.managerEl.createChild({
27989             tag : 'div',
27990             cls : 'roo-document-manager-preview',
27991             cn : [
27992                 {
27993                     tag : 'div',
27994                     tooltip : file[this.toolTipName],
27995                     cls : 'roo-document-manager-thumb',
27996                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
27997                 },
27998                 {
27999                     tag : 'button',
28000                     cls : 'close',
28001                     html : '<i class="fa fa-times-circle"></i>'
28002                 }
28003             ]
28004         });
28005
28006         var close = previewEl.select('button.close', true).first();
28007
28008         close.on('click', this.onRemove, this, file);
28009
28010         file.target = previewEl;
28011
28012         var image = previewEl.select('img', true).first();
28013         
28014         var _this = this;
28015         
28016         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28017         
28018         image.on('click', this.onClick, this, file);
28019         
28020         return file;
28021         
28022     },
28023     
28024     onPreviewLoad : function(file, image)
28025     {
28026         if(typeof(file.target) == 'undefined' || !file.target){
28027             return;
28028         }
28029         
28030         var width = image.dom.naturalWidth || image.dom.width;
28031         var height = image.dom.naturalHeight || image.dom.height;
28032         
28033         if(width > height){
28034             file.target.addClass('wide');
28035             return;
28036         }
28037         
28038         file.target.addClass('tall');
28039         return;
28040         
28041     },
28042     
28043     uploadFromSource : function(file, crop)
28044     {
28045         this.xhr = new XMLHttpRequest();
28046         
28047         this.managerEl.createChild({
28048             tag : 'div',
28049             cls : 'roo-document-manager-loading',
28050             cn : [
28051                 {
28052                     tag : 'div',
28053                     tooltip : file.name,
28054                     cls : 'roo-document-manager-thumb',
28055                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28056                 }
28057             ]
28058
28059         });
28060
28061         this.xhr.open(this.method, this.url, true);
28062         
28063         var headers = {
28064             "Accept": "application/json",
28065             "Cache-Control": "no-cache",
28066             "X-Requested-With": "XMLHttpRequest"
28067         };
28068         
28069         for (var headerName in headers) {
28070             var headerValue = headers[headerName];
28071             if (headerValue) {
28072                 this.xhr.setRequestHeader(headerName, headerValue);
28073             }
28074         }
28075         
28076         var _this = this;
28077         
28078         this.xhr.onload = function()
28079         {
28080             _this.xhrOnLoad(_this.xhr);
28081         }
28082         
28083         this.xhr.onerror = function()
28084         {
28085             _this.xhrOnError(_this.xhr);
28086         }
28087         
28088         var formData = new FormData();
28089
28090         formData.append('returnHTML', 'NO');
28091         
28092         formData.append('crop', crop);
28093         
28094         if(typeof(file.filename) != 'undefined'){
28095             formData.append('filename', file.filename);
28096         }
28097         
28098         if(typeof(file.mimetype) != 'undefined'){
28099             formData.append('mimetype', file.mimetype);
28100         }
28101         
28102         if(this.fireEvent('prepare', this, formData) != false){
28103             this.xhr.send(formData);
28104         };
28105     }
28106 });
28107
28108 /*
28109 * Licence: LGPL
28110 */
28111
28112 /**
28113  * @class Roo.bootstrap.DocumentViewer
28114  * @extends Roo.bootstrap.Component
28115  * Bootstrap DocumentViewer class
28116  * @cfg {Boolean} showDownload (true|false) show download button (default true)
28117  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
28118  * 
28119  * @constructor
28120  * Create a new DocumentViewer
28121  * @param {Object} config The config object
28122  */
28123
28124 Roo.bootstrap.DocumentViewer = function(config){
28125     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
28126     
28127     this.addEvents({
28128         /**
28129          * @event initial
28130          * Fire after initEvent
28131          * @param {Roo.bootstrap.DocumentViewer} this
28132          */
28133         "initial" : true,
28134         /**
28135          * @event click
28136          * Fire after click
28137          * @param {Roo.bootstrap.DocumentViewer} this
28138          */
28139         "click" : true,
28140         /**
28141          * @event download
28142          * Fire after download button
28143          * @param {Roo.bootstrap.DocumentViewer} this
28144          */
28145         "download" : true,
28146         /**
28147          * @event trash
28148          * Fire after trash button
28149          * @param {Roo.bootstrap.DocumentViewer} this
28150          */
28151         "trash" : true
28152         
28153     });
28154 };
28155
28156 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
28157     
28158     showDownload : true,
28159     
28160     showTrash : true,
28161     
28162     getAutoCreate : function()
28163     {
28164         var cfg = {
28165             tag : 'div',
28166             cls : 'roo-document-viewer',
28167             cn : [
28168                 {
28169                     tag : 'div',
28170                     cls : 'roo-document-viewer-body',
28171                     cn : [
28172                         {
28173                             tag : 'div',
28174                             cls : 'roo-document-viewer-thumb',
28175                             cn : [
28176                                 {
28177                                     tag : 'img',
28178                                     cls : 'roo-document-viewer-image'
28179                                 }
28180                             ]
28181                         }
28182                     ]
28183                 },
28184                 {
28185                     tag : 'div',
28186                     cls : 'roo-document-viewer-footer',
28187                     cn : {
28188                         tag : 'div',
28189                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
28190                         cn : [
28191                             {
28192                                 tag : 'div',
28193                                 cls : 'btn-group roo-document-viewer-download',
28194                                 cn : [
28195                                     {
28196                                         tag : 'button',
28197                                         cls : 'btn btn-default',
28198                                         html : '<i class="fa fa-download"></i>'
28199                                     }
28200                                 ]
28201                             },
28202                             {
28203                                 tag : 'div',
28204                                 cls : 'btn-group roo-document-viewer-trash',
28205                                 cn : [
28206                                     {
28207                                         tag : 'button',
28208                                         cls : 'btn btn-default',
28209                                         html : '<i class="fa fa-trash"></i>'
28210                                     }
28211                                 ]
28212                             }
28213                         ]
28214                     }
28215                 }
28216             ]
28217         };
28218         
28219         return cfg;
28220     },
28221     
28222     initEvents : function()
28223     {
28224         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
28225         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
28226         
28227         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
28228         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
28229         
28230         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
28231         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
28232         
28233         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
28234         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
28235         
28236         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
28237         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
28238         
28239         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
28240         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
28241         
28242         this.bodyEl.on('click', this.onClick, this);
28243         this.downloadBtn.on('click', this.onDownload, this);
28244         this.trashBtn.on('click', this.onTrash, this);
28245         
28246         this.downloadBtn.hide();
28247         this.trashBtn.hide();
28248         
28249         if(this.showDownload){
28250             this.downloadBtn.show();
28251         }
28252         
28253         if(this.showTrash){
28254             this.trashBtn.show();
28255         }
28256         
28257         if(!this.showDownload && !this.showTrash) {
28258             this.footerEl.hide();
28259         }
28260         
28261     },
28262     
28263     initial : function()
28264     {
28265         this.fireEvent('initial', this);
28266         
28267     },
28268     
28269     onClick : function(e)
28270     {
28271         e.preventDefault();
28272         
28273         this.fireEvent('click', this);
28274     },
28275     
28276     onDownload : function(e)
28277     {
28278         e.preventDefault();
28279         
28280         this.fireEvent('download', this);
28281     },
28282     
28283     onTrash : function(e)
28284     {
28285         e.preventDefault();
28286         
28287         this.fireEvent('trash', this);
28288     }
28289     
28290 });
28291 /*
28292  * - LGPL
28293  *
28294  * nav progress bar
28295  * 
28296  */
28297
28298 /**
28299  * @class Roo.bootstrap.NavProgressBar
28300  * @extends Roo.bootstrap.Component
28301  * Bootstrap NavProgressBar class
28302  * 
28303  * @constructor
28304  * Create a new nav progress bar
28305  * @param {Object} config The config object
28306  */
28307
28308 Roo.bootstrap.NavProgressBar = function(config){
28309     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
28310
28311     this.bullets = this.bullets || [];
28312    
28313 //    Roo.bootstrap.NavProgressBar.register(this);
28314      this.addEvents({
28315         /**
28316              * @event changed
28317              * Fires when the active item changes
28318              * @param {Roo.bootstrap.NavProgressBar} this
28319              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
28320              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
28321          */
28322         'changed': true
28323      });
28324     
28325 };
28326
28327 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
28328     
28329     bullets : [],
28330     barItems : [],
28331     
28332     getAutoCreate : function()
28333     {
28334         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
28335         
28336         cfg = {
28337             tag : 'div',
28338             cls : 'roo-navigation-bar-group',
28339             cn : [
28340                 {
28341                     tag : 'div',
28342                     cls : 'roo-navigation-top-bar'
28343                 },
28344                 {
28345                     tag : 'div',
28346                     cls : 'roo-navigation-bullets-bar',
28347                     cn : [
28348                         {
28349                             tag : 'ul',
28350                             cls : 'roo-navigation-bar'
28351                         }
28352                     ]
28353                 },
28354                 
28355                 {
28356                     tag : 'div',
28357                     cls : 'roo-navigation-bottom-bar'
28358                 }
28359             ]
28360             
28361         };
28362         
28363         return cfg;
28364         
28365     },
28366     
28367     initEvents: function() 
28368     {
28369         
28370     },
28371     
28372     onRender : function(ct, position) 
28373     {
28374         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28375         
28376         if(this.bullets.length){
28377             Roo.each(this.bullets, function(b){
28378                this.addItem(b);
28379             }, this);
28380         }
28381         
28382         this.format();
28383         
28384     },
28385     
28386     addItem : function(cfg)
28387     {
28388         var item = new Roo.bootstrap.NavProgressItem(cfg);
28389         
28390         item.parentId = this.id;
28391         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
28392         
28393         if(cfg.html){
28394             var top = new Roo.bootstrap.Element({
28395                 tag : 'div',
28396                 cls : 'roo-navigation-bar-text'
28397             });
28398             
28399             var bottom = new Roo.bootstrap.Element({
28400                 tag : 'div',
28401                 cls : 'roo-navigation-bar-text'
28402             });
28403             
28404             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
28405             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
28406             
28407             var topText = new Roo.bootstrap.Element({
28408                 tag : 'span',
28409                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
28410             });
28411             
28412             var bottomText = new Roo.bootstrap.Element({
28413                 tag : 'span',
28414                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
28415             });
28416             
28417             topText.onRender(top.el, null);
28418             bottomText.onRender(bottom.el, null);
28419             
28420             item.topEl = top;
28421             item.bottomEl = bottom;
28422         }
28423         
28424         this.barItems.push(item);
28425         
28426         return item;
28427     },
28428     
28429     getActive : function()
28430     {
28431         var active = false;
28432         
28433         Roo.each(this.barItems, function(v){
28434             
28435             if (!v.isActive()) {
28436                 return;
28437             }
28438             
28439             active = v;
28440             return false;
28441             
28442         });
28443         
28444         return active;
28445     },
28446     
28447     setActiveItem : function(item)
28448     {
28449         var prev = false;
28450         
28451         Roo.each(this.barItems, function(v){
28452             if (v.rid == item.rid) {
28453                 return ;
28454             }
28455             
28456             if (v.isActive()) {
28457                 v.setActive(false);
28458                 prev = v;
28459             }
28460         });
28461
28462         item.setActive(true);
28463         
28464         this.fireEvent('changed', this, item, prev);
28465     },
28466     
28467     getBarItem: function(rid)
28468     {
28469         var ret = false;
28470         
28471         Roo.each(this.barItems, function(e) {
28472             if (e.rid != rid) {
28473                 return;
28474             }
28475             
28476             ret =  e;
28477             return false;
28478         });
28479         
28480         return ret;
28481     },
28482     
28483     indexOfItem : function(item)
28484     {
28485         var index = false;
28486         
28487         Roo.each(this.barItems, function(v, i){
28488             
28489             if (v.rid != item.rid) {
28490                 return;
28491             }
28492             
28493             index = i;
28494             return false
28495         });
28496         
28497         return index;
28498     },
28499     
28500     setActiveNext : function()
28501     {
28502         var i = this.indexOfItem(this.getActive());
28503         
28504         if (i > this.barItems.length) {
28505             return;
28506         }
28507         
28508         this.setActiveItem(this.barItems[i+1]);
28509     },
28510     
28511     setActivePrev : function()
28512     {
28513         var i = this.indexOfItem(this.getActive());
28514         
28515         if (i  < 1) {
28516             return;
28517         }
28518         
28519         this.setActiveItem(this.barItems[i-1]);
28520     },
28521     
28522     format : function()
28523     {
28524         if(!this.barItems.length){
28525             return;
28526         }
28527      
28528         var width = 100 / this.barItems.length;
28529         
28530         Roo.each(this.barItems, function(i){
28531             i.el.setStyle('width', width + '%');
28532             i.topEl.el.setStyle('width', width + '%');
28533             i.bottomEl.el.setStyle('width', width + '%');
28534         }, this);
28535         
28536     }
28537     
28538 });
28539 /*
28540  * - LGPL
28541  *
28542  * Nav Progress Item
28543  * 
28544  */
28545
28546 /**
28547  * @class Roo.bootstrap.NavProgressItem
28548  * @extends Roo.bootstrap.Component
28549  * Bootstrap NavProgressItem class
28550  * @cfg {String} rid the reference id
28551  * @cfg {Boolean} active (true|false) Is item active default false
28552  * @cfg {Boolean} disabled (true|false) Is item active default false
28553  * @cfg {String} html
28554  * @cfg {String} position (top|bottom) text position default bottom
28555  * @cfg {String} icon show icon instead of number
28556  * 
28557  * @constructor
28558  * Create a new NavProgressItem
28559  * @param {Object} config The config object
28560  */
28561 Roo.bootstrap.NavProgressItem = function(config){
28562     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
28563     this.addEvents({
28564         // raw events
28565         /**
28566          * @event click
28567          * The raw click event for the entire grid.
28568          * @param {Roo.bootstrap.NavProgressItem} this
28569          * @param {Roo.EventObject} e
28570          */
28571         "click" : true
28572     });
28573    
28574 };
28575
28576 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
28577     
28578     rid : '',
28579     active : false,
28580     disabled : false,
28581     html : '',
28582     position : 'bottom',
28583     icon : false,
28584     
28585     getAutoCreate : function()
28586     {
28587         var iconCls = 'roo-navigation-bar-item-icon';
28588         
28589         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
28590         
28591         var cfg = {
28592             tag: 'li',
28593             cls: 'roo-navigation-bar-item',
28594             cn : [
28595                 {
28596                     tag : 'i',
28597                     cls : iconCls
28598                 }
28599             ]
28600         };
28601         
28602         if(this.active){
28603             cfg.cls += ' active';
28604         }
28605         if(this.disabled){
28606             cfg.cls += ' disabled';
28607         }
28608         
28609         return cfg;
28610     },
28611     
28612     disable : function()
28613     {
28614         this.setDisabled(true);
28615     },
28616     
28617     enable : function()
28618     {
28619         this.setDisabled(false);
28620     },
28621     
28622     initEvents: function() 
28623     {
28624         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
28625         
28626         this.iconEl.on('click', this.onClick, this);
28627     },
28628     
28629     onClick : function(e)
28630     {
28631         e.preventDefault();
28632         
28633         if(this.disabled){
28634             return;
28635         }
28636         
28637         if(this.fireEvent('click', this, e) === false){
28638             return;
28639         };
28640         
28641         this.parent().setActiveItem(this);
28642     },
28643     
28644     isActive: function () 
28645     {
28646         return this.active;
28647     },
28648     
28649     setActive : function(state)
28650     {
28651         if(this.active == state){
28652             return;
28653         }
28654         
28655         this.active = state;
28656         
28657         if (state) {
28658             this.el.addClass('active');
28659             return;
28660         }
28661         
28662         this.el.removeClass('active');
28663         
28664         return;
28665     },
28666     
28667     setDisabled : function(state)
28668     {
28669         if(this.disabled == state){
28670             return;
28671         }
28672         
28673         this.disabled = state;
28674         
28675         if (state) {
28676             this.el.addClass('disabled');
28677             return;
28678         }
28679         
28680         this.el.removeClass('disabled');
28681     },
28682     
28683     tooltipEl : function()
28684     {
28685         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
28686     }
28687 });
28688  
28689
28690  /*
28691  * - LGPL
28692  *
28693  * FieldLabel
28694  * 
28695  */
28696
28697 /**
28698  * @class Roo.bootstrap.FieldLabel
28699  * @extends Roo.bootstrap.Component
28700  * Bootstrap FieldLabel class
28701  * @cfg {String} html contents of the element
28702  * @cfg {String} tag tag of the element default label
28703  * @cfg {String} cls class of the element
28704  * @cfg {String} target label target 
28705  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
28706  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
28707  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
28708  * @cfg {String} iconTooltip default "This field is required"
28709  * 
28710  * @constructor
28711  * Create a new FieldLabel
28712  * @param {Object} config The config object
28713  */
28714
28715 Roo.bootstrap.FieldLabel = function(config){
28716     Roo.bootstrap.Element.superclass.constructor.call(this, config);
28717     
28718     this.addEvents({
28719             /**
28720              * @event invalid
28721              * Fires after the field has been marked as invalid.
28722              * @param {Roo.form.FieldLabel} this
28723              * @param {String} msg The validation message
28724              */
28725             invalid : true,
28726             /**
28727              * @event valid
28728              * Fires after the field has been validated with no errors.
28729              * @param {Roo.form.FieldLabel} this
28730              */
28731             valid : true
28732         });
28733 };
28734
28735 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
28736     
28737     tag: 'label',
28738     cls: '',
28739     html: '',
28740     target: '',
28741     allowBlank : true,
28742     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
28743     validClass : 'text-success fa fa-lg fa-check',
28744     iconTooltip : 'This field is required',
28745     
28746     getAutoCreate : function(){
28747         
28748         var cfg = {
28749             tag : this.tag,
28750             cls : 'roo-bootstrap-field-label ' + this.cls,
28751             for : this.target,
28752             cn : [
28753                 {
28754                     tag : 'i',
28755                     cls : '',
28756                     tooltip : this.iconTooltip
28757                 },
28758                 {
28759                     tag : 'span',
28760                     html : this.html
28761                 }
28762             ] 
28763         };
28764         
28765         return cfg;
28766     },
28767     
28768     initEvents: function() 
28769     {
28770         Roo.bootstrap.Element.superclass.initEvents.call(this);
28771         
28772         this.iconEl = this.el.select('i', true).first();
28773         
28774         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
28775         
28776         Roo.bootstrap.FieldLabel.register(this);
28777     },
28778     
28779     /**
28780      * Mark this field as valid
28781      */
28782     markValid : function()
28783     {
28784         this.iconEl.show();
28785         
28786         this.iconEl.removeClass(this.invalidClass);
28787         
28788         this.iconEl.addClass(this.validClass);
28789         
28790         this.fireEvent('valid', this);
28791     },
28792     
28793     /**
28794      * Mark this field as invalid
28795      * @param {String} msg The validation message
28796      */
28797     markInvalid : function(msg)
28798     {
28799         this.iconEl.show();
28800         
28801         this.iconEl.removeClass(this.validClass);
28802         
28803         this.iconEl.addClass(this.invalidClass);
28804         
28805         this.fireEvent('invalid', this, msg);
28806     }
28807     
28808    
28809 });
28810
28811 Roo.apply(Roo.bootstrap.FieldLabel, {
28812     
28813     groups: {},
28814     
28815      /**
28816     * register a FieldLabel Group
28817     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28818     */
28819     register : function(label)
28820     {
28821         if(this.groups.hasOwnProperty(label.target)){
28822             return;
28823         }
28824      
28825         this.groups[label.target] = label;
28826         
28827     },
28828     /**
28829     * fetch a FieldLabel Group based on the target
28830     * @param {string} target
28831     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28832     */
28833     get: function(target) {
28834         if (typeof(this.groups[target]) == 'undefined') {
28835             return false;
28836         }
28837         
28838         return this.groups[target] ;
28839     }
28840 });
28841
28842  
28843
28844  /*
28845  * - LGPL
28846  *
28847  * page DateSplitField.
28848  * 
28849  */
28850
28851
28852 /**
28853  * @class Roo.bootstrap.DateSplitField
28854  * @extends Roo.bootstrap.Component
28855  * Bootstrap DateSplitField class
28856  * @cfg {string} fieldLabel - the label associated
28857  * @cfg {Number} labelWidth set the width of label (0-12)
28858  * @cfg {String} labelAlign (top|left)
28859  * @cfg {Boolean} dayAllowBlank (true|false) default false
28860  * @cfg {Boolean} monthAllowBlank (true|false) default false
28861  * @cfg {Boolean} yearAllowBlank (true|false) default false
28862  * @cfg {string} dayPlaceholder 
28863  * @cfg {string} monthPlaceholder
28864  * @cfg {string} yearPlaceholder
28865  * @cfg {string} dayFormat default 'd'
28866  * @cfg {string} monthFormat default 'm'
28867  * @cfg {string} yearFormat default 'Y'
28868
28869  *     
28870  * @constructor
28871  * Create a new DateSplitField
28872  * @param {Object} config The config object
28873  */
28874
28875 Roo.bootstrap.DateSplitField = function(config){
28876     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28877     
28878     this.addEvents({
28879         // raw events
28880          /**
28881          * @event years
28882          * getting the data of years
28883          * @param {Roo.bootstrap.DateSplitField} this
28884          * @param {Object} years
28885          */
28886         "years" : true,
28887         /**
28888          * @event days
28889          * getting the data of days
28890          * @param {Roo.bootstrap.DateSplitField} this
28891          * @param {Object} days
28892          */
28893         "days" : true,
28894         /**
28895          * @event invalid
28896          * Fires after the field has been marked as invalid.
28897          * @param {Roo.form.Field} this
28898          * @param {String} msg The validation message
28899          */
28900         invalid : true,
28901        /**
28902          * @event valid
28903          * Fires after the field has been validated with no errors.
28904          * @param {Roo.form.Field} this
28905          */
28906         valid : true
28907     });
28908 };
28909
28910 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
28911     
28912     fieldLabel : '',
28913     labelAlign : 'top',
28914     labelWidth : 3,
28915     dayAllowBlank : false,
28916     monthAllowBlank : false,
28917     yearAllowBlank : false,
28918     dayPlaceholder : '',
28919     monthPlaceholder : '',
28920     yearPlaceholder : '',
28921     dayFormat : 'd',
28922     monthFormat : 'm',
28923     yearFormat : 'Y',
28924     isFormField : true,
28925     
28926     getAutoCreate : function()
28927     {
28928         var cfg = {
28929             tag : 'div',
28930             cls : 'row roo-date-split-field-group',
28931             cn : [
28932                 {
28933                     tag : 'input',
28934                     type : 'hidden',
28935                     cls : 'form-hidden-field roo-date-split-field-group-value',
28936                     name : this.name
28937                 }
28938             ]
28939         };
28940         
28941         if(this.fieldLabel){
28942             cfg.cn.push({
28943                 tag : 'div',
28944                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
28945                 cn : [
28946                     {
28947                         tag : 'label',
28948                         html : this.fieldLabel
28949                     }
28950                 ]
28951             });
28952         }
28953         
28954         Roo.each(['day', 'month', 'year'], function(t){
28955             cfg.cn.push({
28956                 tag : 'div',
28957                 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
28958             });
28959         }, this);
28960         
28961         return cfg;
28962     },
28963     
28964     inputEl: function ()
28965     {
28966         return this.el.select('.roo-date-split-field-group-value', true).first();
28967     },
28968     
28969     onRender : function(ct, position) 
28970     {
28971         var _this = this;
28972         
28973         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28974         
28975         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
28976         
28977         this.dayField = new Roo.bootstrap.ComboBox({
28978             allowBlank : this.dayAllowBlank,
28979             alwaysQuery : true,
28980             displayField : 'value',
28981             editable : false,
28982             fieldLabel : '',
28983             forceSelection : true,
28984             mode : 'local',
28985             placeholder : this.dayPlaceholder,
28986             selectOnFocus : true,
28987             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28988             triggerAction : 'all',
28989             typeAhead : true,
28990             valueField : 'value',
28991             store : new Roo.data.SimpleStore({
28992                 data : (function() {    
28993                     var days = [];
28994                     _this.fireEvent('days', _this, days);
28995                     return days;
28996                 })(),
28997                 fields : [ 'value' ]
28998             }),
28999             listeners : {
29000                 select : function (_self, record, index)
29001                 {
29002                     _this.setValue(_this.getValue());
29003                 }
29004             }
29005         });
29006
29007         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
29008         
29009         this.monthField = new Roo.bootstrap.MonthField({
29010             after : '<i class=\"fa fa-calendar\"></i>',
29011             allowBlank : this.monthAllowBlank,
29012             placeholder : this.monthPlaceholder,
29013             readOnly : true,
29014             listeners : {
29015                 render : function (_self)
29016                 {
29017                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
29018                         e.preventDefault();
29019                         _self.focus();
29020                     });
29021                 },
29022                 select : function (_self, oldvalue, newvalue)
29023                 {
29024                     _this.setValue(_this.getValue());
29025                 }
29026             }
29027         });
29028         
29029         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
29030         
29031         this.yearField = new Roo.bootstrap.ComboBox({
29032             allowBlank : this.yearAllowBlank,
29033             alwaysQuery : true,
29034             displayField : 'value',
29035             editable : false,
29036             fieldLabel : '',
29037             forceSelection : true,
29038             mode : 'local',
29039             placeholder : this.yearPlaceholder,
29040             selectOnFocus : true,
29041             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29042             triggerAction : 'all',
29043             typeAhead : true,
29044             valueField : 'value',
29045             store : new Roo.data.SimpleStore({
29046                 data : (function() {
29047                     var years = [];
29048                     _this.fireEvent('years', _this, years);
29049                     return years;
29050                 })(),
29051                 fields : [ 'value' ]
29052             }),
29053             listeners : {
29054                 select : function (_self, record, index)
29055                 {
29056                     _this.setValue(_this.getValue());
29057                 }
29058             }
29059         });
29060
29061         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
29062     },
29063     
29064     setValue : function(v, format)
29065     {
29066         this.inputEl.dom.value = v;
29067         
29068         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
29069         
29070         var d = Date.parseDate(v, f);
29071         
29072         if(!d){
29073             this.validate();
29074             return;
29075         }
29076         
29077         this.setDay(d.format(this.dayFormat));
29078         this.setMonth(d.format(this.monthFormat));
29079         this.setYear(d.format(this.yearFormat));
29080         
29081         this.validate();
29082         
29083         return;
29084     },
29085     
29086     setDay : function(v)
29087     {
29088         this.dayField.setValue(v);
29089         this.inputEl.dom.value = this.getValue();
29090         this.validate();
29091         return;
29092     },
29093     
29094     setMonth : function(v)
29095     {
29096         this.monthField.setValue(v, true);
29097         this.inputEl.dom.value = this.getValue();
29098         this.validate();
29099         return;
29100     },
29101     
29102     setYear : function(v)
29103     {
29104         this.yearField.setValue(v);
29105         this.inputEl.dom.value = this.getValue();
29106         this.validate();
29107         return;
29108     },
29109     
29110     getDay : function()
29111     {
29112         return this.dayField.getValue();
29113     },
29114     
29115     getMonth : function()
29116     {
29117         return this.monthField.getValue();
29118     },
29119     
29120     getYear : function()
29121     {
29122         return this.yearField.getValue();
29123     },
29124     
29125     getValue : function()
29126     {
29127         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
29128         
29129         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
29130         
29131         return date;
29132     },
29133     
29134     reset : function()
29135     {
29136         this.setDay('');
29137         this.setMonth('');
29138         this.setYear('');
29139         this.inputEl.dom.value = '';
29140         this.validate();
29141         return;
29142     },
29143     
29144     validate : function()
29145     {
29146         var d = this.dayField.validate();
29147         var m = this.monthField.validate();
29148         var y = this.yearField.validate();
29149         
29150         var valid = true;
29151         
29152         if(
29153                 (!this.dayAllowBlank && !d) ||
29154                 (!this.monthAllowBlank && !m) ||
29155                 (!this.yearAllowBlank && !y)
29156         ){
29157             valid = false;
29158         }
29159         
29160         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
29161             return valid;
29162         }
29163         
29164         if(valid){
29165             this.markValid();
29166             return valid;
29167         }
29168         
29169         this.markInvalid();
29170         
29171         return valid;
29172     },
29173     
29174     markValid : function()
29175     {
29176         
29177         var label = this.el.select('label', true).first();
29178         var icon = this.el.select('i.fa-star', true).first();
29179
29180         if(label && icon){
29181             icon.remove();
29182         }
29183         
29184         this.fireEvent('valid', this);
29185     },
29186     
29187      /**
29188      * Mark this field as invalid
29189      * @param {String} msg The validation message
29190      */
29191     markInvalid : function(msg)
29192     {
29193         
29194         var label = this.el.select('label', true).first();
29195         var icon = this.el.select('i.fa-star', true).first();
29196
29197         if(label && !icon){
29198             this.el.select('.roo-date-split-field-label', true).createChild({
29199                 tag : 'i',
29200                 cls : 'text-danger fa fa-lg fa-star',
29201                 tooltip : 'This field is required',
29202                 style : 'margin-right:5px;'
29203             }, label, true);
29204         }
29205         
29206         this.fireEvent('invalid', this, msg);
29207     },
29208     
29209     clearInvalid : function()
29210     {
29211         var label = this.el.select('label', true).first();
29212         var icon = this.el.select('i.fa-star', true).first();
29213
29214         if(label && icon){
29215             icon.remove();
29216         }
29217         
29218         this.fireEvent('valid', this);
29219     },
29220     
29221     getName: function()
29222     {
29223         return this.name;
29224     }
29225     
29226 });
29227
29228  /**
29229  *
29230  * This is based on 
29231  * http://masonry.desandro.com
29232  *
29233  * The idea is to render all the bricks based on vertical width...
29234  *
29235  * The original code extends 'outlayer' - we might need to use that....
29236  * 
29237  */
29238
29239
29240 /**
29241  * @class Roo.bootstrap.LayoutMasonry
29242  * @extends Roo.bootstrap.Component
29243  * Bootstrap Layout Masonry class
29244  * 
29245  * @constructor
29246  * Create a new Element
29247  * @param {Object} config The config object
29248  */
29249
29250 Roo.bootstrap.LayoutMasonry = function(config){
29251     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
29252     
29253     this.bricks = [];
29254     
29255 };
29256
29257 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
29258     
29259     /**
29260      * @cfg {Boolean} isLayoutInstant = no animation?
29261      */   
29262     isLayoutInstant : false, // needed?
29263    
29264     /**
29265      * @cfg {Number} boxWidth  width of the columns
29266      */   
29267     boxWidth : 450,
29268     
29269       /**
29270      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
29271      */   
29272     boxHeight : 0,
29273     
29274     /**
29275      * @cfg {Number} padWidth padding below box..
29276      */   
29277     padWidth : 10, 
29278     
29279     /**
29280      * @cfg {Number} gutter gutter width..
29281      */   
29282     gutter : 10,
29283     
29284      /**
29285      * @cfg {Number} maxCols maximum number of columns
29286      */   
29287     
29288     maxCols: 0,
29289     
29290     /**
29291      * @cfg {Boolean} isAutoInitial defalut true
29292      */   
29293     isAutoInitial : true, 
29294     
29295     containerWidth: 0,
29296     
29297     /**
29298      * @cfg {Boolean} isHorizontal defalut false
29299      */   
29300     isHorizontal : false, 
29301
29302     currentSize : null,
29303     
29304     tag: 'div',
29305     
29306     cls: '',
29307     
29308     bricks: null, //CompositeElement
29309     
29310     cols : 1,
29311     
29312     _isLayoutInited : false,
29313     
29314 //    isAlternative : false, // only use for vertical layout...
29315     
29316     /**
29317      * @cfg {Number} alternativePadWidth padding below box..
29318      */   
29319     alternativePadWidth : 50, 
29320     
29321     getAutoCreate : function(){
29322         
29323         var cfg = {
29324             tag: this.tag,
29325             cls: 'blog-masonary-wrapper ' + this.cls,
29326             cn : {
29327                 cls : 'mas-boxes masonary'
29328             }
29329         };
29330         
29331         return cfg;
29332     },
29333     
29334     getChildContainer: function( )
29335     {
29336         if (this.boxesEl) {
29337             return this.boxesEl;
29338         }
29339         
29340         this.boxesEl = this.el.select('.mas-boxes').first();
29341         
29342         return this.boxesEl;
29343     },
29344     
29345     
29346     initEvents : function()
29347     {
29348         var _this = this;
29349         
29350         if(this.isAutoInitial){
29351             Roo.log('hook children rendered');
29352             this.on('childrenrendered', function() {
29353                 Roo.log('children rendered');
29354                 _this.initial();
29355             } ,this);
29356         }
29357     },
29358     
29359     initial : function()
29360     {
29361         this.currentSize = this.el.getBox(true);
29362         
29363         Roo.EventManager.onWindowResize(this.resize, this); 
29364
29365         if(!this.isAutoInitial){
29366             this.layout();
29367             return;
29368         }
29369         
29370         this.layout();
29371         
29372         return;
29373         //this.layout.defer(500,this);
29374         
29375     },
29376     
29377     resize : function()
29378     {
29379         Roo.log('resize');
29380         
29381         var cs = this.el.getBox(true);
29382         
29383         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29384             Roo.log("no change in with or X");
29385             return;
29386         }
29387         
29388         this.currentSize = cs;
29389         
29390         this.layout();
29391         
29392     },
29393     
29394     layout : function()
29395     {   
29396         this._resetLayout();
29397         
29398         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29399         
29400         this.layoutItems( isInstant );
29401       
29402         this._isLayoutInited = true;
29403         
29404     },
29405     
29406     _resetLayout : function()
29407     {
29408         if(this.isHorizontal){
29409             this.horizontalMeasureColumns();
29410             return;
29411         }
29412         
29413         this.verticalMeasureColumns();
29414         
29415     },
29416     
29417     verticalMeasureColumns : function()
29418     {
29419         this.getContainerWidth();
29420         
29421 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29422 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
29423 //            return;
29424 //        }
29425         
29426         var boxWidth = this.boxWidth + this.padWidth;
29427         
29428         if(this.containerWidth < this.boxWidth){
29429             boxWidth = this.containerWidth
29430         }
29431         
29432         var containerWidth = this.containerWidth;
29433         
29434         var cols = Math.floor(containerWidth / boxWidth);
29435         
29436         this.cols = Math.max( cols, 1 );
29437         
29438         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
29439         
29440         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
29441         
29442         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
29443         
29444         this.colWidth = boxWidth + avail - this.padWidth;
29445         
29446         this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
29447         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
29448     },
29449     
29450     horizontalMeasureColumns : function()
29451     {
29452         this.getContainerWidth();
29453         
29454         var boxWidth = this.boxWidth;
29455         
29456         if(this.containerWidth < boxWidth){
29457             boxWidth = this.containerWidth;
29458         }
29459         
29460         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
29461         
29462         this.el.setHeight(boxWidth);
29463         
29464     },
29465     
29466     getContainerWidth : function()
29467     {
29468         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
29469     },
29470     
29471     layoutItems : function( isInstant )
29472     {
29473         var items = Roo.apply([], this.bricks);
29474         
29475         if(this.isHorizontal){
29476             this._horizontalLayoutItems( items , isInstant );
29477             return;
29478         }
29479         
29480 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29481 //            this._verticalAlternativeLayoutItems( items , isInstant );
29482 //            return;
29483 //        }
29484         
29485         this._verticalLayoutItems( items , isInstant );
29486         
29487     },
29488     
29489     _verticalLayoutItems : function ( items , isInstant)
29490     {
29491         if ( !items || !items.length ) {
29492             return;
29493         }
29494         
29495         var standard = [
29496             ['xs', 'xs', 'xs', 'tall'],
29497             ['xs', 'xs', 'tall'],
29498             ['xs', 'xs', 'sm'],
29499             ['xs', 'xs', 'xs'],
29500             ['xs', 'tall'],
29501             ['xs', 'sm'],
29502             ['xs', 'xs'],
29503             ['xs'],
29504             
29505             ['sm', 'xs', 'xs'],
29506             ['sm', 'xs'],
29507             ['sm'],
29508             
29509             ['tall', 'xs', 'xs', 'xs'],
29510             ['tall', 'xs', 'xs'],
29511             ['tall', 'xs'],
29512             ['tall']
29513             
29514         ];
29515         
29516         var queue = [];
29517         
29518         var boxes = [];
29519         
29520         var box = [];
29521         
29522         Roo.each(items, function(item, k){
29523             
29524             switch (item.size) {
29525                 // these layouts take up a full box,
29526                 case 'md' :
29527                 case 'md-left' :
29528                 case 'md-right' :
29529                 case 'wide' :
29530                     
29531                     if(box.length){
29532                         boxes.push(box);
29533                         box = [];
29534                     }
29535                     
29536                     boxes.push([item]);
29537                     
29538                     break;
29539                     
29540                 case 'xs' :
29541                 case 'sm' :
29542                 case 'tall' :
29543                     
29544                     box.push(item);
29545                     
29546                     break;
29547                 default :
29548                     break;
29549                     
29550             }
29551             
29552         }, this);
29553         
29554         if(box.length){
29555             boxes.push(box);
29556             box = [];
29557         }
29558         
29559         var filterPattern = function(box, length)
29560         {
29561             if(!box.length){
29562                 return;
29563             }
29564             
29565             var match = false;
29566             
29567             var pattern = box.slice(0, length);
29568             
29569             var format = [];
29570             
29571             Roo.each(pattern, function(i){
29572                 format.push(i.size);
29573             }, this);
29574             
29575             Roo.each(standard, function(s){
29576                 
29577                 if(String(s) != String(format)){
29578                     return;
29579                 }
29580                 
29581                 match = true;
29582                 return false;
29583                 
29584             }, this);
29585             
29586             if(!match && length == 1){
29587                 return;
29588             }
29589             
29590             if(!match){
29591                 filterPattern(box, length - 1);
29592                 return;
29593             }
29594                 
29595             queue.push(pattern);
29596
29597             box = box.slice(length, box.length);
29598
29599             filterPattern(box, 4);
29600
29601             return;
29602             
29603         }
29604         
29605         Roo.each(boxes, function(box, k){
29606             
29607             if(!box.length){
29608                 return;
29609             }
29610             
29611             if(box.length == 1){
29612                 queue.push(box);
29613                 return;
29614             }
29615             
29616             filterPattern(box, 4);
29617             
29618         }, this);
29619         
29620         this._processVerticalLayoutQueue( queue, isInstant );
29621         
29622     },
29623     
29624 //    _verticalAlternativeLayoutItems : function( items , isInstant )
29625 //    {
29626 //        if ( !items || !items.length ) {
29627 //            return;
29628 //        }
29629 //
29630 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
29631 //        
29632 //    },
29633     
29634     _horizontalLayoutItems : function ( items , isInstant)
29635     {
29636         if ( !items || !items.length || items.length < 3) {
29637             return;
29638         }
29639         
29640         items.reverse();
29641         
29642         var eItems = items.slice(0, 3);
29643         
29644         items = items.slice(3, items.length);
29645         
29646         var standard = [
29647             ['xs', 'xs', 'xs', 'wide'],
29648             ['xs', 'xs', 'wide'],
29649             ['xs', 'xs', 'sm'],
29650             ['xs', 'xs', 'xs'],
29651             ['xs', 'wide'],
29652             ['xs', 'sm'],
29653             ['xs', 'xs'],
29654             ['xs'],
29655             
29656             ['sm', 'xs', 'xs'],
29657             ['sm', 'xs'],
29658             ['sm'],
29659             
29660             ['wide', 'xs', 'xs', 'xs'],
29661             ['wide', 'xs', 'xs'],
29662             ['wide', 'xs'],
29663             ['wide'],
29664             
29665             ['wide-thin']
29666         ];
29667         
29668         var queue = [];
29669         
29670         var boxes = [];
29671         
29672         var box = [];
29673         
29674         Roo.each(items, function(item, k){
29675             
29676             switch (item.size) {
29677                 case 'md' :
29678                 case 'md-left' :
29679                 case 'md-right' :
29680                 case 'tall' :
29681                     
29682                     if(box.length){
29683                         boxes.push(box);
29684                         box = [];
29685                     }
29686                     
29687                     boxes.push([item]);
29688                     
29689                     break;
29690                     
29691                 case 'xs' :
29692                 case 'sm' :
29693                 case 'wide' :
29694                 case 'wide-thin' :
29695                     
29696                     box.push(item);
29697                     
29698                     break;
29699                 default :
29700                     break;
29701                     
29702             }
29703             
29704         }, this);
29705         
29706         if(box.length){
29707             boxes.push(box);
29708             box = [];
29709         }
29710         
29711         var filterPattern = function(box, length)
29712         {
29713             if(!box.length){
29714                 return;
29715             }
29716             
29717             var match = false;
29718             
29719             var pattern = box.slice(0, length);
29720             
29721             var format = [];
29722             
29723             Roo.each(pattern, function(i){
29724                 format.push(i.size);
29725             }, this);
29726             
29727             Roo.each(standard, function(s){
29728                 
29729                 if(String(s) != String(format)){
29730                     return;
29731                 }
29732                 
29733                 match = true;
29734                 return false;
29735                 
29736             }, this);
29737             
29738             if(!match && length == 1){
29739                 return;
29740             }
29741             
29742             if(!match){
29743                 filterPattern(box, length - 1);
29744                 return;
29745             }
29746                 
29747             queue.push(pattern);
29748
29749             box = box.slice(length, box.length);
29750
29751             filterPattern(box, 4);
29752
29753             return;
29754             
29755         }
29756         
29757         Roo.each(boxes, function(box, k){
29758             
29759             if(!box.length){
29760                 return;
29761             }
29762             
29763             if(box.length == 1){
29764                 queue.push(box);
29765                 return;
29766             }
29767             
29768             filterPattern(box, 4);
29769             
29770         }, this);
29771         
29772         
29773         var prune = [];
29774         
29775         var pos = this.el.getBox(true);
29776         
29777         var minX = pos.x;
29778         
29779         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29780         
29781         var hit_end = false;
29782         
29783         Roo.each(queue, function(box){
29784             
29785             if(hit_end){
29786                 
29787                 Roo.each(box, function(b){
29788                 
29789                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29790                     b.el.hide();
29791
29792                 }, this);
29793
29794                 return;
29795             }
29796             
29797             var mx = 0;
29798             
29799             Roo.each(box, function(b){
29800                 
29801                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29802                 b.el.show();
29803
29804                 mx = Math.max(mx, b.x);
29805                 
29806             }, this);
29807             
29808             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29809             
29810             if(maxX < minX){
29811                 
29812                 Roo.each(box, function(b){
29813                 
29814                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29815                     b.el.hide();
29816                     
29817                 }, this);
29818                 
29819                 hit_end = true;
29820                 
29821                 return;
29822             }
29823             
29824             prune.push(box);
29825             
29826         }, this);
29827         
29828         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29829     },
29830     
29831     /** Sets position of item in DOM
29832     * @param {Element} item
29833     * @param {Number} x - horizontal position
29834     * @param {Number} y - vertical position
29835     * @param {Boolean} isInstant - disables transitions
29836     */
29837     _processVerticalLayoutQueue : function( queue, isInstant )
29838     {
29839         var pos = this.el.getBox(true);
29840         var x = pos.x;
29841         var y = pos.y;
29842         var maxY = [];
29843         
29844         for (var i = 0; i < this.cols; i++){
29845             maxY[i] = pos.y;
29846         }
29847         
29848         Roo.each(queue, function(box, k){
29849             
29850             var col = k % this.cols;
29851             
29852             Roo.each(box, function(b,kk){
29853                 
29854                 b.el.position('absolute');
29855                 
29856                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29857                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29858                 
29859                 if(b.size == 'md-left' || b.size == 'md-right'){
29860                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29861                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29862                 }
29863                 
29864                 b.el.setWidth(width);
29865                 b.el.setHeight(height);
29866                 // iframe?
29867                 b.el.select('iframe',true).setSize(width,height);
29868                 
29869             }, this);
29870             
29871             for (var i = 0; i < this.cols; i++){
29872                 
29873                 if(maxY[i] < maxY[col]){
29874                     col = i;
29875                     continue;
29876                 }
29877                 
29878                 col = Math.min(col, i);
29879                 
29880             }
29881             
29882             x = pos.x + col * (this.colWidth + this.padWidth);
29883             
29884             y = maxY[col];
29885             
29886             var positions = [];
29887             
29888             switch (box.length){
29889                 case 1 :
29890                     positions = this.getVerticalOneBoxColPositions(x, y, box);
29891                     break;
29892                 case 2 :
29893                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
29894                     break;
29895                 case 3 :
29896                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
29897                     break;
29898                 case 4 :
29899                     positions = this.getVerticalFourBoxColPositions(x, y, box);
29900                     break;
29901                 default :
29902                     break;
29903             }
29904             
29905             Roo.each(box, function(b,kk){
29906                 
29907                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29908                 
29909                 var sz = b.el.getSize();
29910                 
29911                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29912                 
29913             }, this);
29914             
29915         }, this);
29916         
29917         var mY = 0;
29918         
29919         for (var i = 0; i < this.cols; i++){
29920             mY = Math.max(mY, maxY[i]);
29921         }
29922         
29923         this.el.setHeight(mY - pos.y);
29924         
29925     },
29926     
29927 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29928 //    {
29929 //        var pos = this.el.getBox(true);
29930 //        var x = pos.x;
29931 //        var y = pos.y;
29932 //        var maxX = pos.right;
29933 //        
29934 //        var maxHeight = 0;
29935 //        
29936 //        Roo.each(items, function(item, k){
29937 //            
29938 //            var c = k % 2;
29939 //            
29940 //            item.el.position('absolute');
29941 //                
29942 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29943 //
29944 //            item.el.setWidth(width);
29945 //
29946 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
29947 //
29948 //            item.el.setHeight(height);
29949 //            
29950 //            if(c == 0){
29951 //                item.el.setXY([x, y], isInstant ? false : true);
29952 //            } else {
29953 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
29954 //            }
29955 //            
29956 //            y = y + height + this.alternativePadWidth;
29957 //            
29958 //            maxHeight = maxHeight + height + this.alternativePadWidth;
29959 //            
29960 //        }, this);
29961 //        
29962 //        this.el.setHeight(maxHeight);
29963 //        
29964 //    },
29965     
29966     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
29967     {
29968         var pos = this.el.getBox(true);
29969         
29970         var minX = pos.x;
29971         var minY = pos.y;
29972         
29973         var maxX = pos.right;
29974         
29975         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
29976         
29977         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29978         
29979         Roo.each(queue, function(box, k){
29980             
29981             Roo.each(box, function(b, kk){
29982                 
29983                 b.el.position('absolute');
29984                 
29985                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29986                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29987                 
29988                 if(b.size == 'md-left' || b.size == 'md-right'){
29989                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29990                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29991                 }
29992                 
29993                 b.el.setWidth(width);
29994                 b.el.setHeight(height);
29995                 
29996             }, this);
29997             
29998             if(!box.length){
29999                 return;
30000             }
30001             
30002             var positions = [];
30003             
30004             switch (box.length){
30005                 case 1 :
30006                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
30007                     break;
30008                 case 2 :
30009                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
30010                     break;
30011                 case 3 :
30012                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
30013                     break;
30014                 case 4 :
30015                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
30016                     break;
30017                 default :
30018                     break;
30019             }
30020             
30021             Roo.each(box, function(b,kk){
30022                 
30023                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30024                 
30025                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
30026                 
30027             }, this);
30028             
30029         }, this);
30030         
30031     },
30032     
30033     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
30034     {
30035         Roo.each(eItems, function(b,k){
30036             
30037             b.size = (k == 0) ? 'sm' : 'xs';
30038             b.x = (k == 0) ? 2 : 1;
30039             b.y = (k == 0) ? 2 : 1;
30040             
30041             b.el.position('absolute');
30042             
30043             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30044                 
30045             b.el.setWidth(width);
30046             
30047             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30048             
30049             b.el.setHeight(height);
30050             
30051         }, this);
30052
30053         var positions = [];
30054         
30055         positions.push({
30056             x : maxX - this.unitWidth * 2 - this.gutter,
30057             y : minY
30058         });
30059         
30060         positions.push({
30061             x : maxX - this.unitWidth,
30062             y : minY + (this.unitWidth + this.gutter) * 2
30063         });
30064         
30065         positions.push({
30066             x : maxX - this.unitWidth * 3 - this.gutter * 2,
30067             y : minY
30068         });
30069         
30070         Roo.each(eItems, function(b,k){
30071             
30072             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
30073
30074         }, this);
30075         
30076     },
30077     
30078     getVerticalOneBoxColPositions : function(x, y, box)
30079     {
30080         var pos = [];
30081         
30082         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
30083         
30084         if(box[0].size == 'md-left'){
30085             rand = 0;
30086         }
30087         
30088         if(box[0].size == 'md-right'){
30089             rand = 1;
30090         }
30091         
30092         pos.push({
30093             x : x + (this.unitWidth + this.gutter) * rand,
30094             y : y
30095         });
30096         
30097         return pos;
30098     },
30099     
30100     getVerticalTwoBoxColPositions : function(x, y, box)
30101     {
30102         var pos = [];
30103         
30104         if(box[0].size == 'xs'){
30105             
30106             pos.push({
30107                 x : x,
30108                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
30109             });
30110
30111             pos.push({
30112                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
30113                 y : y
30114             });
30115             
30116             return pos;
30117             
30118         }
30119         
30120         pos.push({
30121             x : x,
30122             y : y
30123         });
30124
30125         pos.push({
30126             x : x + (this.unitWidth + this.gutter) * 2,
30127             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
30128         });
30129         
30130         return pos;
30131         
30132     },
30133     
30134     getVerticalThreeBoxColPositions : function(x, y, box)
30135     {
30136         var pos = [];
30137         
30138         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30139             
30140             pos.push({
30141                 x : x,
30142                 y : y
30143             });
30144
30145             pos.push({
30146                 x : x + (this.unitWidth + this.gutter) * 1,
30147                 y : y
30148             });
30149             
30150             pos.push({
30151                 x : x + (this.unitWidth + this.gutter) * 2,
30152                 y : y
30153             });
30154             
30155             return pos;
30156             
30157         }
30158         
30159         if(box[0].size == 'xs' && box[1].size == 'xs'){
30160             
30161             pos.push({
30162                 x : x,
30163                 y : y
30164             });
30165
30166             pos.push({
30167                 x : x,
30168                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
30169             });
30170             
30171             pos.push({
30172                 x : x + (this.unitWidth + this.gutter) * 1,
30173                 y : y
30174             });
30175             
30176             return pos;
30177             
30178         }
30179         
30180         pos.push({
30181             x : x,
30182             y : y
30183         });
30184
30185         pos.push({
30186             x : x + (this.unitWidth + this.gutter) * 2,
30187             y : y
30188         });
30189
30190         pos.push({
30191             x : x + (this.unitWidth + this.gutter) * 2,
30192             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
30193         });
30194             
30195         return pos;
30196         
30197     },
30198     
30199     getVerticalFourBoxColPositions : function(x, y, box)
30200     {
30201         var pos = [];
30202         
30203         if(box[0].size == 'xs'){
30204             
30205             pos.push({
30206                 x : x,
30207                 y : y
30208             });
30209
30210             pos.push({
30211                 x : x,
30212                 y : y + (this.unitHeight + this.gutter) * 1
30213             });
30214             
30215             pos.push({
30216                 x : x,
30217                 y : y + (this.unitHeight + this.gutter) * 2
30218             });
30219             
30220             pos.push({
30221                 x : x + (this.unitWidth + this.gutter) * 1,
30222                 y : y
30223             });
30224             
30225             return pos;
30226             
30227         }
30228         
30229         pos.push({
30230             x : x,
30231             y : y
30232         });
30233
30234         pos.push({
30235             x : x + (this.unitWidth + this.gutter) * 2,
30236             y : y
30237         });
30238
30239         pos.push({
30240             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
30241             y : y + (this.unitHeight + this.gutter) * 1
30242         });
30243
30244         pos.push({
30245             x : x + (this.unitWidth + this.gutter) * 2,
30246             y : y + (this.unitWidth + this.gutter) * 2
30247         });
30248
30249         return pos;
30250         
30251     },
30252     
30253     getHorizontalOneBoxColPositions : function(maxX, minY, box)
30254     {
30255         var pos = [];
30256         
30257         if(box[0].size == 'md-left'){
30258             pos.push({
30259                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30260                 y : minY
30261             });
30262             
30263             return pos;
30264         }
30265         
30266         if(box[0].size == 'md-right'){
30267             pos.push({
30268                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30269                 y : minY + (this.unitWidth + this.gutter) * 1
30270             });
30271             
30272             return pos;
30273         }
30274         
30275         var rand = Math.floor(Math.random() * (4 - box[0].y));
30276         
30277         pos.push({
30278             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30279             y : minY + (this.unitWidth + this.gutter) * rand
30280         });
30281         
30282         return pos;
30283         
30284     },
30285     
30286     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
30287     {
30288         var pos = [];
30289         
30290         if(box[0].size == 'xs'){
30291             
30292             pos.push({
30293                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30294                 y : minY
30295             });
30296
30297             pos.push({
30298                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30299                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
30300             });
30301             
30302             return pos;
30303             
30304         }
30305         
30306         pos.push({
30307             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30308             y : minY
30309         });
30310
30311         pos.push({
30312             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30313             y : minY + (this.unitWidth + this.gutter) * 2
30314         });
30315         
30316         return pos;
30317         
30318     },
30319     
30320     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
30321     {
30322         var pos = [];
30323         
30324         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30325             
30326             pos.push({
30327                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30328                 y : minY
30329             });
30330
30331             pos.push({
30332                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30333                 y : minY + (this.unitWidth + this.gutter) * 1
30334             });
30335             
30336             pos.push({
30337                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30338                 y : minY + (this.unitWidth + this.gutter) * 2
30339             });
30340             
30341             return pos;
30342             
30343         }
30344         
30345         if(box[0].size == 'xs' && box[1].size == 'xs'){
30346             
30347             pos.push({
30348                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30349                 y : minY
30350             });
30351
30352             pos.push({
30353                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30354                 y : minY
30355             });
30356             
30357             pos.push({
30358                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30359                 y : minY + (this.unitWidth + this.gutter) * 1
30360             });
30361             
30362             return pos;
30363             
30364         }
30365         
30366         pos.push({
30367             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30368             y : minY
30369         });
30370
30371         pos.push({
30372             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30373             y : minY + (this.unitWidth + this.gutter) * 2
30374         });
30375
30376         pos.push({
30377             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30378             y : minY + (this.unitWidth + this.gutter) * 2
30379         });
30380             
30381         return pos;
30382         
30383     },
30384     
30385     getHorizontalFourBoxColPositions : function(maxX, minY, box)
30386     {
30387         var pos = [];
30388         
30389         if(box[0].size == 'xs'){
30390             
30391             pos.push({
30392                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30393                 y : minY
30394             });
30395
30396             pos.push({
30397                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30398                 y : minY
30399             });
30400             
30401             pos.push({
30402                 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),
30403                 y : minY
30404             });
30405             
30406             pos.push({
30407                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
30408                 y : minY + (this.unitWidth + this.gutter) * 1
30409             });
30410             
30411             return pos;
30412             
30413         }
30414         
30415         pos.push({
30416             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30417             y : minY
30418         });
30419         
30420         pos.push({
30421             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30422             y : minY + (this.unitWidth + this.gutter) * 2
30423         });
30424         
30425         pos.push({
30426             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30427             y : minY + (this.unitWidth + this.gutter) * 2
30428         });
30429         
30430         pos.push({
30431             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),
30432             y : minY + (this.unitWidth + this.gutter) * 2
30433         });
30434
30435         return pos;
30436         
30437     }
30438     
30439 });
30440
30441  
30442
30443  /**
30444  *
30445  * This is based on 
30446  * http://masonry.desandro.com
30447  *
30448  * The idea is to render all the bricks based on vertical width...
30449  *
30450  * The original code extends 'outlayer' - we might need to use that....
30451  * 
30452  */
30453
30454
30455 /**
30456  * @class Roo.bootstrap.LayoutMasonryAuto
30457  * @extends Roo.bootstrap.Component
30458  * Bootstrap Layout Masonry class
30459  * 
30460  * @constructor
30461  * Create a new Element
30462  * @param {Object} config The config object
30463  */
30464
30465 Roo.bootstrap.LayoutMasonryAuto = function(config){
30466     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
30467 };
30468
30469 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
30470     
30471       /**
30472      * @cfg {Boolean} isFitWidth  - resize the width..
30473      */   
30474     isFitWidth : false,  // options..
30475     /**
30476      * @cfg {Boolean} isOriginLeft = left align?
30477      */   
30478     isOriginLeft : true,
30479     /**
30480      * @cfg {Boolean} isOriginTop = top align?
30481      */   
30482     isOriginTop : false,
30483     /**
30484      * @cfg {Boolean} isLayoutInstant = no animation?
30485      */   
30486     isLayoutInstant : false, // needed?
30487     /**
30488      * @cfg {Boolean} isResizingContainer = not sure if this is used..
30489      */   
30490     isResizingContainer : true,
30491     /**
30492      * @cfg {Number} columnWidth  width of the columns 
30493      */   
30494     
30495     columnWidth : 0,
30496     
30497     /**
30498      * @cfg {Number} maxCols maximum number of columns
30499      */   
30500     
30501     maxCols: 0,
30502     /**
30503      * @cfg {Number} padHeight padding below box..
30504      */   
30505     
30506     padHeight : 10, 
30507     
30508     /**
30509      * @cfg {Boolean} isAutoInitial defalut true
30510      */   
30511     
30512     isAutoInitial : true, 
30513     
30514     // private?
30515     gutter : 0,
30516     
30517     containerWidth: 0,
30518     initialColumnWidth : 0,
30519     currentSize : null,
30520     
30521     colYs : null, // array.
30522     maxY : 0,
30523     padWidth: 10,
30524     
30525     
30526     tag: 'div',
30527     cls: '',
30528     bricks: null, //CompositeElement
30529     cols : 0, // array?
30530     // element : null, // wrapped now this.el
30531     _isLayoutInited : null, 
30532     
30533     
30534     getAutoCreate : function(){
30535         
30536         var cfg = {
30537             tag: this.tag,
30538             cls: 'blog-masonary-wrapper ' + this.cls,
30539             cn : {
30540                 cls : 'mas-boxes masonary'
30541             }
30542         };
30543         
30544         return cfg;
30545     },
30546     
30547     getChildContainer: function( )
30548     {
30549         if (this.boxesEl) {
30550             return this.boxesEl;
30551         }
30552         
30553         this.boxesEl = this.el.select('.mas-boxes').first();
30554         
30555         return this.boxesEl;
30556     },
30557     
30558     
30559     initEvents : function()
30560     {
30561         var _this = this;
30562         
30563         if(this.isAutoInitial){
30564             Roo.log('hook children rendered');
30565             this.on('childrenrendered', function() {
30566                 Roo.log('children rendered');
30567                 _this.initial();
30568             } ,this);
30569         }
30570         
30571     },
30572     
30573     initial : function()
30574     {
30575         this.reloadItems();
30576
30577         this.currentSize = this.el.getBox(true);
30578
30579         /// was window resize... - let's see if this works..
30580         Roo.EventManager.onWindowResize(this.resize, this); 
30581
30582         if(!this.isAutoInitial){
30583             this.layout();
30584             return;
30585         }
30586         
30587         this.layout.defer(500,this);
30588     },
30589     
30590     reloadItems: function()
30591     {
30592         this.bricks = this.el.select('.masonry-brick', true);
30593         
30594         this.bricks.each(function(b) {
30595             //Roo.log(b.getSize());
30596             if (!b.attr('originalwidth')) {
30597                 b.attr('originalwidth',  b.getSize().width);
30598             }
30599             
30600         });
30601         
30602         Roo.log(this.bricks.elements.length);
30603     },
30604     
30605     resize : function()
30606     {
30607         Roo.log('resize');
30608         var cs = this.el.getBox(true);
30609         
30610         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
30611             Roo.log("no change in with or X");
30612             return;
30613         }
30614         this.currentSize = cs;
30615         this.layout();
30616     },
30617     
30618     layout : function()
30619     {
30620          Roo.log('layout');
30621         this._resetLayout();
30622         //this._manageStamps();
30623       
30624         // don't animate first layout
30625         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30626         this.layoutItems( isInstant );
30627       
30628         // flag for initalized
30629         this._isLayoutInited = true;
30630     },
30631     
30632     layoutItems : function( isInstant )
30633     {
30634         //var items = this._getItemsForLayout( this.items );
30635         // original code supports filtering layout items.. we just ignore it..
30636         
30637         this._layoutItems( this.bricks , isInstant );
30638       
30639         this._postLayout();
30640     },
30641     _layoutItems : function ( items , isInstant)
30642     {
30643        //this.fireEvent( 'layout', this, items );
30644     
30645
30646         if ( !items || !items.elements.length ) {
30647           // no items, emit event with empty array
30648             return;
30649         }
30650
30651         var queue = [];
30652         items.each(function(item) {
30653             Roo.log("layout item");
30654             Roo.log(item);
30655             // get x/y object from method
30656             var position = this._getItemLayoutPosition( item );
30657             // enqueue
30658             position.item = item;
30659             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
30660             queue.push( position );
30661         }, this);
30662       
30663         this._processLayoutQueue( queue );
30664     },
30665     /** Sets position of item in DOM
30666     * @param {Element} item
30667     * @param {Number} x - horizontal position
30668     * @param {Number} y - vertical position
30669     * @param {Boolean} isInstant - disables transitions
30670     */
30671     _processLayoutQueue : function( queue )
30672     {
30673         for ( var i=0, len = queue.length; i < len; i++ ) {
30674             var obj = queue[i];
30675             obj.item.position('absolute');
30676             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
30677         }
30678     },
30679       
30680     
30681     /**
30682     * Any logic you want to do after each layout,
30683     * i.e. size the container
30684     */
30685     _postLayout : function()
30686     {
30687         this.resizeContainer();
30688     },
30689     
30690     resizeContainer : function()
30691     {
30692         if ( !this.isResizingContainer ) {
30693             return;
30694         }
30695         var size = this._getContainerSize();
30696         if ( size ) {
30697             this.el.setSize(size.width,size.height);
30698             this.boxesEl.setSize(size.width,size.height);
30699         }
30700     },
30701     
30702     
30703     
30704     _resetLayout : function()
30705     {
30706         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
30707         this.colWidth = this.el.getWidth();
30708         //this.gutter = this.el.getWidth(); 
30709         
30710         this.measureColumns();
30711
30712         // reset column Y
30713         var i = this.cols;
30714         this.colYs = [];
30715         while (i--) {
30716             this.colYs.push( 0 );
30717         }
30718     
30719         this.maxY = 0;
30720     },
30721
30722     measureColumns : function()
30723     {
30724         this.getContainerWidth();
30725       // if columnWidth is 0, default to outerWidth of first item
30726         if ( !this.columnWidth ) {
30727             var firstItem = this.bricks.first();
30728             Roo.log(firstItem);
30729             this.columnWidth  = this.containerWidth;
30730             if (firstItem && firstItem.attr('originalwidth') ) {
30731                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
30732             }
30733             // columnWidth fall back to item of first element
30734             Roo.log("set column width?");
30735                         this.initialColumnWidth = this.columnWidth  ;
30736
30737             // if first elem has no width, default to size of container
30738             
30739         }
30740         
30741         
30742         if (this.initialColumnWidth) {
30743             this.columnWidth = this.initialColumnWidth;
30744         }
30745         
30746         
30747             
30748         // column width is fixed at the top - however if container width get's smaller we should
30749         // reduce it...
30750         
30751         // this bit calcs how man columns..
30752             
30753         var columnWidth = this.columnWidth += this.gutter;
30754       
30755         // calculate columns
30756         var containerWidth = this.containerWidth + this.gutter;
30757         
30758         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
30759         // fix rounding errors, typically with gutters
30760         var excess = columnWidth - containerWidth % columnWidth;
30761         
30762         
30763         // if overshoot is less than a pixel, round up, otherwise floor it
30764         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
30765         cols = Math[ mathMethod ]( cols );
30766         this.cols = Math.max( cols, 1 );
30767         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30768         
30769          // padding positioning..
30770         var totalColWidth = this.cols * this.columnWidth;
30771         var padavail = this.containerWidth - totalColWidth;
30772         // so for 2 columns - we need 3 'pads'
30773         
30774         var padNeeded = (1+this.cols) * this.padWidth;
30775         
30776         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
30777         
30778         this.columnWidth += padExtra
30779         //this.padWidth = Math.floor(padavail /  ( this.cols));
30780         
30781         // adjust colum width so that padding is fixed??
30782         
30783         // we have 3 columns ... total = width * 3
30784         // we have X left over... that should be used by 
30785         
30786         //if (this.expandC) {
30787             
30788         //}
30789         
30790         
30791         
30792     },
30793     
30794     getContainerWidth : function()
30795     {
30796        /* // container is parent if fit width
30797         var container = this.isFitWidth ? this.element.parentNode : this.element;
30798         // check that this.size and size are there
30799         // IE8 triggers resize on body size change, so they might not be
30800         
30801         var size = getSize( container );  //FIXME
30802         this.containerWidth = size && size.innerWidth; //FIXME
30803         */
30804          
30805         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30806         
30807     },
30808     
30809     _getItemLayoutPosition : function( item )  // what is item?
30810     {
30811         // we resize the item to our columnWidth..
30812       
30813         item.setWidth(this.columnWidth);
30814         item.autoBoxAdjust  = false;
30815         
30816         var sz = item.getSize();
30817  
30818         // how many columns does this brick span
30819         var remainder = this.containerWidth % this.columnWidth;
30820         
30821         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30822         // round if off by 1 pixel, otherwise use ceil
30823         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
30824         colSpan = Math.min( colSpan, this.cols );
30825         
30826         // normally this should be '1' as we dont' currently allow multi width columns..
30827         
30828         var colGroup = this._getColGroup( colSpan );
30829         // get the minimum Y value from the columns
30830         var minimumY = Math.min.apply( Math, colGroup );
30831         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30832         
30833         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
30834          
30835         // position the brick
30836         var position = {
30837             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30838             y: this.currentSize.y + minimumY + this.padHeight
30839         };
30840         
30841         Roo.log(position);
30842         // apply setHeight to necessary columns
30843         var setHeight = minimumY + sz.height + this.padHeight;
30844         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30845         
30846         var setSpan = this.cols + 1 - colGroup.length;
30847         for ( var i = 0; i < setSpan; i++ ) {
30848           this.colYs[ shortColIndex + i ] = setHeight ;
30849         }
30850       
30851         return position;
30852     },
30853     
30854     /**
30855      * @param {Number} colSpan - number of columns the element spans
30856      * @returns {Array} colGroup
30857      */
30858     _getColGroup : function( colSpan )
30859     {
30860         if ( colSpan < 2 ) {
30861           // if brick spans only one column, use all the column Ys
30862           return this.colYs;
30863         }
30864       
30865         var colGroup = [];
30866         // how many different places could this brick fit horizontally
30867         var groupCount = this.cols + 1 - colSpan;
30868         // for each group potential horizontal position
30869         for ( var i = 0; i < groupCount; i++ ) {
30870           // make an array of colY values for that one group
30871           var groupColYs = this.colYs.slice( i, i + colSpan );
30872           // and get the max value of the array
30873           colGroup[i] = Math.max.apply( Math, groupColYs );
30874         }
30875         return colGroup;
30876     },
30877     /*
30878     _manageStamp : function( stamp )
30879     {
30880         var stampSize =  stamp.getSize();
30881         var offset = stamp.getBox();
30882         // get the columns that this stamp affects
30883         var firstX = this.isOriginLeft ? offset.x : offset.right;
30884         var lastX = firstX + stampSize.width;
30885         var firstCol = Math.floor( firstX / this.columnWidth );
30886         firstCol = Math.max( 0, firstCol );
30887         
30888         var lastCol = Math.floor( lastX / this.columnWidth );
30889         // lastCol should not go over if multiple of columnWidth #425
30890         lastCol -= lastX % this.columnWidth ? 0 : 1;
30891         lastCol = Math.min( this.cols - 1, lastCol );
30892         
30893         // set colYs to bottom of the stamp
30894         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30895             stampSize.height;
30896             
30897         for ( var i = firstCol; i <= lastCol; i++ ) {
30898           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30899         }
30900     },
30901     */
30902     
30903     _getContainerSize : function()
30904     {
30905         this.maxY = Math.max.apply( Math, this.colYs );
30906         var size = {
30907             height: this.maxY
30908         };
30909       
30910         if ( this.isFitWidth ) {
30911             size.width = this._getContainerFitWidth();
30912         }
30913       
30914         return size;
30915     },
30916     
30917     _getContainerFitWidth : function()
30918     {
30919         var unusedCols = 0;
30920         // count unused columns
30921         var i = this.cols;
30922         while ( --i ) {
30923           if ( this.colYs[i] !== 0 ) {
30924             break;
30925           }
30926           unusedCols++;
30927         }
30928         // fit container to columns that have been used
30929         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30930     },
30931     
30932     needsResizeLayout : function()
30933     {
30934         var previousWidth = this.containerWidth;
30935         this.getContainerWidth();
30936         return previousWidth !== this.containerWidth;
30937     }
30938  
30939 });
30940
30941  
30942
30943  /*
30944  * - LGPL
30945  *
30946  * element
30947  * 
30948  */
30949
30950 /**
30951  * @class Roo.bootstrap.MasonryBrick
30952  * @extends Roo.bootstrap.Component
30953  * Bootstrap MasonryBrick class
30954  * 
30955  * @constructor
30956  * Create a new MasonryBrick
30957  * @param {Object} config The config object
30958  */
30959
30960 Roo.bootstrap.MasonryBrick = function(config){
30961     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
30962     
30963     this.addEvents({
30964         // raw events
30965         /**
30966          * @event click
30967          * When a MasonryBrick is clcik
30968          * @param {Roo.bootstrap.MasonryBrick} this
30969          * @param {Roo.EventObject} e
30970          */
30971         "click" : true
30972     });
30973 };
30974
30975 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
30976     
30977     /**
30978      * @cfg {String} title
30979      */   
30980     title : '',
30981     /**
30982      * @cfg {String} html
30983      */   
30984     html : '',
30985     /**
30986      * @cfg {String} bgimage
30987      */   
30988     bgimage : '',
30989     /**
30990      * @cfg {String} videourl
30991      */   
30992     videourl : '',
30993     /**
30994      * @cfg {String} cls
30995      */   
30996     cls : '',
30997     /**
30998      * @cfg {String} href
30999      */   
31000     href : '',
31001     /**
31002      * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
31003      */   
31004     size : 'xs',
31005     
31006     /**
31007      * @cfg {String} (center|bottom) placetitle
31008      */   
31009     placetitle : '',
31010     
31011     /**
31012      * @cfg {Boolean} isFitContainer defalut true
31013      */   
31014     isFitContainer : true, 
31015     
31016     /**
31017      * @cfg {Boolean} preventDefault defalut false
31018      */   
31019     preventDefault : false, 
31020     
31021     getAutoCreate : function()
31022     {
31023         if(!this.isFitContainer){
31024             return this.getSplitAutoCreate();
31025         }
31026         
31027         var cls = 'masonry-brick masonry-brick-full';
31028         
31029         if(this.href.length){
31030             cls += ' masonry-brick-link';
31031         }
31032         
31033         if(this.bgimage.length){
31034             cls += ' masonry-brick-image';
31035         }
31036         
31037         if(!this.html.length){
31038             cls += ' enable-mask';
31039         }
31040         
31041         if(this.size){
31042             cls += ' masonry-' + this.size + '-brick';
31043         }
31044         
31045         if(this.placetitle.length){
31046             
31047             switch (this.placetitle) {
31048                 case 'center' :
31049                     cls += ' masonry-center-title';
31050                     break;
31051                 case 'bottom' :
31052                     cls += ' masonry-bottom-title';
31053                     break;
31054                 default:
31055                     break;
31056             }
31057             
31058         } else {
31059             if(!this.html.length && !this.bgimage.length){
31060                 cls += ' masonry-center-title';
31061             }
31062
31063             if(!this.html.length && this.bgimage.length){
31064                 cls += ' masonry-bottom-title';
31065             }
31066         }
31067         
31068         if(this.cls){
31069             cls += ' ' + this.cls;
31070         }
31071         
31072         var cfg = {
31073             tag: (this.href.length) ? 'a' : 'div',
31074             cls: cls,
31075             cn: [
31076                 {
31077                     tag: 'div',
31078                     cls: 'masonry-brick-paragraph',
31079                     cn: []
31080                 }
31081             ]
31082         };
31083         
31084         if(this.href.length){
31085             cfg.href = this.href;
31086         }
31087         
31088         var cn = cfg.cn[0].cn;
31089         
31090         if(this.title.length){
31091             cn.push({
31092                 tag: 'h4',
31093                 cls: 'masonry-brick-title',
31094                 html: this.title
31095             });
31096         }
31097         
31098         if(this.html.length){
31099             cn.push({
31100                 tag: 'p',
31101                 cls: 'masonry-brick-text',
31102                 html: this.html
31103             });
31104         }  
31105         if (!this.title.length && !this.html.length) {
31106             cfg.cn[0].cls += ' hide';
31107         }
31108         
31109         if(this.bgimage.length){
31110             cfg.cn.push({
31111                 tag: 'img',
31112                 cls: 'masonry-brick-image-view',
31113                 src: this.bgimage
31114             });
31115         }
31116         
31117         if(this.videourl.length){
31118             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31119             // youtube support only?
31120             cfg.cn.push({
31121                 tag: 'iframe',
31122                 cls: 'masonry-brick-image-view',
31123                 src: vurl,
31124                 frameborder : 0,
31125                 allowfullscreen : true
31126             });
31127             
31128             
31129         }
31130         
31131         cfg.cn.push({
31132             tag: 'div',
31133             cls: 'masonry-brick-mask'
31134         });
31135         
31136         return cfg;
31137         
31138     },
31139     
31140     getSplitAutoCreate : function()
31141     {
31142         var cls = 'masonry-brick masonry-brick-split';
31143         
31144         if(this.href.length){
31145             cls += ' masonry-brick-link';
31146         }
31147         
31148         if(this.bgimage.length){
31149             cls += ' masonry-brick-image';
31150         }
31151         
31152         if(this.size){
31153             cls += ' masonry-' + this.size + '-brick';
31154         }
31155         
31156         switch (this.placetitle) {
31157             case 'center' :
31158                 cls += ' masonry-center-title';
31159                 break;
31160             case 'bottom' :
31161                 cls += ' masonry-bottom-title';
31162                 break;
31163             default:
31164                 if(!this.bgimage.length){
31165                     cls += ' masonry-center-title';
31166                 }
31167
31168                 if(this.bgimage.length){
31169                     cls += ' masonry-bottom-title';
31170                 }
31171                 break;
31172         }
31173         
31174         if(this.cls){
31175             cls += ' ' + this.cls;
31176         }
31177         
31178         var cfg = {
31179             tag: (this.href.length) ? 'a' : 'div',
31180             cls: cls,
31181             cn: [
31182                 {
31183                     tag: 'div',
31184                     cls: 'masonry-brick-split-head',
31185                     cn: [
31186                         {
31187                             tag: 'div',
31188                             cls: 'masonry-brick-paragraph',
31189                             cn: []
31190                         }
31191                     ]
31192                 },
31193                 {
31194                     tag: 'div',
31195                     cls: 'masonry-brick-split-body',
31196                     cn: []
31197                 }
31198             ]
31199         };
31200         
31201         if(this.href.length){
31202             cfg.href = this.href;
31203         }
31204         
31205         if(this.title.length){
31206             cfg.cn[0].cn[0].cn.push({
31207                 tag: 'h4',
31208                 cls: 'masonry-brick-title',
31209                 html: this.title
31210             });
31211         }
31212         
31213         if(this.html.length){
31214             cfg.cn[1].cn.push({
31215                 tag: 'p',
31216                 cls: 'masonry-brick-text',
31217                 html: this.html
31218             });
31219         }
31220
31221         if(this.bgimage.length){
31222             cfg.cn[0].cn.push({
31223                 tag: 'img',
31224                 cls: 'masonry-brick-image-view',
31225                 src: this.bgimage
31226             });
31227         }
31228         
31229         if(this.videourl.length){
31230             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31231             // youtube support only?
31232             cfg.cn[0].cn.cn.push({
31233                 tag: 'iframe',
31234                 cls: 'masonry-brick-image-view',
31235                 src: vurl,
31236                 frameborder : 0,
31237                 allowfullscreen : true
31238             });
31239         }
31240         
31241         return cfg;
31242     },
31243     
31244     initEvents: function() 
31245     {
31246         switch (this.size) {
31247             case 'xs' :
31248                 this.x = 1;
31249                 this.y = 1;
31250                 break;
31251             case 'sm' :
31252                 this.x = 2;
31253                 this.y = 2;
31254                 break;
31255             case 'md' :
31256             case 'md-left' :
31257             case 'md-right' :
31258                 this.x = 3;
31259                 this.y = 3;
31260                 break;
31261             case 'tall' :
31262                 this.x = 2;
31263                 this.y = 3;
31264                 break;
31265             case 'wide' :
31266                 this.x = 3;
31267                 this.y = 2;
31268                 break;
31269             case 'wide-thin' :
31270                 this.x = 3;
31271                 this.y = 1;
31272                 break;
31273                         
31274             default :
31275                 break;
31276         }
31277         
31278         if(Roo.isTouch){
31279             this.el.on('touchstart', this.onTouchStart, this);
31280             this.el.on('touchmove', this.onTouchMove, this);
31281             this.el.on('touchend', this.onTouchEnd, this);
31282             this.el.on('contextmenu', this.onContextMenu, this);
31283         } else {
31284             this.el.on('mouseenter'  ,this.enter, this);
31285             this.el.on('mouseleave', this.leave, this);
31286             this.el.on('click', this.onClick, this);
31287         }
31288         
31289         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
31290             this.parent().bricks.push(this);   
31291         }
31292         
31293     },
31294     
31295     onClick: function(e, el)
31296     {
31297         var time = this.endTimer - this.startTimer;
31298         
31299         if(Roo.isTouch){
31300             if(time > 1000){
31301                 e.preventDefault();
31302                 return;
31303             }
31304         }
31305         
31306         if(!this.preventDefault){
31307             return;
31308         }
31309         
31310         e.preventDefault();
31311         this.fireEvent('click', this);
31312     },
31313     
31314     enter: function(e, el)
31315     {
31316         e.preventDefault();
31317         
31318         if(!this.isFitContainer){
31319             return;
31320         }
31321         
31322         if(this.bgimage.length && this.html.length){
31323             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31324         }
31325     },
31326     
31327     leave: function(e, el)
31328     {
31329         e.preventDefault();
31330         
31331         if(!this.isFitContainer){
31332             return;
31333         }
31334         
31335         if(this.bgimage.length && this.html.length){
31336             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31337         }
31338     },
31339     
31340     onTouchStart: function(e, el)
31341     {
31342 //        e.preventDefault();
31343         
31344         this.touchmoved = false;
31345         
31346         if(!this.isFitContainer){
31347             return;
31348         }
31349         
31350         if(!this.bgimage.length || !this.html.length){
31351             return;
31352         }
31353         
31354         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31355         
31356         this.timer = new Date().getTime();
31357         
31358     },
31359     
31360     onTouchMove: function(e, el)
31361     {
31362         this.touchmoved = true;
31363     },
31364     
31365     onContextMenu : function(e,el)
31366     {
31367         e.preventDefault();
31368         e.stopPropagation();
31369         return false;
31370     },
31371     
31372     onTouchEnd: function(e, el)
31373     {
31374 //        e.preventDefault();
31375         
31376         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
31377         
31378             this.leave(e,el);
31379             
31380             return;
31381         }
31382         
31383         if(!this.bgimage.length || !this.html.length){
31384             
31385             if(this.href.length){
31386                 window.location.href = this.href;
31387             }
31388             
31389             return;
31390         }
31391         
31392         if(!this.isFitContainer){
31393             return;
31394         }
31395         
31396         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31397         
31398         window.location.href = this.href;
31399     }
31400     
31401 });
31402
31403  
31404
31405  /*
31406  * - LGPL
31407  *
31408  * element
31409  * 
31410  */
31411
31412 /**
31413  * @class Roo.bootstrap.Brick
31414  * @extends Roo.bootstrap.Component
31415  * Bootstrap Brick class
31416  * 
31417  * @constructor
31418  * Create a new Brick
31419  * @param {Object} config The config object
31420  */
31421
31422 Roo.bootstrap.Brick = function(config){
31423     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
31424     
31425     this.addEvents({
31426         // raw events
31427         /**
31428          * @event click
31429          * When a Brick is click
31430          * @param {Roo.bootstrap.Brick} this
31431          * @param {Roo.EventObject} e
31432          */
31433         "click" : true
31434     });
31435 };
31436
31437 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
31438     
31439     /**
31440      * @cfg {String} title
31441      */   
31442     title : '',
31443     /**
31444      * @cfg {String} html
31445      */   
31446     html : '',
31447     /**
31448      * @cfg {String} bgimage
31449      */   
31450     bgimage : '',
31451     /**
31452      * @cfg {String} cls
31453      */   
31454     cls : '',
31455     /**
31456      * @cfg {String} href
31457      */   
31458     href : '',
31459     /**
31460      * @cfg {String} video
31461      */   
31462     video : '',
31463     /**
31464      * @cfg {Boolean} square
31465      */   
31466     square : true,
31467     
31468     getAutoCreate : function()
31469     {
31470         var cls = 'roo-brick';
31471         
31472         if(this.href.length){
31473             cls += ' roo-brick-link';
31474         }
31475         
31476         if(this.bgimage.length){
31477             cls += ' roo-brick-image';
31478         }
31479         
31480         if(!this.html.length && !this.bgimage.length){
31481             cls += ' roo-brick-center-title';
31482         }
31483         
31484         if(!this.html.length && this.bgimage.length){
31485             cls += ' roo-brick-bottom-title';
31486         }
31487         
31488         if(this.cls){
31489             cls += ' ' + this.cls;
31490         }
31491         
31492         var cfg = {
31493             tag: (this.href.length) ? 'a' : 'div',
31494             cls: cls,
31495             cn: [
31496                 {
31497                     tag: 'div',
31498                     cls: 'roo-brick-paragraph',
31499                     cn: []
31500                 }
31501             ]
31502         };
31503         
31504         if(this.href.length){
31505             cfg.href = this.href;
31506         }
31507         
31508         var cn = cfg.cn[0].cn;
31509         
31510         if(this.title.length){
31511             cn.push({
31512                 tag: 'h4',
31513                 cls: 'roo-brick-title',
31514                 html: this.title
31515             });
31516         }
31517         
31518         if(this.html.length){
31519             cn.push({
31520                 tag: 'p',
31521                 cls: 'roo-brick-text',
31522                 html: this.html
31523             });
31524         } else {
31525             cn.cls += ' hide';
31526         }
31527         
31528         if(this.bgimage.length){
31529             cfg.cn.push({
31530                 tag: 'img',
31531                 cls: 'roo-brick-image-view',
31532                 src: this.bgimage
31533             });
31534         }
31535         
31536         return cfg;
31537     },
31538     
31539     initEvents: function() 
31540     {
31541         if(this.title.length || this.html.length){
31542             this.el.on('mouseenter'  ,this.enter, this);
31543             this.el.on('mouseleave', this.leave, this);
31544         }
31545         
31546         
31547         Roo.EventManager.onWindowResize(this.resize, this); 
31548         
31549         this.resize();
31550     },
31551     
31552     resize : function()
31553     {
31554         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
31555         
31556         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
31557         
31558         if(this.bgimage.length){
31559             var image = this.el.select('.roo-brick-image-view', true).first();
31560             image.setWidth(paragraph.getWidth());
31561             image.setHeight(paragraph.getWidth());
31562             
31563             this.el.setHeight(paragraph.getWidth());
31564             
31565         }
31566         
31567     },
31568     
31569     enter: function(e, el)
31570     {
31571         e.preventDefault();
31572         
31573         if(this.bgimage.length){
31574             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
31575             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
31576         }
31577     },
31578     
31579     leave: function(e, el)
31580     {
31581         e.preventDefault();
31582         
31583         if(this.bgimage.length){
31584             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
31585             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
31586         }
31587     }
31588     
31589 });
31590
31591  
31592
31593  /*
31594  * - LGPL
31595  *
31596  * Input
31597  * 
31598  */
31599
31600 /**
31601  * @class Roo.bootstrap.NumberField
31602  * @extends Roo.bootstrap.Input
31603  * Bootstrap NumberField class
31604  * 
31605  * 
31606  * 
31607  * 
31608  * @constructor
31609  * Create a new NumberField
31610  * @param {Object} config The config object
31611  */
31612
31613 Roo.bootstrap.NumberField = function(config){
31614     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
31615 };
31616
31617 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
31618     
31619     /**
31620      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
31621      */
31622     allowDecimals : true,
31623     /**
31624      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
31625      */
31626     decimalSeparator : ".",
31627     /**
31628      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
31629      */
31630     decimalPrecision : 2,
31631     /**
31632      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
31633      */
31634     allowNegative : true,
31635     /**
31636      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
31637      */
31638     minValue : Number.NEGATIVE_INFINITY,
31639     /**
31640      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
31641      */
31642     maxValue : Number.MAX_VALUE,
31643     /**
31644      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
31645      */
31646     minText : "The minimum value for this field is {0}",
31647     /**
31648      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
31649      */
31650     maxText : "The maximum value for this field is {0}",
31651     /**
31652      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
31653      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
31654      */
31655     nanText : "{0} is not a valid number",
31656     /**
31657      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
31658      */
31659     castInt : true,
31660
31661     // private
31662     initEvents : function()
31663     {   
31664         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
31665         
31666         var allowed = "0123456789";
31667         
31668         if(this.allowDecimals){
31669             allowed += this.decimalSeparator;
31670         }
31671         
31672         if(this.allowNegative){
31673             allowed += "-";
31674         }
31675         
31676         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
31677         
31678         var keyPress = function(e){
31679             
31680             var k = e.getKey();
31681             
31682             var c = e.getCharCode();
31683             
31684             if(
31685                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
31686                     allowed.indexOf(String.fromCharCode(c)) === -1
31687             ){
31688                 e.stopEvent();
31689                 return;
31690             }
31691             
31692             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
31693                 return;
31694             }
31695             
31696             if(allowed.indexOf(String.fromCharCode(c)) === -1){
31697                 e.stopEvent();
31698             }
31699         };
31700         
31701         this.el.on("keypress", keyPress, this);
31702     },
31703     
31704     validateValue : function(value)
31705     {
31706         
31707         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
31708             return false;
31709         }
31710         
31711         var num = this.parseValue(value);
31712         
31713         if(isNaN(num)){
31714             this.markInvalid(String.format(this.nanText, value));
31715             return false;
31716         }
31717         
31718         if(num < this.minValue){
31719             this.markInvalid(String.format(this.minText, this.minValue));
31720             return false;
31721         }
31722         
31723         if(num > this.maxValue){
31724             this.markInvalid(String.format(this.maxText, this.maxValue));
31725             return false;
31726         }
31727         
31728         return true;
31729     },
31730
31731     getValue : function()
31732     {
31733         return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
31734     },
31735
31736     parseValue : function(value)
31737     {
31738         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
31739         return isNaN(value) ? '' : value;
31740     },
31741
31742     fixPrecision : function(value)
31743     {
31744         var nan = isNaN(value);
31745         
31746         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
31747             return nan ? '' : value;
31748         }
31749         return parseFloat(value).toFixed(this.decimalPrecision);
31750     },
31751
31752     setValue : function(v)
31753     {
31754         v = this.fixPrecision(v);
31755         Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
31756     },
31757
31758     decimalPrecisionFcn : function(v)
31759     {
31760         return Math.floor(v);
31761     },
31762
31763     beforeBlur : function()
31764     {
31765         if(!this.castInt){
31766             return;
31767         }
31768         
31769         var v = this.parseValue(this.getRawValue());
31770         if(v){
31771             this.setValue(v);
31772         }
31773     }
31774     
31775 });
31776
31777  
31778
31779 /*
31780 * Licence: LGPL
31781 */
31782
31783 /**
31784  * @class Roo.bootstrap.DocumentSlider
31785  * @extends Roo.bootstrap.Component
31786  * Bootstrap DocumentSlider class
31787  * 
31788  * @constructor
31789  * Create a new DocumentViewer
31790  * @param {Object} config The config object
31791  */
31792
31793 Roo.bootstrap.DocumentSlider = function(config){
31794     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
31795     
31796     this.files = [];
31797     
31798     this.addEvents({
31799         /**
31800          * @event initial
31801          * Fire after initEvent
31802          * @param {Roo.bootstrap.DocumentSlider} this
31803          */
31804         "initial" : true,
31805         /**
31806          * @event update
31807          * Fire after update
31808          * @param {Roo.bootstrap.DocumentSlider} this
31809          */
31810         "update" : true,
31811         /**
31812          * @event click
31813          * Fire after click
31814          * @param {Roo.bootstrap.DocumentSlider} this
31815          */
31816         "click" : true
31817     });
31818 };
31819
31820 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
31821     
31822     files : false,
31823     
31824     indicator : 0,
31825     
31826     getAutoCreate : function()
31827     {
31828         var cfg = {
31829             tag : 'div',
31830             cls : 'roo-document-slider',
31831             cn : [
31832                 {
31833                     tag : 'div',
31834                     cls : 'roo-document-slider-header',
31835                     cn : [
31836                         {
31837                             tag : 'div',
31838                             cls : 'roo-document-slider-header-title'
31839                         }
31840                     ]
31841                 },
31842                 {
31843                     tag : 'div',
31844                     cls : 'roo-document-slider-body',
31845                     cn : [
31846                         {
31847                             tag : 'div',
31848                             cls : 'roo-document-slider-prev',
31849                             cn : [
31850                                 {
31851                                     tag : 'i',
31852                                     cls : 'fa fa-chevron-left'
31853                                 }
31854                             ]
31855                         },
31856                         {
31857                             tag : 'div',
31858                             cls : 'roo-document-slider-thumb',
31859                             cn : [
31860                                 {
31861                                     tag : 'img',
31862                                     cls : 'roo-document-slider-image'
31863                                 }
31864                             ]
31865                         },
31866                         {
31867                             tag : 'div',
31868                             cls : 'roo-document-slider-next',
31869                             cn : [
31870                                 {
31871                                     tag : 'i',
31872                                     cls : 'fa fa-chevron-right'
31873                                 }
31874                             ]
31875                         }
31876                     ]
31877                 }
31878             ]
31879         };
31880         
31881         return cfg;
31882     },
31883     
31884     initEvents : function()
31885     {
31886         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
31887         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
31888         
31889         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
31890         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
31891         
31892         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
31893         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
31894         
31895         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
31896         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
31897         
31898         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
31899         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
31900         
31901         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
31902         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
31903         
31904         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
31905         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
31906         
31907         this.thumbEl.on('click', this.onClick, this);
31908         
31909         this.prevIndicator.on('click', this.prev, this);
31910         
31911         this.nextIndicator.on('click', this.next, this);
31912         
31913     },
31914     
31915     initial : function()
31916     {
31917         if(this.files.length){
31918             this.indicator = 1;
31919             this.update()
31920         }
31921         
31922         this.fireEvent('initial', this);
31923     },
31924     
31925     update : function()
31926     {
31927         this.imageEl.attr('src', this.files[this.indicator - 1]);
31928         
31929         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
31930         
31931         this.prevIndicator.show();
31932         
31933         if(this.indicator == 1){
31934             this.prevIndicator.hide();
31935         }
31936         
31937         this.nextIndicator.show();
31938         
31939         if(this.indicator == this.files.length){
31940             this.nextIndicator.hide();
31941         }
31942         
31943         this.thumbEl.scrollTo('top');
31944         
31945         this.fireEvent('update', this);
31946     },
31947     
31948     onClick : function(e)
31949     {
31950         e.preventDefault();
31951         
31952         this.fireEvent('click', this);
31953     },
31954     
31955     prev : function(e)
31956     {
31957         e.preventDefault();
31958         
31959         this.indicator = Math.max(1, this.indicator - 1);
31960         
31961         this.update();
31962     },
31963     
31964     next : function(e)
31965     {
31966         e.preventDefault();
31967         
31968         this.indicator = Math.min(this.files.length, this.indicator + 1);
31969         
31970         this.update();
31971     }
31972 });
31973 /*
31974  * - LGPL
31975  *
31976  * RadioSet
31977  *
31978  *
31979  */
31980
31981 /**
31982  * @class Roo.bootstrap.RadioSet
31983  * @extends Roo.bootstrap.Input
31984  * Bootstrap RadioSet class
31985  * @cfg {String} indicatorpos (left|right) default left
31986  * @cfg {Boolean} inline (true|false) inline the element (default true)
31987  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
31988  * @constructor
31989  * Create a new RadioSet
31990  * @param {Object} config The config object
31991  */
31992
31993 Roo.bootstrap.RadioSet = function(config){
31994     
31995     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
31996
31997     this.radioes = [];
31998     
31999     Roo.bootstrap.RadioSet.register(this);
32000     
32001     this.addEvents({
32002         /**
32003         * @event check
32004         * Fires when the element is checked or unchecked.
32005         * @param {Roo.bootstrap.RadioSet} this This radio
32006         * @param {Roo.bootstrap.Radio} item The checked item
32007         */
32008        check : true
32009     });
32010     
32011 };
32012
32013 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
32014
32015     radioes : false,
32016     
32017     inline : true,
32018     
32019     weight : '',
32020     
32021     fieldLabel : '',
32022     
32023     indicatorpos : 'left',
32024     
32025     getAutoCreate : function()
32026     {
32027         var label = {
32028             tag : 'label',
32029             cls : 'roo-radio-set-label',
32030             cn : [
32031                 {
32032                     tag : 'span',
32033                     html : this.fieldLabel
32034                 }
32035             ]
32036         };
32037         
32038         if(this.indicatorpos == 'left'){
32039             label.cn.unshift({
32040                 tag : 'i',
32041                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
32042                 tooltip : 'This field is required'
32043             });
32044         } else {
32045             label.cn.push({
32046                 tag : 'i',
32047                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
32048                 tooltip : 'This field is required'
32049             });
32050         }
32051         
32052         var items = {
32053             tag : 'div',
32054             cls : 'roo-radio-set-items'
32055         };
32056         
32057         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
32058         
32059         if (align === 'left' && this.fieldLabel.length) {
32060             
32061             label.cls += ' col-md-' + this.labelWidth;
32062             
32063             items = {
32064                 cls : "roo-radio-set-right col-md-" + (12 - this.labelWidth), 
32065                 cn: [
32066                     items
32067                 ]
32068             };
32069         }
32070         
32071         var cfg = {
32072             tag : 'div',
32073             cls : 'roo-radio-set',
32074             cn : [
32075                 {
32076                     tag : 'input',
32077                     cls : 'roo-radio-set-input',
32078                     type : 'text',
32079                     name : this.name,
32080                     value : this.value ? this.value :  ''
32081                 },
32082                 label,
32083                 items
32084             ]
32085         };
32086         
32087         if(this.weight.length){
32088             cfg.cls += ' roo-radio-' + this.weight;
32089         }
32090         
32091         if(this.inline) {
32092             cfg.cls += ' roo-radio-set-inline';
32093         }
32094         
32095         return cfg;
32096         
32097     },
32098
32099     initEvents : function()
32100     {
32101         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
32102         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
32103         
32104         if(!this.fieldLabel.length){
32105             this.labelEl.hide();
32106         }
32107         
32108         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
32109         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
32110         
32111         this.indicatorEl().setVisibilityMode(Roo.Element.DISPLAY);
32112         this.indicatorEl().hide();
32113         
32114         this.originalValue = this.getValue();
32115         
32116     },
32117     
32118     inputEl: function ()
32119     {
32120         return this.el.select('.roo-radio-set-input', true).first();
32121     },
32122     
32123     getChildContainer : function()
32124     {
32125         return this.itemsEl;
32126     },
32127     
32128     register : function(item)
32129     {
32130         this.radioes.push(item);
32131         
32132     },
32133     
32134     validate : function()
32135     {   
32136         var valid = false;
32137         
32138         Roo.each(this.radioes, function(i){
32139             if(!i.checked){
32140                 return;
32141             }
32142             
32143             valid = true;
32144             return false;
32145         });
32146         
32147         if(this.disabled || this.allowBlank || valid){
32148             this.markValid();
32149             return true;
32150         }
32151         
32152         this.markInvalid();
32153         return false;
32154         
32155     },
32156     
32157     markValid : function()
32158     {
32159         if(this.labelEl.isVisible(true)){
32160             this.indicatorEl().hide();
32161         }
32162         
32163         this.el.removeClass([this.invalidClass, this.validClass]);
32164         this.el.addClass(this.validClass);
32165         
32166         this.fireEvent('valid', this);
32167     },
32168     
32169     markInvalid : function(msg)
32170     {
32171         if(this.allowBlank || this.disabled){
32172             return;
32173         }
32174         
32175         if(this.labelEl.isVisible(true)){
32176             this.indicatorEl().show();
32177         }
32178         
32179         this.el.removeClass([this.invalidClass, this.validClass]);
32180         this.el.addClass(this.invalidClass);
32181         
32182         this.fireEvent('invalid', this, msg);
32183         
32184     },
32185     
32186     setValue : function(v, suppressEvent)
32187     {   
32188         Roo.each(this.radioes, function(i){
32189             
32190             i.checked = false;
32191             i.el.removeClass('checked');
32192             
32193             if(i.value === v || i.value.toString() === v.toString()){
32194                 i.checked = true;
32195                 i.el.addClass('checked');
32196                 
32197                 if(suppressEvent !== true){
32198                     this.fireEvent('check', this, i);
32199                 }
32200             }
32201             
32202         }, this);
32203         
32204         Roo.bootstrap.RadioSet.superclass.setValue.call(this, v);
32205         
32206     },
32207     
32208     clearInvalid : function(){
32209         
32210         if(!this.el || this.preventMark){
32211             return;
32212         }
32213         
32214         if(this.labelEl.isVisible(true)){
32215             this.indicatorEl().hide();
32216         }
32217         
32218         this.el.removeClass([this.invalidClass]);
32219         
32220         this.fireEvent('valid', this);
32221     }
32222     
32223 });
32224
32225 Roo.apply(Roo.bootstrap.RadioSet, {
32226     
32227     groups: {},
32228     
32229     register : function(set)
32230     {
32231         this.groups[set.name] = set;
32232     },
32233     
32234     get: function(name) 
32235     {
32236         if (typeof(this.groups[name]) == 'undefined') {
32237             return false;
32238         }
32239         
32240         return this.groups[name] ;
32241     }
32242     
32243 });
32244 /*
32245  * Based on:
32246  * Ext JS Library 1.1.1
32247  * Copyright(c) 2006-2007, Ext JS, LLC.
32248  *
32249  * Originally Released Under LGPL - original licence link has changed is not relivant.
32250  *
32251  * Fork - LGPL
32252  * <script type="text/javascript">
32253  */
32254
32255
32256 /**
32257  * @class Roo.bootstrap.SplitBar
32258  * @extends Roo.util.Observable
32259  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
32260  * <br><br>
32261  * Usage:
32262  * <pre><code>
32263 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
32264                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
32265 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
32266 split.minSize = 100;
32267 split.maxSize = 600;
32268 split.animate = true;
32269 split.on('moved', splitterMoved);
32270 </code></pre>
32271  * @constructor
32272  * Create a new SplitBar
32273  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
32274  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
32275  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
32276  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
32277                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
32278                         position of the SplitBar).
32279  */
32280 Roo.bootstrap.SplitBar = function(cfg){
32281     
32282     /** @private */
32283     
32284     //{
32285     //  dragElement : elm
32286     //  resizingElement: el,
32287         // optional..
32288     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
32289     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
32290         // existingProxy ???
32291     //}
32292     
32293     this.el = Roo.get(cfg.dragElement, true);
32294     this.el.dom.unselectable = "on";
32295     /** @private */
32296     this.resizingEl = Roo.get(cfg.resizingElement, true);
32297
32298     /**
32299      * @private
32300      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
32301      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
32302      * @type Number
32303      */
32304     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
32305     
32306     /**
32307      * The minimum size of the resizing element. (Defaults to 0)
32308      * @type Number
32309      */
32310     this.minSize = 0;
32311     
32312     /**
32313      * The maximum size of the resizing element. (Defaults to 2000)
32314      * @type Number
32315      */
32316     this.maxSize = 2000;
32317     
32318     /**
32319      * Whether to animate the transition to the new size
32320      * @type Boolean
32321      */
32322     this.animate = false;
32323     
32324     /**
32325      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
32326      * @type Boolean
32327      */
32328     this.useShim = false;
32329     
32330     /** @private */
32331     this.shim = null;
32332     
32333     if(!cfg.existingProxy){
32334         /** @private */
32335         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
32336     }else{
32337         this.proxy = Roo.get(cfg.existingProxy).dom;
32338     }
32339     /** @private */
32340     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
32341     
32342     /** @private */
32343     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
32344     
32345     /** @private */
32346     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
32347     
32348     /** @private */
32349     this.dragSpecs = {};
32350     
32351     /**
32352      * @private The adapter to use to positon and resize elements
32353      */
32354     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
32355     this.adapter.init(this);
32356     
32357     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32358         /** @private */
32359         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
32360         this.el.addClass("roo-splitbar-h");
32361     }else{
32362         /** @private */
32363         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
32364         this.el.addClass("roo-splitbar-v");
32365     }
32366     
32367     this.addEvents({
32368         /**
32369          * @event resize
32370          * Fires when the splitter is moved (alias for {@link #event-moved})
32371          * @param {Roo.bootstrap.SplitBar} this
32372          * @param {Number} newSize the new width or height
32373          */
32374         "resize" : true,
32375         /**
32376          * @event moved
32377          * Fires when the splitter is moved
32378          * @param {Roo.bootstrap.SplitBar} this
32379          * @param {Number} newSize the new width or height
32380          */
32381         "moved" : true,
32382         /**
32383          * @event beforeresize
32384          * Fires before the splitter is dragged
32385          * @param {Roo.bootstrap.SplitBar} this
32386          */
32387         "beforeresize" : true,
32388
32389         "beforeapply" : true
32390     });
32391
32392     Roo.util.Observable.call(this);
32393 };
32394
32395 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
32396     onStartProxyDrag : function(x, y){
32397         this.fireEvent("beforeresize", this);
32398         if(!this.overlay){
32399             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
32400             o.unselectable();
32401             o.enableDisplayMode("block");
32402             // all splitbars share the same overlay
32403             Roo.bootstrap.SplitBar.prototype.overlay = o;
32404         }
32405         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32406         this.overlay.show();
32407         Roo.get(this.proxy).setDisplayed("block");
32408         var size = this.adapter.getElementSize(this);
32409         this.activeMinSize = this.getMinimumSize();;
32410         this.activeMaxSize = this.getMaximumSize();;
32411         var c1 = size - this.activeMinSize;
32412         var c2 = Math.max(this.activeMaxSize - size, 0);
32413         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32414             this.dd.resetConstraints();
32415             this.dd.setXConstraint(
32416                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
32417                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
32418             );
32419             this.dd.setYConstraint(0, 0);
32420         }else{
32421             this.dd.resetConstraints();
32422             this.dd.setXConstraint(0, 0);
32423             this.dd.setYConstraint(
32424                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
32425                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
32426             );
32427          }
32428         this.dragSpecs.startSize = size;
32429         this.dragSpecs.startPoint = [x, y];
32430         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
32431     },
32432     
32433     /** 
32434      * @private Called after the drag operation by the DDProxy
32435      */
32436     onEndProxyDrag : function(e){
32437         Roo.get(this.proxy).setDisplayed(false);
32438         var endPoint = Roo.lib.Event.getXY(e);
32439         if(this.overlay){
32440             this.overlay.hide();
32441         }
32442         var newSize;
32443         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32444             newSize = this.dragSpecs.startSize + 
32445                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
32446                     endPoint[0] - this.dragSpecs.startPoint[0] :
32447                     this.dragSpecs.startPoint[0] - endPoint[0]
32448                 );
32449         }else{
32450             newSize = this.dragSpecs.startSize + 
32451                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
32452                     endPoint[1] - this.dragSpecs.startPoint[1] :
32453                     this.dragSpecs.startPoint[1] - endPoint[1]
32454                 );
32455         }
32456         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
32457         if(newSize != this.dragSpecs.startSize){
32458             if(this.fireEvent('beforeapply', this, newSize) !== false){
32459                 this.adapter.setElementSize(this, newSize);
32460                 this.fireEvent("moved", this, newSize);
32461                 this.fireEvent("resize", this, newSize);
32462             }
32463         }
32464     },
32465     
32466     /**
32467      * Get the adapter this SplitBar uses
32468      * @return The adapter object
32469      */
32470     getAdapter : function(){
32471         return this.adapter;
32472     },
32473     
32474     /**
32475      * Set the adapter this SplitBar uses
32476      * @param {Object} adapter A SplitBar adapter object
32477      */
32478     setAdapter : function(adapter){
32479         this.adapter = adapter;
32480         this.adapter.init(this);
32481     },
32482     
32483     /**
32484      * Gets the minimum size for the resizing element
32485      * @return {Number} The minimum size
32486      */
32487     getMinimumSize : function(){
32488         return this.minSize;
32489     },
32490     
32491     /**
32492      * Sets the minimum size for the resizing element
32493      * @param {Number} minSize The minimum size
32494      */
32495     setMinimumSize : function(minSize){
32496         this.minSize = minSize;
32497     },
32498     
32499     /**
32500      * Gets the maximum size for the resizing element
32501      * @return {Number} The maximum size
32502      */
32503     getMaximumSize : function(){
32504         return this.maxSize;
32505     },
32506     
32507     /**
32508      * Sets the maximum size for the resizing element
32509      * @param {Number} maxSize The maximum size
32510      */
32511     setMaximumSize : function(maxSize){
32512         this.maxSize = maxSize;
32513     },
32514     
32515     /**
32516      * Sets the initialize size for the resizing element
32517      * @param {Number} size The initial size
32518      */
32519     setCurrentSize : function(size){
32520         var oldAnimate = this.animate;
32521         this.animate = false;
32522         this.adapter.setElementSize(this, size);
32523         this.animate = oldAnimate;
32524     },
32525     
32526     /**
32527      * Destroy this splitbar. 
32528      * @param {Boolean} removeEl True to remove the element
32529      */
32530     destroy : function(removeEl){
32531         if(this.shim){
32532             this.shim.remove();
32533         }
32534         this.dd.unreg();
32535         this.proxy.parentNode.removeChild(this.proxy);
32536         if(removeEl){
32537             this.el.remove();
32538         }
32539     }
32540 });
32541
32542 /**
32543  * @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.
32544  */
32545 Roo.bootstrap.SplitBar.createProxy = function(dir){
32546     var proxy = new Roo.Element(document.createElement("div"));
32547     proxy.unselectable();
32548     var cls = 'roo-splitbar-proxy';
32549     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
32550     document.body.appendChild(proxy.dom);
32551     return proxy.dom;
32552 };
32553
32554 /** 
32555  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
32556  * Default Adapter. It assumes the splitter and resizing element are not positioned
32557  * elements and only gets/sets the width of the element. Generally used for table based layouts.
32558  */
32559 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
32560 };
32561
32562 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
32563     // do nothing for now
32564     init : function(s){
32565     
32566     },
32567     /**
32568      * Called before drag operations to get the current size of the resizing element. 
32569      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
32570      */
32571      getElementSize : function(s){
32572         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32573             return s.resizingEl.getWidth();
32574         }else{
32575             return s.resizingEl.getHeight();
32576         }
32577     },
32578     
32579     /**
32580      * Called after drag operations to set the size of the resizing element.
32581      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
32582      * @param {Number} newSize The new size to set
32583      * @param {Function} onComplete A function to be invoked when resizing is complete
32584      */
32585     setElementSize : function(s, newSize, onComplete){
32586         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32587             if(!s.animate){
32588                 s.resizingEl.setWidth(newSize);
32589                 if(onComplete){
32590                     onComplete(s, newSize);
32591                 }
32592             }else{
32593                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
32594             }
32595         }else{
32596             
32597             if(!s.animate){
32598                 s.resizingEl.setHeight(newSize);
32599                 if(onComplete){
32600                     onComplete(s, newSize);
32601                 }
32602             }else{
32603                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
32604             }
32605         }
32606     }
32607 };
32608
32609 /** 
32610  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
32611  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
32612  * Adapter that  moves the splitter element to align with the resized sizing element. 
32613  * Used with an absolute positioned SplitBar.
32614  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
32615  * document.body, make sure you assign an id to the body element.
32616  */
32617 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
32618     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
32619     this.container = Roo.get(container);
32620 };
32621
32622 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
32623     init : function(s){
32624         this.basic.init(s);
32625     },
32626     
32627     getElementSize : function(s){
32628         return this.basic.getElementSize(s);
32629     },
32630     
32631     setElementSize : function(s, newSize, onComplete){
32632         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
32633     },
32634     
32635     moveSplitter : function(s){
32636         var yes = Roo.bootstrap.SplitBar;
32637         switch(s.placement){
32638             case yes.LEFT:
32639                 s.el.setX(s.resizingEl.getRight());
32640                 break;
32641             case yes.RIGHT:
32642                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
32643                 break;
32644             case yes.TOP:
32645                 s.el.setY(s.resizingEl.getBottom());
32646                 break;
32647             case yes.BOTTOM:
32648                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
32649                 break;
32650         }
32651     }
32652 };
32653
32654 /**
32655  * Orientation constant - Create a vertical SplitBar
32656  * @static
32657  * @type Number
32658  */
32659 Roo.bootstrap.SplitBar.VERTICAL = 1;
32660
32661 /**
32662  * Orientation constant - Create a horizontal SplitBar
32663  * @static
32664  * @type Number
32665  */
32666 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
32667
32668 /**
32669  * Placement constant - The resizing element is to the left of the splitter element
32670  * @static
32671  * @type Number
32672  */
32673 Roo.bootstrap.SplitBar.LEFT = 1;
32674
32675 /**
32676  * Placement constant - The resizing element is to the right of the splitter element
32677  * @static
32678  * @type Number
32679  */
32680 Roo.bootstrap.SplitBar.RIGHT = 2;
32681
32682 /**
32683  * Placement constant - The resizing element is positioned above the splitter element
32684  * @static
32685  * @type Number
32686  */
32687 Roo.bootstrap.SplitBar.TOP = 3;
32688
32689 /**
32690  * Placement constant - The resizing element is positioned under splitter element
32691  * @static
32692  * @type Number
32693  */
32694 Roo.bootstrap.SplitBar.BOTTOM = 4;
32695 Roo.namespace("Roo.bootstrap.layout");/*
32696  * Based on:
32697  * Ext JS Library 1.1.1
32698  * Copyright(c) 2006-2007, Ext JS, LLC.
32699  *
32700  * Originally Released Under LGPL - original licence link has changed is not relivant.
32701  *
32702  * Fork - LGPL
32703  * <script type="text/javascript">
32704  */
32705
32706 /**
32707  * @class Roo.bootstrap.layout.Manager
32708  * @extends Roo.bootstrap.Component
32709  * Base class for layout managers.
32710  */
32711 Roo.bootstrap.layout.Manager = function(config)
32712 {
32713     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
32714
32715
32716
32717
32718
32719     /** false to disable window resize monitoring @type Boolean */
32720     this.monitorWindowResize = true;
32721     this.regions = {};
32722     this.addEvents({
32723         /**
32724          * @event layout
32725          * Fires when a layout is performed.
32726          * @param {Roo.LayoutManager} this
32727          */
32728         "layout" : true,
32729         /**
32730          * @event regionresized
32731          * Fires when the user resizes a region.
32732          * @param {Roo.LayoutRegion} region The resized region
32733          * @param {Number} newSize The new size (width for east/west, height for north/south)
32734          */
32735         "regionresized" : true,
32736         /**
32737          * @event regioncollapsed
32738          * Fires when a region is collapsed.
32739          * @param {Roo.LayoutRegion} region The collapsed region
32740          */
32741         "regioncollapsed" : true,
32742         /**
32743          * @event regionexpanded
32744          * Fires when a region is expanded.
32745          * @param {Roo.LayoutRegion} region The expanded region
32746          */
32747         "regionexpanded" : true
32748     });
32749     this.updating = false;
32750
32751     if (config.el) {
32752         this.el = Roo.get(config.el);
32753         this.initEvents();
32754     }
32755
32756 };
32757
32758 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
32759
32760
32761     regions : null,
32762
32763     monitorWindowResize : true,
32764
32765
32766     updating : false,
32767
32768
32769     onRender : function(ct, position)
32770     {
32771         if(!this.el){
32772             this.el = Roo.get(ct);
32773             this.initEvents();
32774         }
32775         //this.fireEvent('render',this);
32776     },
32777
32778
32779     initEvents: function()
32780     {
32781
32782
32783         // ie scrollbar fix
32784         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32785             document.body.scroll = "no";
32786         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32787             this.el.position('relative');
32788         }
32789         this.id = this.el.id;
32790         this.el.addClass("roo-layout-container");
32791         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32792         if(this.el.dom != document.body ) {
32793             this.el.on('resize', this.layout,this);
32794             this.el.on('show', this.layout,this);
32795         }
32796
32797     },
32798
32799     /**
32800      * Returns true if this layout is currently being updated
32801      * @return {Boolean}
32802      */
32803     isUpdating : function(){
32804         return this.updating;
32805     },
32806
32807     /**
32808      * Suspend the LayoutManager from doing auto-layouts while
32809      * making multiple add or remove calls
32810      */
32811     beginUpdate : function(){
32812         this.updating = true;
32813     },
32814
32815     /**
32816      * Restore auto-layouts and optionally disable the manager from performing a layout
32817      * @param {Boolean} noLayout true to disable a layout update
32818      */
32819     endUpdate : function(noLayout){
32820         this.updating = false;
32821         if(!noLayout){
32822             this.layout();
32823         }
32824     },
32825
32826     layout: function(){
32827         // abstract...
32828     },
32829
32830     onRegionResized : function(region, newSize){
32831         this.fireEvent("regionresized", region, newSize);
32832         this.layout();
32833     },
32834
32835     onRegionCollapsed : function(region){
32836         this.fireEvent("regioncollapsed", region);
32837     },
32838
32839     onRegionExpanded : function(region){
32840         this.fireEvent("regionexpanded", region);
32841     },
32842
32843     /**
32844      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32845      * performs box-model adjustments.
32846      * @return {Object} The size as an object {width: (the width), height: (the height)}
32847      */
32848     getViewSize : function()
32849     {
32850         var size;
32851         if(this.el.dom != document.body){
32852             size = this.el.getSize();
32853         }else{
32854             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32855         }
32856         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32857         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32858         return size;
32859     },
32860
32861     /**
32862      * Returns the Element this layout is bound to.
32863      * @return {Roo.Element}
32864      */
32865     getEl : function(){
32866         return this.el;
32867     },
32868
32869     /**
32870      * Returns the specified region.
32871      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32872      * @return {Roo.LayoutRegion}
32873      */
32874     getRegion : function(target){
32875         return this.regions[target.toLowerCase()];
32876     },
32877
32878     onWindowResize : function(){
32879         if(this.monitorWindowResize){
32880             this.layout();
32881         }
32882     }
32883 });
32884 /*
32885  * Based on:
32886  * Ext JS Library 1.1.1
32887  * Copyright(c) 2006-2007, Ext JS, LLC.
32888  *
32889  * Originally Released Under LGPL - original licence link has changed is not relivant.
32890  *
32891  * Fork - LGPL
32892  * <script type="text/javascript">
32893  */
32894 /**
32895  * @class Roo.bootstrap.layout.Border
32896  * @extends Roo.bootstrap.layout.Manager
32897  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32898  * please see: examples/bootstrap/nested.html<br><br>
32899  
32900 <b>The container the layout is rendered into can be either the body element or any other element.
32901 If it is not the body element, the container needs to either be an absolute positioned element,
32902 or you will need to add "position:relative" to the css of the container.  You will also need to specify
32903 the container size if it is not the body element.</b>
32904
32905 * @constructor
32906 * Create a new Border
32907 * @param {Object} config Configuration options
32908  */
32909 Roo.bootstrap.layout.Border = function(config){
32910     config = config || {};
32911     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
32912     
32913     
32914     
32915     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
32916         if(config[region]){
32917             config[region].region = region;
32918             this.addRegion(config[region]);
32919         }
32920     },this);
32921     
32922 };
32923
32924 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
32925
32926 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
32927     /**
32928      * Creates and adds a new region if it doesn't already exist.
32929      * @param {String} target The target region key (north, south, east, west or center).
32930      * @param {Object} config The regions config object
32931      * @return {BorderLayoutRegion} The new region
32932      */
32933     addRegion : function(config)
32934     {
32935         if(!this.regions[config.region]){
32936             var r = this.factory(config);
32937             this.bindRegion(r);
32938         }
32939         return this.regions[config.region];
32940     },
32941
32942     // private (kinda)
32943     bindRegion : function(r){
32944         this.regions[r.config.region] = r;
32945         
32946         r.on("visibilitychange",    this.layout, this);
32947         r.on("paneladded",          this.layout, this);
32948         r.on("panelremoved",        this.layout, this);
32949         r.on("invalidated",         this.layout, this);
32950         r.on("resized",             this.onRegionResized, this);
32951         r.on("collapsed",           this.onRegionCollapsed, this);
32952         r.on("expanded",            this.onRegionExpanded, this);
32953     },
32954
32955     /**
32956      * Performs a layout update.
32957      */
32958     layout : function()
32959     {
32960         if(this.updating) {
32961             return;
32962         }
32963         
32964         // render all the rebions if they have not been done alreayd?
32965         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
32966             if(this.regions[region] && !this.regions[region].bodyEl){
32967                 this.regions[region].onRender(this.el)
32968             }
32969         },this);
32970         
32971         var size = this.getViewSize();
32972         var w = size.width;
32973         var h = size.height;
32974         var centerW = w;
32975         var centerH = h;
32976         var centerY = 0;
32977         var centerX = 0;
32978         //var x = 0, y = 0;
32979
32980         var rs = this.regions;
32981         var north = rs["north"];
32982         var south = rs["south"]; 
32983         var west = rs["west"];
32984         var east = rs["east"];
32985         var center = rs["center"];
32986         //if(this.hideOnLayout){ // not supported anymore
32987             //c.el.setStyle("display", "none");
32988         //}
32989         if(north && north.isVisible()){
32990             var b = north.getBox();
32991             var m = north.getMargins();
32992             b.width = w - (m.left+m.right);
32993             b.x = m.left;
32994             b.y = m.top;
32995             centerY = b.height + b.y + m.bottom;
32996             centerH -= centerY;
32997             north.updateBox(this.safeBox(b));
32998         }
32999         if(south && south.isVisible()){
33000             var b = south.getBox();
33001             var m = south.getMargins();
33002             b.width = w - (m.left+m.right);
33003             b.x = m.left;
33004             var totalHeight = (b.height + m.top + m.bottom);
33005             b.y = h - totalHeight + m.top;
33006             centerH -= totalHeight;
33007             south.updateBox(this.safeBox(b));
33008         }
33009         if(west && west.isVisible()){
33010             var b = west.getBox();
33011             var m = west.getMargins();
33012             b.height = centerH - (m.top+m.bottom);
33013             b.x = m.left;
33014             b.y = centerY + m.top;
33015             var totalWidth = (b.width + m.left + m.right);
33016             centerX += totalWidth;
33017             centerW -= totalWidth;
33018             west.updateBox(this.safeBox(b));
33019         }
33020         if(east && east.isVisible()){
33021             var b = east.getBox();
33022             var m = east.getMargins();
33023             b.height = centerH - (m.top+m.bottom);
33024             var totalWidth = (b.width + m.left + m.right);
33025             b.x = w - totalWidth + m.left;
33026             b.y = centerY + m.top;
33027             centerW -= totalWidth;
33028             east.updateBox(this.safeBox(b));
33029         }
33030         if(center){
33031             var m = center.getMargins();
33032             var centerBox = {
33033                 x: centerX + m.left,
33034                 y: centerY + m.top,
33035                 width: centerW - (m.left+m.right),
33036                 height: centerH - (m.top+m.bottom)
33037             };
33038             //if(this.hideOnLayout){
33039                 //center.el.setStyle("display", "block");
33040             //}
33041             center.updateBox(this.safeBox(centerBox));
33042         }
33043         this.el.repaint();
33044         this.fireEvent("layout", this);
33045     },
33046
33047     // private
33048     safeBox : function(box){
33049         box.width = Math.max(0, box.width);
33050         box.height = Math.max(0, box.height);
33051         return box;
33052     },
33053
33054     /**
33055      * Adds a ContentPanel (or subclass) to this layout.
33056      * @param {String} target The target region key (north, south, east, west or center).
33057      * @param {Roo.ContentPanel} panel The panel to add
33058      * @return {Roo.ContentPanel} The added panel
33059      */
33060     add : function(target, panel){
33061          
33062         target = target.toLowerCase();
33063         return this.regions[target].add(panel);
33064     },
33065
33066     /**
33067      * Remove a ContentPanel (or subclass) to this layout.
33068      * @param {String} target The target region key (north, south, east, west or center).
33069      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
33070      * @return {Roo.ContentPanel} The removed panel
33071      */
33072     remove : function(target, panel){
33073         target = target.toLowerCase();
33074         return this.regions[target].remove(panel);
33075     },
33076
33077     /**
33078      * Searches all regions for a panel with the specified id
33079      * @param {String} panelId
33080      * @return {Roo.ContentPanel} The panel or null if it wasn't found
33081      */
33082     findPanel : function(panelId){
33083         var rs = this.regions;
33084         for(var target in rs){
33085             if(typeof rs[target] != "function"){
33086                 var p = rs[target].getPanel(panelId);
33087                 if(p){
33088                     return p;
33089                 }
33090             }
33091         }
33092         return null;
33093     },
33094
33095     /**
33096      * Searches all regions for a panel with the specified id and activates (shows) it.
33097      * @param {String/ContentPanel} panelId The panels id or the panel itself
33098      * @return {Roo.ContentPanel} The shown panel or null
33099      */
33100     showPanel : function(panelId) {
33101       var rs = this.regions;
33102       for(var target in rs){
33103          var r = rs[target];
33104          if(typeof r != "function"){
33105             if(r.hasPanel(panelId)){
33106                return r.showPanel(panelId);
33107             }
33108          }
33109       }
33110       return null;
33111    },
33112
33113    /**
33114      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
33115      * @param {Roo.state.Provider} provider (optional) An alternate state provider
33116      */
33117    /*
33118     restoreState : function(provider){
33119         if(!provider){
33120             provider = Roo.state.Manager;
33121         }
33122         var sm = new Roo.LayoutStateManager();
33123         sm.init(this, provider);
33124     },
33125 */
33126  
33127  
33128     /**
33129      * Adds a xtype elements to the layout.
33130      * <pre><code>
33131
33132 layout.addxtype({
33133        xtype : 'ContentPanel',
33134        region: 'west',
33135        items: [ .... ]
33136    }
33137 );
33138
33139 layout.addxtype({
33140         xtype : 'NestedLayoutPanel',
33141         region: 'west',
33142         layout: {
33143            center: { },
33144            west: { }   
33145         },
33146         items : [ ... list of content panels or nested layout panels.. ]
33147    }
33148 );
33149 </code></pre>
33150      * @param {Object} cfg Xtype definition of item to add.
33151      */
33152     addxtype : function(cfg)
33153     {
33154         // basically accepts a pannel...
33155         // can accept a layout region..!?!?
33156         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
33157         
33158         
33159         // theory?  children can only be panels??
33160         
33161         //if (!cfg.xtype.match(/Panel$/)) {
33162         //    return false;
33163         //}
33164         var ret = false;
33165         
33166         if (typeof(cfg.region) == 'undefined') {
33167             Roo.log("Failed to add Panel, region was not set");
33168             Roo.log(cfg);
33169             return false;
33170         }
33171         var region = cfg.region;
33172         delete cfg.region;
33173         
33174           
33175         var xitems = [];
33176         if (cfg.items) {
33177             xitems = cfg.items;
33178             delete cfg.items;
33179         }
33180         var nb = false;
33181         
33182         switch(cfg.xtype) 
33183         {
33184             case 'Content':  // ContentPanel (el, cfg)
33185             case 'Scroll':  // ContentPanel (el, cfg)
33186             case 'View': 
33187                 cfg.autoCreate = true;
33188                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33189                 //} else {
33190                 //    var el = this.el.createChild();
33191                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
33192                 //}
33193                 
33194                 this.add(region, ret);
33195                 break;
33196             
33197             /*
33198             case 'TreePanel': // our new panel!
33199                 cfg.el = this.el.createChild();
33200                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33201                 this.add(region, ret);
33202                 break;
33203             */
33204             
33205             case 'Nest': 
33206                 // create a new Layout (which is  a Border Layout...
33207                 
33208                 var clayout = cfg.layout;
33209                 clayout.el  = this.el.createChild();
33210                 clayout.items   = clayout.items  || [];
33211                 
33212                 delete cfg.layout;
33213                 
33214                 // replace this exitems with the clayout ones..
33215                 xitems = clayout.items;
33216                  
33217                 // force background off if it's in center...
33218                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
33219                     cfg.background = false;
33220                 }
33221                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
33222                 
33223                 
33224                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33225                 //console.log('adding nested layout panel '  + cfg.toSource());
33226                 this.add(region, ret);
33227                 nb = {}; /// find first...
33228                 break;
33229             
33230             case 'Grid':
33231                 
33232                 // needs grid and region
33233                 
33234                 //var el = this.getRegion(region).el.createChild();
33235                 /*
33236                  *var el = this.el.createChild();
33237                 // create the grid first...
33238                 cfg.grid.container = el;
33239                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
33240                 */
33241                 
33242                 if (region == 'center' && this.active ) {
33243                     cfg.background = false;
33244                 }
33245                 
33246                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33247                 
33248                 this.add(region, ret);
33249                 /*
33250                 if (cfg.background) {
33251                     // render grid on panel activation (if panel background)
33252                     ret.on('activate', function(gp) {
33253                         if (!gp.grid.rendered) {
33254                     //        gp.grid.render(el);
33255                         }
33256                     });
33257                 } else {
33258                   //  cfg.grid.render(el);
33259                 }
33260                 */
33261                 break;
33262            
33263            
33264             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
33265                 // it was the old xcomponent building that caused this before.
33266                 // espeically if border is the top element in the tree.
33267                 ret = this;
33268                 break; 
33269                 
33270                     
33271                 
33272                 
33273                 
33274             default:
33275                 /*
33276                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
33277                     
33278                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33279                     this.add(region, ret);
33280                 } else {
33281                 */
33282                     Roo.log(cfg);
33283                     throw "Can not add '" + cfg.xtype + "' to Border";
33284                     return null;
33285              
33286                                 
33287              
33288         }
33289         this.beginUpdate();
33290         // add children..
33291         var region = '';
33292         var abn = {};
33293         Roo.each(xitems, function(i)  {
33294             region = nb && i.region ? i.region : false;
33295             
33296             var add = ret.addxtype(i);
33297            
33298             if (region) {
33299                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
33300                 if (!i.background) {
33301                     abn[region] = nb[region] ;
33302                 }
33303             }
33304             
33305         });
33306         this.endUpdate();
33307
33308         // make the last non-background panel active..
33309         //if (nb) { Roo.log(abn); }
33310         if (nb) {
33311             
33312             for(var r in abn) {
33313                 region = this.getRegion(r);
33314                 if (region) {
33315                     // tried using nb[r], but it does not work..
33316                      
33317                     region.showPanel(abn[r]);
33318                    
33319                 }
33320             }
33321         }
33322         return ret;
33323         
33324     },
33325     
33326     
33327 // private
33328     factory : function(cfg)
33329     {
33330         
33331         var validRegions = Roo.bootstrap.layout.Border.regions;
33332
33333         var target = cfg.region;
33334         cfg.mgr = this;
33335         
33336         var r = Roo.bootstrap.layout;
33337         Roo.log(target);
33338         switch(target){
33339             case "north":
33340                 return new r.North(cfg);
33341             case "south":
33342                 return new r.South(cfg);
33343             case "east":
33344                 return new r.East(cfg);
33345             case "west":
33346                 return new r.West(cfg);
33347             case "center":
33348                 return new r.Center(cfg);
33349         }
33350         throw 'Layout region "'+target+'" not supported.';
33351     }
33352     
33353     
33354 });
33355  /*
33356  * Based on:
33357  * Ext JS Library 1.1.1
33358  * Copyright(c) 2006-2007, Ext JS, LLC.
33359  *
33360  * Originally Released Under LGPL - original licence link has changed is not relivant.
33361  *
33362  * Fork - LGPL
33363  * <script type="text/javascript">
33364  */
33365  
33366 /**
33367  * @class Roo.bootstrap.layout.Basic
33368  * @extends Roo.util.Observable
33369  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
33370  * and does not have a titlebar, tabs or any other features. All it does is size and position 
33371  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
33372  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
33373  * @cfg {string}   region  the region that it inhabits..
33374  * @cfg {bool}   skipConfig skip config?
33375  * 
33376
33377  */
33378 Roo.bootstrap.layout.Basic = function(config){
33379     
33380     this.mgr = config.mgr;
33381     
33382     this.position = config.region;
33383     
33384     var skipConfig = config.skipConfig;
33385     
33386     this.events = {
33387         /**
33388          * @scope Roo.BasicLayoutRegion
33389          */
33390         
33391         /**
33392          * @event beforeremove
33393          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
33394          * @param {Roo.LayoutRegion} this
33395          * @param {Roo.ContentPanel} panel The panel
33396          * @param {Object} e The cancel event object
33397          */
33398         "beforeremove" : true,
33399         /**
33400          * @event invalidated
33401          * Fires when the layout for this region is changed.
33402          * @param {Roo.LayoutRegion} this
33403          */
33404         "invalidated" : true,
33405         /**
33406          * @event visibilitychange
33407          * Fires when this region is shown or hidden 
33408          * @param {Roo.LayoutRegion} this
33409          * @param {Boolean} visibility true or false
33410          */
33411         "visibilitychange" : true,
33412         /**
33413          * @event paneladded
33414          * Fires when a panel is added. 
33415          * @param {Roo.LayoutRegion} this
33416          * @param {Roo.ContentPanel} panel The panel
33417          */
33418         "paneladded" : true,
33419         /**
33420          * @event panelremoved
33421          * Fires when a panel is removed. 
33422          * @param {Roo.LayoutRegion} this
33423          * @param {Roo.ContentPanel} panel The panel
33424          */
33425         "panelremoved" : true,
33426         /**
33427          * @event beforecollapse
33428          * Fires when this region before collapse.
33429          * @param {Roo.LayoutRegion} this
33430          */
33431         "beforecollapse" : true,
33432         /**
33433          * @event collapsed
33434          * Fires when this region is collapsed.
33435          * @param {Roo.LayoutRegion} this
33436          */
33437         "collapsed" : true,
33438         /**
33439          * @event expanded
33440          * Fires when this region is expanded.
33441          * @param {Roo.LayoutRegion} this
33442          */
33443         "expanded" : true,
33444         /**
33445          * @event slideshow
33446          * Fires when this region is slid into view.
33447          * @param {Roo.LayoutRegion} this
33448          */
33449         "slideshow" : true,
33450         /**
33451          * @event slidehide
33452          * Fires when this region slides out of view. 
33453          * @param {Roo.LayoutRegion} this
33454          */
33455         "slidehide" : true,
33456         /**
33457          * @event panelactivated
33458          * Fires when a panel is activated. 
33459          * @param {Roo.LayoutRegion} this
33460          * @param {Roo.ContentPanel} panel The activated panel
33461          */
33462         "panelactivated" : true,
33463         /**
33464          * @event resized
33465          * Fires when the user resizes this region. 
33466          * @param {Roo.LayoutRegion} this
33467          * @param {Number} newSize The new size (width for east/west, height for north/south)
33468          */
33469         "resized" : true
33470     };
33471     /** A collection of panels in this region. @type Roo.util.MixedCollection */
33472     this.panels = new Roo.util.MixedCollection();
33473     this.panels.getKey = this.getPanelId.createDelegate(this);
33474     this.box = null;
33475     this.activePanel = null;
33476     // ensure listeners are added...
33477     
33478     if (config.listeners || config.events) {
33479         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
33480             listeners : config.listeners || {},
33481             events : config.events || {}
33482         });
33483     }
33484     
33485     if(skipConfig !== true){
33486         this.applyConfig(config);
33487     }
33488 };
33489
33490 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
33491 {
33492     getPanelId : function(p){
33493         return p.getId();
33494     },
33495     
33496     applyConfig : function(config){
33497         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33498         this.config = config;
33499         
33500     },
33501     
33502     /**
33503      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
33504      * the width, for horizontal (north, south) the height.
33505      * @param {Number} newSize The new width or height
33506      */
33507     resizeTo : function(newSize){
33508         var el = this.el ? this.el :
33509                  (this.activePanel ? this.activePanel.getEl() : null);
33510         if(el){
33511             switch(this.position){
33512                 case "east":
33513                 case "west":
33514                     el.setWidth(newSize);
33515                     this.fireEvent("resized", this, newSize);
33516                 break;
33517                 case "north":
33518                 case "south":
33519                     el.setHeight(newSize);
33520                     this.fireEvent("resized", this, newSize);
33521                 break;                
33522             }
33523         }
33524     },
33525     
33526     getBox : function(){
33527         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33528     },
33529     
33530     getMargins : function(){
33531         return this.margins;
33532     },
33533     
33534     updateBox : function(box){
33535         this.box = box;
33536         var el = this.activePanel.getEl();
33537         el.dom.style.left = box.x + "px";
33538         el.dom.style.top = box.y + "px";
33539         this.activePanel.setSize(box.width, box.height);
33540     },
33541     
33542     /**
33543      * Returns the container element for this region.
33544      * @return {Roo.Element}
33545      */
33546     getEl : function(){
33547         return this.activePanel;
33548     },
33549     
33550     /**
33551      * Returns true if this region is currently visible.
33552      * @return {Boolean}
33553      */
33554     isVisible : function(){
33555         return this.activePanel ? true : false;
33556     },
33557     
33558     setActivePanel : function(panel){
33559         panel = this.getPanel(panel);
33560         if(this.activePanel && this.activePanel != panel){
33561             this.activePanel.setActiveState(false);
33562             this.activePanel.getEl().setLeftTop(-10000,-10000);
33563         }
33564         this.activePanel = panel;
33565         panel.setActiveState(true);
33566         if(this.box){
33567             panel.setSize(this.box.width, this.box.height);
33568         }
33569         this.fireEvent("panelactivated", this, panel);
33570         this.fireEvent("invalidated");
33571     },
33572     
33573     /**
33574      * Show the specified panel.
33575      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33576      * @return {Roo.ContentPanel} The shown panel or null
33577      */
33578     showPanel : function(panel){
33579         panel = this.getPanel(panel);
33580         if(panel){
33581             this.setActivePanel(panel);
33582         }
33583         return panel;
33584     },
33585     
33586     /**
33587      * Get the active panel for this region.
33588      * @return {Roo.ContentPanel} The active panel or null
33589      */
33590     getActivePanel : function(){
33591         return this.activePanel;
33592     },
33593     
33594     /**
33595      * Add the passed ContentPanel(s)
33596      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33597      * @return {Roo.ContentPanel} The panel added (if only one was added)
33598      */
33599     add : function(panel){
33600         if(arguments.length > 1){
33601             for(var i = 0, len = arguments.length; i < len; i++) {
33602                 this.add(arguments[i]);
33603             }
33604             return null;
33605         }
33606         if(this.hasPanel(panel)){
33607             this.showPanel(panel);
33608             return panel;
33609         }
33610         var el = panel.getEl();
33611         if(el.dom.parentNode != this.mgr.el.dom){
33612             this.mgr.el.dom.appendChild(el.dom);
33613         }
33614         if(panel.setRegion){
33615             panel.setRegion(this);
33616         }
33617         this.panels.add(panel);
33618         el.setStyle("position", "absolute");
33619         if(!panel.background){
33620             this.setActivePanel(panel);
33621             if(this.config.initialSize && this.panels.getCount()==1){
33622                 this.resizeTo(this.config.initialSize);
33623             }
33624         }
33625         this.fireEvent("paneladded", this, panel);
33626         return panel;
33627     },
33628     
33629     /**
33630      * Returns true if the panel is in this region.
33631      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33632      * @return {Boolean}
33633      */
33634     hasPanel : function(panel){
33635         if(typeof panel == "object"){ // must be panel obj
33636             panel = panel.getId();
33637         }
33638         return this.getPanel(panel) ? true : false;
33639     },
33640     
33641     /**
33642      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33643      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33644      * @param {Boolean} preservePanel Overrides the config preservePanel option
33645      * @return {Roo.ContentPanel} The panel that was removed
33646      */
33647     remove : function(panel, preservePanel){
33648         panel = this.getPanel(panel);
33649         if(!panel){
33650             return null;
33651         }
33652         var e = {};
33653         this.fireEvent("beforeremove", this, panel, e);
33654         if(e.cancel === true){
33655             return null;
33656         }
33657         var panelId = panel.getId();
33658         this.panels.removeKey(panelId);
33659         return panel;
33660     },
33661     
33662     /**
33663      * Returns the panel specified or null if it's not in this region.
33664      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33665      * @return {Roo.ContentPanel}
33666      */
33667     getPanel : function(id){
33668         if(typeof id == "object"){ // must be panel obj
33669             return id;
33670         }
33671         return this.panels.get(id);
33672     },
33673     
33674     /**
33675      * Returns this regions position (north/south/east/west/center).
33676      * @return {String} 
33677      */
33678     getPosition: function(){
33679         return this.position;    
33680     }
33681 });/*
33682  * Based on:
33683  * Ext JS Library 1.1.1
33684  * Copyright(c) 2006-2007, Ext JS, LLC.
33685  *
33686  * Originally Released Under LGPL - original licence link has changed is not relivant.
33687  *
33688  * Fork - LGPL
33689  * <script type="text/javascript">
33690  */
33691  
33692 /**
33693  * @class Roo.bootstrap.layout.Region
33694  * @extends Roo.bootstrap.layout.Basic
33695  * This class represents a region in a layout manager.
33696  
33697  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33698  * @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})
33699  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
33700  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
33701  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
33702  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
33703  * @cfg {String}    title           The title for the region (overrides panel titles)
33704  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
33705  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33706  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
33707  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33708  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
33709  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33710  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
33711  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
33712  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
33713  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
33714
33715  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
33716  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
33717  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
33718  * @cfg {Number}    width           For East/West panels
33719  * @cfg {Number}    height          For North/South panels
33720  * @cfg {Boolean}   split           To show the splitter
33721  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
33722  * 
33723  * @cfg {string}   cls             Extra CSS classes to add to region
33724  * 
33725  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
33726  * @cfg {string}   region  the region that it inhabits..
33727  *
33728
33729  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
33730  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
33731
33732  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
33733  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
33734  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
33735  */
33736 Roo.bootstrap.layout.Region = function(config)
33737 {
33738     this.applyConfig(config);
33739
33740     var mgr = config.mgr;
33741     var pos = config.region;
33742     config.skipConfig = true;
33743     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
33744     
33745     if (mgr.el) {
33746         this.onRender(mgr.el);   
33747     }
33748      
33749     this.visible = true;
33750     this.collapsed = false;
33751     this.unrendered_panels = [];
33752 };
33753
33754 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
33755
33756     position: '', // set by wrapper (eg. north/south etc..)
33757     unrendered_panels : null,  // unrendered panels.
33758     createBody : function(){
33759         /** This region's body element 
33760         * @type Roo.Element */
33761         this.bodyEl = this.el.createChild({
33762                 tag: "div",
33763                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
33764         });
33765     },
33766
33767     onRender: function(ctr, pos)
33768     {
33769         var dh = Roo.DomHelper;
33770         /** This region's container element 
33771         * @type Roo.Element */
33772         this.el = dh.append(ctr.dom, {
33773                 tag: "div",
33774                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
33775             }, true);
33776         /** This region's title element 
33777         * @type Roo.Element */
33778     
33779         this.titleEl = dh.append(this.el.dom,
33780             {
33781                     tag: "div",
33782                     unselectable: "on",
33783                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
33784                     children:[
33785                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
33786                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
33787                     ]}, true);
33788         
33789         this.titleEl.enableDisplayMode();
33790         /** This region's title text element 
33791         * @type HTMLElement */
33792         this.titleTextEl = this.titleEl.dom.firstChild;
33793         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33794         /*
33795         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
33796         this.closeBtn.enableDisplayMode();
33797         this.closeBtn.on("click", this.closeClicked, this);
33798         this.closeBtn.hide();
33799     */
33800         this.createBody(this.config);
33801         if(this.config.hideWhenEmpty){
33802             this.hide();
33803             this.on("paneladded", this.validateVisibility, this);
33804             this.on("panelremoved", this.validateVisibility, this);
33805         }
33806         if(this.autoScroll){
33807             this.bodyEl.setStyle("overflow", "auto");
33808         }else{
33809             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
33810         }
33811         //if(c.titlebar !== false){
33812             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
33813                 this.titleEl.hide();
33814             }else{
33815                 this.titleEl.show();
33816                 if(this.config.title){
33817                     this.titleTextEl.innerHTML = this.config.title;
33818                 }
33819             }
33820         //}
33821         if(this.config.collapsed){
33822             this.collapse(true);
33823         }
33824         if(this.config.hidden){
33825             this.hide();
33826         }
33827         
33828         if (this.unrendered_panels && this.unrendered_panels.length) {
33829             for (var i =0;i< this.unrendered_panels.length; i++) {
33830                 this.add(this.unrendered_panels[i]);
33831             }
33832             this.unrendered_panels = null;
33833             
33834         }
33835         
33836     },
33837     
33838     applyConfig : function(c)
33839     {
33840         /*
33841          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
33842             var dh = Roo.DomHelper;
33843             if(c.titlebar !== false){
33844                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
33845                 this.collapseBtn.on("click", this.collapse, this);
33846                 this.collapseBtn.enableDisplayMode();
33847                 /*
33848                 if(c.showPin === true || this.showPin){
33849                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
33850                     this.stickBtn.enableDisplayMode();
33851                     this.stickBtn.on("click", this.expand, this);
33852                     this.stickBtn.hide();
33853                 }
33854                 
33855             }
33856             */
33857             /** This region's collapsed element
33858             * @type Roo.Element */
33859             /*
33860              *
33861             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33862                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33863             ]}, true);
33864             
33865             if(c.floatable !== false){
33866                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33867                this.collapsedEl.on("click", this.collapseClick, this);
33868             }
33869
33870             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33871                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33872                    id: "message", unselectable: "on", style:{"float":"left"}});
33873                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33874              }
33875             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33876             this.expandBtn.on("click", this.expand, this);
33877             
33878         }
33879         
33880         if(this.collapseBtn){
33881             this.collapseBtn.setVisible(c.collapsible == true);
33882         }
33883         
33884         this.cmargins = c.cmargins || this.cmargins ||
33885                          (this.position == "west" || this.position == "east" ?
33886                              {top: 0, left: 2, right:2, bottom: 0} :
33887                              {top: 2, left: 0, right:0, bottom: 2});
33888         */
33889         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33890         
33891         
33892         this.bottomTabs = c.tabPosition != "top";
33893         
33894         this.autoScroll = c.autoScroll || false;
33895         
33896         
33897        
33898         
33899         this.duration = c.duration || .30;
33900         this.slideDuration = c.slideDuration || .45;
33901         this.config = c;
33902        
33903     },
33904     /**
33905      * Returns true if this region is currently visible.
33906      * @return {Boolean}
33907      */
33908     isVisible : function(){
33909         return this.visible;
33910     },
33911
33912     /**
33913      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33914      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
33915      */
33916     //setCollapsedTitle : function(title){
33917     //    title = title || "&#160;";
33918      //   if(this.collapsedTitleTextEl){
33919       //      this.collapsedTitleTextEl.innerHTML = title;
33920        // }
33921     //},
33922
33923     getBox : function(){
33924         var b;
33925       //  if(!this.collapsed){
33926             b = this.el.getBox(false, true);
33927        // }else{
33928           //  b = this.collapsedEl.getBox(false, true);
33929         //}
33930         return b;
33931     },
33932
33933     getMargins : function(){
33934         return this.margins;
33935         //return this.collapsed ? this.cmargins : this.margins;
33936     },
33937 /*
33938     highlight : function(){
33939         this.el.addClass("x-layout-panel-dragover");
33940     },
33941
33942     unhighlight : function(){
33943         this.el.removeClass("x-layout-panel-dragover");
33944     },
33945 */
33946     updateBox : function(box)
33947     {
33948         if (!this.bodyEl) {
33949             return; // not rendered yet..
33950         }
33951         
33952         this.box = box;
33953         if(!this.collapsed){
33954             this.el.dom.style.left = box.x + "px";
33955             this.el.dom.style.top = box.y + "px";
33956             this.updateBody(box.width, box.height);
33957         }else{
33958             this.collapsedEl.dom.style.left = box.x + "px";
33959             this.collapsedEl.dom.style.top = box.y + "px";
33960             this.collapsedEl.setSize(box.width, box.height);
33961         }
33962         if(this.tabs){
33963             this.tabs.autoSizeTabs();
33964         }
33965     },
33966
33967     updateBody : function(w, h)
33968     {
33969         if(w !== null){
33970             this.el.setWidth(w);
33971             w -= this.el.getBorderWidth("rl");
33972             if(this.config.adjustments){
33973                 w += this.config.adjustments[0];
33974             }
33975         }
33976         if(h !== null && h > 0){
33977             this.el.setHeight(h);
33978             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
33979             h -= this.el.getBorderWidth("tb");
33980             if(this.config.adjustments){
33981                 h += this.config.adjustments[1];
33982             }
33983             this.bodyEl.setHeight(h);
33984             if(this.tabs){
33985                 h = this.tabs.syncHeight(h);
33986             }
33987         }
33988         if(this.panelSize){
33989             w = w !== null ? w : this.panelSize.width;
33990             h = h !== null ? h : this.panelSize.height;
33991         }
33992         if(this.activePanel){
33993             var el = this.activePanel.getEl();
33994             w = w !== null ? w : el.getWidth();
33995             h = h !== null ? h : el.getHeight();
33996             this.panelSize = {width: w, height: h};
33997             this.activePanel.setSize(w, h);
33998         }
33999         if(Roo.isIE && this.tabs){
34000             this.tabs.el.repaint();
34001         }
34002     },
34003
34004     /**
34005      * Returns the container element for this region.
34006      * @return {Roo.Element}
34007      */
34008     getEl : function(){
34009         return this.el;
34010     },
34011
34012     /**
34013      * Hides this region.
34014      */
34015     hide : function(){
34016         //if(!this.collapsed){
34017             this.el.dom.style.left = "-2000px";
34018             this.el.hide();
34019         //}else{
34020          //   this.collapsedEl.dom.style.left = "-2000px";
34021          //   this.collapsedEl.hide();
34022        // }
34023         this.visible = false;
34024         this.fireEvent("visibilitychange", this, false);
34025     },
34026
34027     /**
34028      * Shows this region if it was previously hidden.
34029      */
34030     show : function(){
34031         //if(!this.collapsed){
34032             this.el.show();
34033         //}else{
34034         //    this.collapsedEl.show();
34035        // }
34036         this.visible = true;
34037         this.fireEvent("visibilitychange", this, true);
34038     },
34039 /*
34040     closeClicked : function(){
34041         if(this.activePanel){
34042             this.remove(this.activePanel);
34043         }
34044     },
34045
34046     collapseClick : function(e){
34047         if(this.isSlid){
34048            e.stopPropagation();
34049            this.slideIn();
34050         }else{
34051            e.stopPropagation();
34052            this.slideOut();
34053         }
34054     },
34055 */
34056     /**
34057      * Collapses this region.
34058      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
34059      */
34060     /*
34061     collapse : function(skipAnim, skipCheck = false){
34062         if(this.collapsed) {
34063             return;
34064         }
34065         
34066         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
34067             
34068             this.collapsed = true;
34069             if(this.split){
34070                 this.split.el.hide();
34071             }
34072             if(this.config.animate && skipAnim !== true){
34073                 this.fireEvent("invalidated", this);
34074                 this.animateCollapse();
34075             }else{
34076                 this.el.setLocation(-20000,-20000);
34077                 this.el.hide();
34078                 this.collapsedEl.show();
34079                 this.fireEvent("collapsed", this);
34080                 this.fireEvent("invalidated", this);
34081             }
34082         }
34083         
34084     },
34085 */
34086     animateCollapse : function(){
34087         // overridden
34088     },
34089
34090     /**
34091      * Expands this region if it was previously collapsed.
34092      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
34093      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
34094      */
34095     /*
34096     expand : function(e, skipAnim){
34097         if(e) {
34098             e.stopPropagation();
34099         }
34100         if(!this.collapsed || this.el.hasActiveFx()) {
34101             return;
34102         }
34103         if(this.isSlid){
34104             this.afterSlideIn();
34105             skipAnim = true;
34106         }
34107         this.collapsed = false;
34108         if(this.config.animate && skipAnim !== true){
34109             this.animateExpand();
34110         }else{
34111             this.el.show();
34112             if(this.split){
34113                 this.split.el.show();
34114             }
34115             this.collapsedEl.setLocation(-2000,-2000);
34116             this.collapsedEl.hide();
34117             this.fireEvent("invalidated", this);
34118             this.fireEvent("expanded", this);
34119         }
34120     },
34121 */
34122     animateExpand : function(){
34123         // overridden
34124     },
34125
34126     initTabs : function()
34127     {
34128         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
34129         
34130         var ts = new Roo.bootstrap.panel.Tabs({
34131                 el: this.bodyEl.dom,
34132                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
34133                 disableTooltips: this.config.disableTabTips,
34134                 toolbar : this.config.toolbar
34135             });
34136         
34137         if(this.config.hideTabs){
34138             ts.stripWrap.setDisplayed(false);
34139         }
34140         this.tabs = ts;
34141         ts.resizeTabs = this.config.resizeTabs === true;
34142         ts.minTabWidth = this.config.minTabWidth || 40;
34143         ts.maxTabWidth = this.config.maxTabWidth || 250;
34144         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
34145         ts.monitorResize = false;
34146         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
34147         ts.bodyEl.addClass('roo-layout-tabs-body');
34148         this.panels.each(this.initPanelAsTab, this);
34149     },
34150
34151     initPanelAsTab : function(panel){
34152         var ti = this.tabs.addTab(
34153             panel.getEl().id,
34154             panel.getTitle(),
34155             null,
34156             this.config.closeOnTab && panel.isClosable(),
34157             panel.tpl
34158         );
34159         if(panel.tabTip !== undefined){
34160             ti.setTooltip(panel.tabTip);
34161         }
34162         ti.on("activate", function(){
34163               this.setActivePanel(panel);
34164         }, this);
34165         
34166         if(this.config.closeOnTab){
34167             ti.on("beforeclose", function(t, e){
34168                 e.cancel = true;
34169                 this.remove(panel);
34170             }, this);
34171         }
34172         
34173         panel.tabItem = ti;
34174         
34175         return ti;
34176     },
34177
34178     updatePanelTitle : function(panel, title)
34179     {
34180         if(this.activePanel == panel){
34181             this.updateTitle(title);
34182         }
34183         if(this.tabs){
34184             var ti = this.tabs.getTab(panel.getEl().id);
34185             ti.setText(title);
34186             if(panel.tabTip !== undefined){
34187                 ti.setTooltip(panel.tabTip);
34188             }
34189         }
34190     },
34191
34192     updateTitle : function(title){
34193         if(this.titleTextEl && !this.config.title){
34194             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
34195         }
34196     },
34197
34198     setActivePanel : function(panel)
34199     {
34200         panel = this.getPanel(panel);
34201         if(this.activePanel && this.activePanel != panel){
34202             this.activePanel.setActiveState(false);
34203         }
34204         this.activePanel = panel;
34205         panel.setActiveState(true);
34206         if(this.panelSize){
34207             panel.setSize(this.panelSize.width, this.panelSize.height);
34208         }
34209         if(this.closeBtn){
34210             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
34211         }
34212         this.updateTitle(panel.getTitle());
34213         if(this.tabs){
34214             this.fireEvent("invalidated", this);
34215         }
34216         this.fireEvent("panelactivated", this, panel);
34217     },
34218
34219     /**
34220      * Shows the specified panel.
34221      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
34222      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
34223      */
34224     showPanel : function(panel)
34225     {
34226         panel = this.getPanel(panel);
34227         if(panel){
34228             if(this.tabs){
34229                 var tab = this.tabs.getTab(panel.getEl().id);
34230                 if(tab.isHidden()){
34231                     this.tabs.unhideTab(tab.id);
34232                 }
34233                 tab.activate();
34234             }else{
34235                 this.setActivePanel(panel);
34236             }
34237         }
34238         return panel;
34239     },
34240
34241     /**
34242      * Get the active panel for this region.
34243      * @return {Roo.ContentPanel} The active panel or null
34244      */
34245     getActivePanel : function(){
34246         return this.activePanel;
34247     },
34248
34249     validateVisibility : function(){
34250         if(this.panels.getCount() < 1){
34251             this.updateTitle("&#160;");
34252             this.closeBtn.hide();
34253             this.hide();
34254         }else{
34255             if(!this.isVisible()){
34256                 this.show();
34257             }
34258         }
34259     },
34260
34261     /**
34262      * Adds the passed ContentPanel(s) to this region.
34263      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34264      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
34265      */
34266     add : function(panel)
34267     {
34268         if(arguments.length > 1){
34269             for(var i = 0, len = arguments.length; i < len; i++) {
34270                 this.add(arguments[i]);
34271             }
34272             return null;
34273         }
34274         
34275         // if we have not been rendered yet, then we can not really do much of this..
34276         if (!this.bodyEl) {
34277             this.unrendered_panels.push(panel);
34278             return panel;
34279         }
34280         
34281         
34282         
34283         
34284         if(this.hasPanel(panel)){
34285             this.showPanel(panel);
34286             return panel;
34287         }
34288         panel.setRegion(this);
34289         this.panels.add(panel);
34290        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
34291             // sinle panel - no tab...?? would it not be better to render it with the tabs,
34292             // and hide them... ???
34293             this.bodyEl.dom.appendChild(panel.getEl().dom);
34294             if(panel.background !== true){
34295                 this.setActivePanel(panel);
34296             }
34297             this.fireEvent("paneladded", this, panel);
34298             return panel;
34299         }
34300         */
34301         if(!this.tabs){
34302             this.initTabs();
34303         }else{
34304             this.initPanelAsTab(panel);
34305         }
34306         
34307         
34308         if(panel.background !== true){
34309             this.tabs.activate(panel.getEl().id);
34310         }
34311         this.fireEvent("paneladded", this, panel);
34312         return panel;
34313     },
34314
34315     /**
34316      * Hides the tab for the specified panel.
34317      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34318      */
34319     hidePanel : function(panel){
34320         if(this.tabs && (panel = this.getPanel(panel))){
34321             this.tabs.hideTab(panel.getEl().id);
34322         }
34323     },
34324
34325     /**
34326      * Unhides the tab for a previously hidden panel.
34327      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34328      */
34329     unhidePanel : function(panel){
34330         if(this.tabs && (panel = this.getPanel(panel))){
34331             this.tabs.unhideTab(panel.getEl().id);
34332         }
34333     },
34334
34335     clearPanels : function(){
34336         while(this.panels.getCount() > 0){
34337              this.remove(this.panels.first());
34338         }
34339     },
34340
34341     /**
34342      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34343      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34344      * @param {Boolean} preservePanel Overrides the config preservePanel option
34345      * @return {Roo.ContentPanel} The panel that was removed
34346      */
34347     remove : function(panel, preservePanel)
34348     {
34349         panel = this.getPanel(panel);
34350         if(!panel){
34351             return null;
34352         }
34353         var e = {};
34354         this.fireEvent("beforeremove", this, panel, e);
34355         if(e.cancel === true){
34356             return null;
34357         }
34358         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
34359         var panelId = panel.getId();
34360         this.panels.removeKey(panelId);
34361         if(preservePanel){
34362             document.body.appendChild(panel.getEl().dom);
34363         }
34364         if(this.tabs){
34365             this.tabs.removeTab(panel.getEl().id);
34366         }else if (!preservePanel){
34367             this.bodyEl.dom.removeChild(panel.getEl().dom);
34368         }
34369         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
34370             var p = this.panels.first();
34371             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
34372             tempEl.appendChild(p.getEl().dom);
34373             this.bodyEl.update("");
34374             this.bodyEl.dom.appendChild(p.getEl().dom);
34375             tempEl = null;
34376             this.updateTitle(p.getTitle());
34377             this.tabs = null;
34378             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34379             this.setActivePanel(p);
34380         }
34381         panel.setRegion(null);
34382         if(this.activePanel == panel){
34383             this.activePanel = null;
34384         }
34385         if(this.config.autoDestroy !== false && preservePanel !== true){
34386             try{panel.destroy();}catch(e){}
34387         }
34388         this.fireEvent("panelremoved", this, panel);
34389         return panel;
34390     },
34391
34392     /**
34393      * Returns the TabPanel component used by this region
34394      * @return {Roo.TabPanel}
34395      */
34396     getTabs : function(){
34397         return this.tabs;
34398     },
34399
34400     createTool : function(parentEl, className){
34401         var btn = Roo.DomHelper.append(parentEl, {
34402             tag: "div",
34403             cls: "x-layout-tools-button",
34404             children: [ {
34405                 tag: "div",
34406                 cls: "roo-layout-tools-button-inner " + className,
34407                 html: "&#160;"
34408             }]
34409         }, true);
34410         btn.addClassOnOver("roo-layout-tools-button-over");
34411         return btn;
34412     }
34413 });/*
34414  * Based on:
34415  * Ext JS Library 1.1.1
34416  * Copyright(c) 2006-2007, Ext JS, LLC.
34417  *
34418  * Originally Released Under LGPL - original licence link has changed is not relivant.
34419  *
34420  * Fork - LGPL
34421  * <script type="text/javascript">
34422  */
34423  
34424
34425
34426 /**
34427  * @class Roo.SplitLayoutRegion
34428  * @extends Roo.LayoutRegion
34429  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
34430  */
34431 Roo.bootstrap.layout.Split = function(config){
34432     this.cursor = config.cursor;
34433     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
34434 };
34435
34436 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
34437 {
34438     splitTip : "Drag to resize.",
34439     collapsibleSplitTip : "Drag to resize. Double click to hide.",
34440     useSplitTips : false,
34441
34442     applyConfig : function(config){
34443         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
34444     },
34445     
34446     onRender : function(ctr,pos) {
34447         
34448         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
34449         if(!this.config.split){
34450             return;
34451         }
34452         if(!this.split){
34453             
34454             var splitEl = Roo.DomHelper.append(ctr.dom,  {
34455                             tag: "div",
34456                             id: this.el.id + "-split",
34457                             cls: "roo-layout-split roo-layout-split-"+this.position,
34458                             html: "&#160;"
34459             });
34460             /** The SplitBar for this region 
34461             * @type Roo.SplitBar */
34462             // does not exist yet...
34463             Roo.log([this.position, this.orientation]);
34464             
34465             this.split = new Roo.bootstrap.SplitBar({
34466                 dragElement : splitEl,
34467                 resizingElement: this.el,
34468                 orientation : this.orientation
34469             });
34470             
34471             this.split.on("moved", this.onSplitMove, this);
34472             this.split.useShim = this.config.useShim === true;
34473             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
34474             if(this.useSplitTips){
34475                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
34476             }
34477             //if(config.collapsible){
34478             //    this.split.el.on("dblclick", this.collapse,  this);
34479             //}
34480         }
34481         if(typeof this.config.minSize != "undefined"){
34482             this.split.minSize = this.config.minSize;
34483         }
34484         if(typeof this.config.maxSize != "undefined"){
34485             this.split.maxSize = this.config.maxSize;
34486         }
34487         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
34488             this.hideSplitter();
34489         }
34490         
34491     },
34492
34493     getHMaxSize : function(){
34494          var cmax = this.config.maxSize || 10000;
34495          var center = this.mgr.getRegion("center");
34496          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
34497     },
34498
34499     getVMaxSize : function(){
34500          var cmax = this.config.maxSize || 10000;
34501          var center = this.mgr.getRegion("center");
34502          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
34503     },
34504
34505     onSplitMove : function(split, newSize){
34506         this.fireEvent("resized", this, newSize);
34507     },
34508     
34509     /** 
34510      * Returns the {@link Roo.SplitBar} for this region.
34511      * @return {Roo.SplitBar}
34512      */
34513     getSplitBar : function(){
34514         return this.split;
34515     },
34516     
34517     hide : function(){
34518         this.hideSplitter();
34519         Roo.bootstrap.layout.Split.superclass.hide.call(this);
34520     },
34521
34522     hideSplitter : function(){
34523         if(this.split){
34524             this.split.el.setLocation(-2000,-2000);
34525             this.split.el.hide();
34526         }
34527     },
34528
34529     show : function(){
34530         if(this.split){
34531             this.split.el.show();
34532         }
34533         Roo.bootstrap.layout.Split.superclass.show.call(this);
34534     },
34535     
34536     beforeSlide: function(){
34537         if(Roo.isGecko){// firefox overflow auto bug workaround
34538             this.bodyEl.clip();
34539             if(this.tabs) {
34540                 this.tabs.bodyEl.clip();
34541             }
34542             if(this.activePanel){
34543                 this.activePanel.getEl().clip();
34544                 
34545                 if(this.activePanel.beforeSlide){
34546                     this.activePanel.beforeSlide();
34547                 }
34548             }
34549         }
34550     },
34551     
34552     afterSlide : function(){
34553         if(Roo.isGecko){// firefox overflow auto bug workaround
34554             this.bodyEl.unclip();
34555             if(this.tabs) {
34556                 this.tabs.bodyEl.unclip();
34557             }
34558             if(this.activePanel){
34559                 this.activePanel.getEl().unclip();
34560                 if(this.activePanel.afterSlide){
34561                     this.activePanel.afterSlide();
34562                 }
34563             }
34564         }
34565     },
34566
34567     initAutoHide : function(){
34568         if(this.autoHide !== false){
34569             if(!this.autoHideHd){
34570                 var st = new Roo.util.DelayedTask(this.slideIn, this);
34571                 this.autoHideHd = {
34572                     "mouseout": function(e){
34573                         if(!e.within(this.el, true)){
34574                             st.delay(500);
34575                         }
34576                     },
34577                     "mouseover" : function(e){
34578                         st.cancel();
34579                     },
34580                     scope : this
34581                 };
34582             }
34583             this.el.on(this.autoHideHd);
34584         }
34585     },
34586
34587     clearAutoHide : function(){
34588         if(this.autoHide !== false){
34589             this.el.un("mouseout", this.autoHideHd.mouseout);
34590             this.el.un("mouseover", this.autoHideHd.mouseover);
34591         }
34592     },
34593
34594     clearMonitor : function(){
34595         Roo.get(document).un("click", this.slideInIf, this);
34596     },
34597
34598     // these names are backwards but not changed for compat
34599     slideOut : function(){
34600         if(this.isSlid || this.el.hasActiveFx()){
34601             return;
34602         }
34603         this.isSlid = true;
34604         if(this.collapseBtn){
34605             this.collapseBtn.hide();
34606         }
34607         this.closeBtnState = this.closeBtn.getStyle('display');
34608         this.closeBtn.hide();
34609         if(this.stickBtn){
34610             this.stickBtn.show();
34611         }
34612         this.el.show();
34613         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
34614         this.beforeSlide();
34615         this.el.setStyle("z-index", 10001);
34616         this.el.slideIn(this.getSlideAnchor(), {
34617             callback: function(){
34618                 this.afterSlide();
34619                 this.initAutoHide();
34620                 Roo.get(document).on("click", this.slideInIf, this);
34621                 this.fireEvent("slideshow", this);
34622             },
34623             scope: this,
34624             block: true
34625         });
34626     },
34627
34628     afterSlideIn : function(){
34629         this.clearAutoHide();
34630         this.isSlid = false;
34631         this.clearMonitor();
34632         this.el.setStyle("z-index", "");
34633         if(this.collapseBtn){
34634             this.collapseBtn.show();
34635         }
34636         this.closeBtn.setStyle('display', this.closeBtnState);
34637         if(this.stickBtn){
34638             this.stickBtn.hide();
34639         }
34640         this.fireEvent("slidehide", this);
34641     },
34642
34643     slideIn : function(cb){
34644         if(!this.isSlid || this.el.hasActiveFx()){
34645             Roo.callback(cb);
34646             return;
34647         }
34648         this.isSlid = false;
34649         this.beforeSlide();
34650         this.el.slideOut(this.getSlideAnchor(), {
34651             callback: function(){
34652                 this.el.setLeftTop(-10000, -10000);
34653                 this.afterSlide();
34654                 this.afterSlideIn();
34655                 Roo.callback(cb);
34656             },
34657             scope: this,
34658             block: true
34659         });
34660     },
34661     
34662     slideInIf : function(e){
34663         if(!e.within(this.el)){
34664             this.slideIn();
34665         }
34666     },
34667
34668     animateCollapse : function(){
34669         this.beforeSlide();
34670         this.el.setStyle("z-index", 20000);
34671         var anchor = this.getSlideAnchor();
34672         this.el.slideOut(anchor, {
34673             callback : function(){
34674                 this.el.setStyle("z-index", "");
34675                 this.collapsedEl.slideIn(anchor, {duration:.3});
34676                 this.afterSlide();
34677                 this.el.setLocation(-10000,-10000);
34678                 this.el.hide();
34679                 this.fireEvent("collapsed", this);
34680             },
34681             scope: this,
34682             block: true
34683         });
34684     },
34685
34686     animateExpand : function(){
34687         this.beforeSlide();
34688         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34689         this.el.setStyle("z-index", 20000);
34690         this.collapsedEl.hide({
34691             duration:.1
34692         });
34693         this.el.slideIn(this.getSlideAnchor(), {
34694             callback : function(){
34695                 this.el.setStyle("z-index", "");
34696                 this.afterSlide();
34697                 if(this.split){
34698                     this.split.el.show();
34699                 }
34700                 this.fireEvent("invalidated", this);
34701                 this.fireEvent("expanded", this);
34702             },
34703             scope: this,
34704             block: true
34705         });
34706     },
34707
34708     anchors : {
34709         "west" : "left",
34710         "east" : "right",
34711         "north" : "top",
34712         "south" : "bottom"
34713     },
34714
34715     sanchors : {
34716         "west" : "l",
34717         "east" : "r",
34718         "north" : "t",
34719         "south" : "b"
34720     },
34721
34722     canchors : {
34723         "west" : "tl-tr",
34724         "east" : "tr-tl",
34725         "north" : "tl-bl",
34726         "south" : "bl-tl"
34727     },
34728
34729     getAnchor : function(){
34730         return this.anchors[this.position];
34731     },
34732
34733     getCollapseAnchor : function(){
34734         return this.canchors[this.position];
34735     },
34736
34737     getSlideAnchor : function(){
34738         return this.sanchors[this.position];
34739     },
34740
34741     getAlignAdj : function(){
34742         var cm = this.cmargins;
34743         switch(this.position){
34744             case "west":
34745                 return [0, 0];
34746             break;
34747             case "east":
34748                 return [0, 0];
34749             break;
34750             case "north":
34751                 return [0, 0];
34752             break;
34753             case "south":
34754                 return [0, 0];
34755             break;
34756         }
34757     },
34758
34759     getExpandAdj : function(){
34760         var c = this.collapsedEl, cm = this.cmargins;
34761         switch(this.position){
34762             case "west":
34763                 return [-(cm.right+c.getWidth()+cm.left), 0];
34764             break;
34765             case "east":
34766                 return [cm.right+c.getWidth()+cm.left, 0];
34767             break;
34768             case "north":
34769                 return [0, -(cm.top+cm.bottom+c.getHeight())];
34770             break;
34771             case "south":
34772                 return [0, cm.top+cm.bottom+c.getHeight()];
34773             break;
34774         }
34775     }
34776 });/*
34777  * Based on:
34778  * Ext JS Library 1.1.1
34779  * Copyright(c) 2006-2007, Ext JS, LLC.
34780  *
34781  * Originally Released Under LGPL - original licence link has changed is not relivant.
34782  *
34783  * Fork - LGPL
34784  * <script type="text/javascript">
34785  */
34786 /*
34787  * These classes are private internal classes
34788  */
34789 Roo.bootstrap.layout.Center = function(config){
34790     config.region = "center";
34791     Roo.bootstrap.layout.Region.call(this, config);
34792     this.visible = true;
34793     this.minWidth = config.minWidth || 20;
34794     this.minHeight = config.minHeight || 20;
34795 };
34796
34797 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
34798     hide : function(){
34799         // center panel can't be hidden
34800     },
34801     
34802     show : function(){
34803         // center panel can't be hidden
34804     },
34805     
34806     getMinWidth: function(){
34807         return this.minWidth;
34808     },
34809     
34810     getMinHeight: function(){
34811         return this.minHeight;
34812     }
34813 });
34814
34815
34816
34817
34818  
34819
34820
34821
34822
34823
34824 Roo.bootstrap.layout.North = function(config)
34825 {
34826     config.region = 'north';
34827     config.cursor = 'n-resize';
34828     
34829     Roo.bootstrap.layout.Split.call(this, config);
34830     
34831     
34832     if(this.split){
34833         this.split.placement = Roo.bootstrap.SplitBar.TOP;
34834         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
34835         this.split.el.addClass("roo-layout-split-v");
34836     }
34837     var size = config.initialSize || config.height;
34838     if(typeof size != "undefined"){
34839         this.el.setHeight(size);
34840     }
34841 };
34842 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
34843 {
34844     orientation: Roo.bootstrap.SplitBar.VERTICAL,
34845     
34846     
34847     
34848     getBox : function(){
34849         if(this.collapsed){
34850             return this.collapsedEl.getBox();
34851         }
34852         var box = this.el.getBox();
34853         if(this.split){
34854             box.height += this.split.el.getHeight();
34855         }
34856         return box;
34857     },
34858     
34859     updateBox : function(box){
34860         if(this.split && !this.collapsed){
34861             box.height -= this.split.el.getHeight();
34862             this.split.el.setLeft(box.x);
34863             this.split.el.setTop(box.y+box.height);
34864             this.split.el.setWidth(box.width);
34865         }
34866         if(this.collapsed){
34867             this.updateBody(box.width, null);
34868         }
34869         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34870     }
34871 });
34872
34873
34874
34875
34876
34877 Roo.bootstrap.layout.South = function(config){
34878     config.region = 'south';
34879     config.cursor = 's-resize';
34880     Roo.bootstrap.layout.Split.call(this, config);
34881     if(this.split){
34882         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
34883         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
34884         this.split.el.addClass("roo-layout-split-v");
34885     }
34886     var size = config.initialSize || config.height;
34887     if(typeof size != "undefined"){
34888         this.el.setHeight(size);
34889     }
34890 };
34891
34892 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
34893     orientation: Roo.bootstrap.SplitBar.VERTICAL,
34894     getBox : function(){
34895         if(this.collapsed){
34896             return this.collapsedEl.getBox();
34897         }
34898         var box = this.el.getBox();
34899         if(this.split){
34900             var sh = this.split.el.getHeight();
34901             box.height += sh;
34902             box.y -= sh;
34903         }
34904         return box;
34905     },
34906     
34907     updateBox : function(box){
34908         if(this.split && !this.collapsed){
34909             var sh = this.split.el.getHeight();
34910             box.height -= sh;
34911             box.y += sh;
34912             this.split.el.setLeft(box.x);
34913             this.split.el.setTop(box.y-sh);
34914             this.split.el.setWidth(box.width);
34915         }
34916         if(this.collapsed){
34917             this.updateBody(box.width, null);
34918         }
34919         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34920     }
34921 });
34922
34923 Roo.bootstrap.layout.East = function(config){
34924     config.region = "east";
34925     config.cursor = "e-resize";
34926     Roo.bootstrap.layout.Split.call(this, config);
34927     if(this.split){
34928         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
34929         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
34930         this.split.el.addClass("roo-layout-split-h");
34931     }
34932     var size = config.initialSize || config.width;
34933     if(typeof size != "undefined"){
34934         this.el.setWidth(size);
34935     }
34936 };
34937 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
34938     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
34939     getBox : function(){
34940         if(this.collapsed){
34941             return this.collapsedEl.getBox();
34942         }
34943         var box = this.el.getBox();
34944         if(this.split){
34945             var sw = this.split.el.getWidth();
34946             box.width += sw;
34947             box.x -= sw;
34948         }
34949         return box;
34950     },
34951
34952     updateBox : function(box){
34953         if(this.split && !this.collapsed){
34954             var sw = this.split.el.getWidth();
34955             box.width -= sw;
34956             this.split.el.setLeft(box.x);
34957             this.split.el.setTop(box.y);
34958             this.split.el.setHeight(box.height);
34959             box.x += sw;
34960         }
34961         if(this.collapsed){
34962             this.updateBody(null, box.height);
34963         }
34964         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34965     }
34966 });
34967
34968 Roo.bootstrap.layout.West = function(config){
34969     config.region = "west";
34970     config.cursor = "w-resize";
34971     
34972     Roo.bootstrap.layout.Split.call(this, config);
34973     if(this.split){
34974         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
34975         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
34976         this.split.el.addClass("roo-layout-split-h");
34977     }
34978     
34979 };
34980 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
34981     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
34982     
34983     onRender: function(ctr, pos)
34984     {
34985         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
34986         var size = this.config.initialSize || this.config.width;
34987         if(typeof size != "undefined"){
34988             this.el.setWidth(size);
34989         }
34990     },
34991     
34992     getBox : function(){
34993         if(this.collapsed){
34994             return this.collapsedEl.getBox();
34995         }
34996         var box = this.el.getBox();
34997         if(this.split){
34998             box.width += this.split.el.getWidth();
34999         }
35000         return box;
35001     },
35002     
35003     updateBox : function(box){
35004         if(this.split && !this.collapsed){
35005             var sw = this.split.el.getWidth();
35006             box.width -= sw;
35007             this.split.el.setLeft(box.x+box.width);
35008             this.split.el.setTop(box.y);
35009             this.split.el.setHeight(box.height);
35010         }
35011         if(this.collapsed){
35012             this.updateBody(null, box.height);
35013         }
35014         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
35015     }
35016 });
35017 Roo.namespace("Roo.bootstrap.panel");/*
35018  * Based on:
35019  * Ext JS Library 1.1.1
35020  * Copyright(c) 2006-2007, Ext JS, LLC.
35021  *
35022  * Originally Released Under LGPL - original licence link has changed is not relivant.
35023  *
35024  * Fork - LGPL
35025  * <script type="text/javascript">
35026  */
35027 /**
35028  * @class Roo.ContentPanel
35029  * @extends Roo.util.Observable
35030  * A basic ContentPanel element.
35031  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
35032  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
35033  * @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
35034  * @cfg {Boolean}   closable      True if the panel can be closed/removed
35035  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
35036  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
35037  * @cfg {Toolbar}   toolbar       A toolbar for this panel
35038  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
35039  * @cfg {String} title          The title for this panel
35040  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
35041  * @cfg {String} url            Calls {@link #setUrl} with this value
35042  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
35043  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
35044  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
35045  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
35046  * @cfg {Boolean} badges render the badges
35047
35048  * @constructor
35049  * Create a new ContentPanel.
35050  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
35051  * @param {String/Object} config A string to set only the title or a config object
35052  * @param {String} content (optional) Set the HTML content for this panel
35053  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
35054  */
35055 Roo.bootstrap.panel.Content = function( config){
35056     
35057     this.tpl = config.tpl || false;
35058     
35059     var el = config.el;
35060     var content = config.content;
35061
35062     if(config.autoCreate){ // xtype is available if this is called from factory
35063         el = Roo.id();
35064     }
35065     this.el = Roo.get(el);
35066     if(!this.el && config && config.autoCreate){
35067         if(typeof config.autoCreate == "object"){
35068             if(!config.autoCreate.id){
35069                 config.autoCreate.id = config.id||el;
35070             }
35071             this.el = Roo.DomHelper.append(document.body,
35072                         config.autoCreate, true);
35073         }else{
35074             var elcfg =  {   tag: "div",
35075                             cls: "roo-layout-inactive-content",
35076                             id: config.id||el
35077                             };
35078             if (config.html) {
35079                 elcfg.html = config.html;
35080                 
35081             }
35082                         
35083             this.el = Roo.DomHelper.append(document.body, elcfg , true);
35084         }
35085     } 
35086     this.closable = false;
35087     this.loaded = false;
35088     this.active = false;
35089    
35090       
35091     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
35092         
35093         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
35094         
35095         this.wrapEl = this.el; //this.el.wrap();
35096         var ti = [];
35097         if (config.toolbar.items) {
35098             ti = config.toolbar.items ;
35099             delete config.toolbar.items ;
35100         }
35101         
35102         var nitems = [];
35103         this.toolbar.render(this.wrapEl, 'before');
35104         for(var i =0;i < ti.length;i++) {
35105           //  Roo.log(['add child', items[i]]);
35106             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
35107         }
35108         this.toolbar.items = nitems;
35109         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
35110         delete config.toolbar;
35111         
35112     }
35113     /*
35114     // xtype created footer. - not sure if will work as we normally have to render first..
35115     if (this.footer && !this.footer.el && this.footer.xtype) {
35116         if (!this.wrapEl) {
35117             this.wrapEl = this.el.wrap();
35118         }
35119     
35120         this.footer.container = this.wrapEl.createChild();
35121          
35122         this.footer = Roo.factory(this.footer, Roo);
35123         
35124     }
35125     */
35126     
35127      if(typeof config == "string"){
35128         this.title = config;
35129     }else{
35130         Roo.apply(this, config);
35131     }
35132     
35133     if(this.resizeEl){
35134         this.resizeEl = Roo.get(this.resizeEl, true);
35135     }else{
35136         this.resizeEl = this.el;
35137     }
35138     // handle view.xtype
35139     
35140  
35141     
35142     
35143     this.addEvents({
35144         /**
35145          * @event activate
35146          * Fires when this panel is activated. 
35147          * @param {Roo.ContentPanel} this
35148          */
35149         "activate" : true,
35150         /**
35151          * @event deactivate
35152          * Fires when this panel is activated. 
35153          * @param {Roo.ContentPanel} this
35154          */
35155         "deactivate" : true,
35156
35157         /**
35158          * @event resize
35159          * Fires when this panel is resized if fitToFrame is true.
35160          * @param {Roo.ContentPanel} this
35161          * @param {Number} width The width after any component adjustments
35162          * @param {Number} height The height after any component adjustments
35163          */
35164         "resize" : true,
35165         
35166          /**
35167          * @event render
35168          * Fires when this tab is created
35169          * @param {Roo.ContentPanel} this
35170          */
35171         "render" : true
35172         
35173         
35174         
35175     });
35176     
35177
35178     
35179     
35180     if(this.autoScroll){
35181         this.resizeEl.setStyle("overflow", "auto");
35182     } else {
35183         // fix randome scrolling
35184         //this.el.on('scroll', function() {
35185         //    Roo.log('fix random scolling');
35186         //    this.scrollTo('top',0); 
35187         //});
35188     }
35189     content = content || this.content;
35190     if(content){
35191         this.setContent(content);
35192     }
35193     if(config && config.url){
35194         this.setUrl(this.url, this.params, this.loadOnce);
35195     }
35196     
35197     
35198     
35199     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
35200     
35201     if (this.view && typeof(this.view.xtype) != 'undefined') {
35202         this.view.el = this.el.appendChild(document.createElement("div"));
35203         this.view = Roo.factory(this.view); 
35204         this.view.render  &&  this.view.render(false, '');  
35205     }
35206     
35207     
35208     this.fireEvent('render', this);
35209 };
35210
35211 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
35212     
35213     tabTip : '',
35214     
35215     setRegion : function(region){
35216         this.region = region;
35217         this.setActiveClass(region && !this.background);
35218     },
35219     
35220     
35221     setActiveClass: function(state)
35222     {
35223         if(state){
35224            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
35225            this.el.setStyle('position','relative');
35226         }else{
35227            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
35228            this.el.setStyle('position', 'absolute');
35229         } 
35230     },
35231     
35232     /**
35233      * Returns the toolbar for this Panel if one was configured. 
35234      * @return {Roo.Toolbar} 
35235      */
35236     getToolbar : function(){
35237         return this.toolbar;
35238     },
35239     
35240     setActiveState : function(active)
35241     {
35242         this.active = active;
35243         this.setActiveClass(active);
35244         if(!active){
35245             this.fireEvent("deactivate", this);
35246         }else{
35247             this.fireEvent("activate", this);
35248         }
35249     },
35250     /**
35251      * Updates this panel's element
35252      * @param {String} content The new content
35253      * @param {Boolean} loadScripts (optional) true to look for and process scripts
35254     */
35255     setContent : function(content, loadScripts){
35256         this.el.update(content, loadScripts);
35257     },
35258
35259     ignoreResize : function(w, h){
35260         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
35261             return true;
35262         }else{
35263             this.lastSize = {width: w, height: h};
35264             return false;
35265         }
35266     },
35267     /**
35268      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
35269      * @return {Roo.UpdateManager} The UpdateManager
35270      */
35271     getUpdateManager : function(){
35272         return this.el.getUpdateManager();
35273     },
35274      /**
35275      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
35276      * @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:
35277 <pre><code>
35278 panel.load({
35279     url: "your-url.php",
35280     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
35281     callback: yourFunction,
35282     scope: yourObject, //(optional scope)
35283     discardUrl: false,
35284     nocache: false,
35285     text: "Loading...",
35286     timeout: 30,
35287     scripts: false
35288 });
35289 </code></pre>
35290      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
35291      * 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.
35292      * @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}
35293      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
35294      * @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.
35295      * @return {Roo.ContentPanel} this
35296      */
35297     load : function(){
35298         var um = this.el.getUpdateManager();
35299         um.update.apply(um, arguments);
35300         return this;
35301     },
35302
35303
35304     /**
35305      * 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.
35306      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
35307      * @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)
35308      * @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)
35309      * @return {Roo.UpdateManager} The UpdateManager
35310      */
35311     setUrl : function(url, params, loadOnce){
35312         if(this.refreshDelegate){
35313             this.removeListener("activate", this.refreshDelegate);
35314         }
35315         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35316         this.on("activate", this.refreshDelegate);
35317         return this.el.getUpdateManager();
35318     },
35319     
35320     _handleRefresh : function(url, params, loadOnce){
35321         if(!loadOnce || !this.loaded){
35322             var updater = this.el.getUpdateManager();
35323             updater.update(url, params, this._setLoaded.createDelegate(this));
35324         }
35325     },
35326     
35327     _setLoaded : function(){
35328         this.loaded = true;
35329     }, 
35330     
35331     /**
35332      * Returns this panel's id
35333      * @return {String} 
35334      */
35335     getId : function(){
35336         return this.el.id;
35337     },
35338     
35339     /** 
35340      * Returns this panel's element - used by regiosn to add.
35341      * @return {Roo.Element} 
35342      */
35343     getEl : function(){
35344         return this.wrapEl || this.el;
35345     },
35346     
35347    
35348     
35349     adjustForComponents : function(width, height)
35350     {
35351         //Roo.log('adjustForComponents ');
35352         if(this.resizeEl != this.el){
35353             width -= this.el.getFrameWidth('lr');
35354             height -= this.el.getFrameWidth('tb');
35355         }
35356         if(this.toolbar){
35357             var te = this.toolbar.getEl();
35358             height -= te.getHeight();
35359             te.setWidth(width);
35360         }
35361         if(this.footer){
35362             var te = this.footer.getEl();
35363             Roo.log("footer:" + te.getHeight());
35364             
35365             height -= te.getHeight();
35366             te.setWidth(width);
35367         }
35368         
35369         
35370         if(this.adjustments){
35371             width += this.adjustments[0];
35372             height += this.adjustments[1];
35373         }
35374         return {"width": width, "height": height};
35375     },
35376     
35377     setSize : function(width, height){
35378         if(this.fitToFrame && !this.ignoreResize(width, height)){
35379             if(this.fitContainer && this.resizeEl != this.el){
35380                 this.el.setSize(width, height);
35381             }
35382             var size = this.adjustForComponents(width, height);
35383             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
35384             this.fireEvent('resize', this, size.width, size.height);
35385         }
35386     },
35387     
35388     /**
35389      * Returns this panel's title
35390      * @return {String} 
35391      */
35392     getTitle : function(){
35393         return this.title;
35394     },
35395     
35396     /**
35397      * Set this panel's title
35398      * @param {String} title
35399      */
35400     setTitle : function(title){
35401         this.title = title;
35402         if(this.region){
35403             this.region.updatePanelTitle(this, title);
35404         }
35405     },
35406     
35407     /**
35408      * Returns true is this panel was configured to be closable
35409      * @return {Boolean} 
35410      */
35411     isClosable : function(){
35412         return this.closable;
35413     },
35414     
35415     beforeSlide : function(){
35416         this.el.clip();
35417         this.resizeEl.clip();
35418     },
35419     
35420     afterSlide : function(){
35421         this.el.unclip();
35422         this.resizeEl.unclip();
35423     },
35424     
35425     /**
35426      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
35427      *   Will fail silently if the {@link #setUrl} method has not been called.
35428      *   This does not activate the panel, just updates its content.
35429      */
35430     refresh : function(){
35431         if(this.refreshDelegate){
35432            this.loaded = false;
35433            this.refreshDelegate();
35434         }
35435     },
35436     
35437     /**
35438      * Destroys this panel
35439      */
35440     destroy : function(){
35441         this.el.removeAllListeners();
35442         var tempEl = document.createElement("span");
35443         tempEl.appendChild(this.el.dom);
35444         tempEl.innerHTML = "";
35445         this.el.remove();
35446         this.el = null;
35447     },
35448     
35449     /**
35450      * form - if the content panel contains a form - this is a reference to it.
35451      * @type {Roo.form.Form}
35452      */
35453     form : false,
35454     /**
35455      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
35456      *    This contains a reference to it.
35457      * @type {Roo.View}
35458      */
35459     view : false,
35460     
35461       /**
35462      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
35463      * <pre><code>
35464
35465 layout.addxtype({
35466        xtype : 'Form',
35467        items: [ .... ]
35468    }
35469 );
35470
35471 </code></pre>
35472      * @param {Object} cfg Xtype definition of item to add.
35473      */
35474     
35475     
35476     getChildContainer: function () {
35477         return this.getEl();
35478     }
35479     
35480     
35481     /*
35482         var  ret = new Roo.factory(cfg);
35483         return ret;
35484         
35485         
35486         // add form..
35487         if (cfg.xtype.match(/^Form$/)) {
35488             
35489             var el;
35490             //if (this.footer) {
35491             //    el = this.footer.container.insertSibling(false, 'before');
35492             //} else {
35493                 el = this.el.createChild();
35494             //}
35495
35496             this.form = new  Roo.form.Form(cfg);
35497             
35498             
35499             if ( this.form.allItems.length) {
35500                 this.form.render(el.dom);
35501             }
35502             return this.form;
35503         }
35504         // should only have one of theses..
35505         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
35506             // views.. should not be just added - used named prop 'view''
35507             
35508             cfg.el = this.el.appendChild(document.createElement("div"));
35509             // factory?
35510             
35511             var ret = new Roo.factory(cfg);
35512              
35513              ret.render && ret.render(false, ''); // render blank..
35514             this.view = ret;
35515             return ret;
35516         }
35517         return false;
35518     }
35519     \*/
35520 });
35521  
35522 /**
35523  * @class Roo.bootstrap.panel.Grid
35524  * @extends Roo.bootstrap.panel.Content
35525  * @constructor
35526  * Create a new GridPanel.
35527  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
35528  * @param {Object} config A the config object
35529   
35530  */
35531
35532
35533
35534 Roo.bootstrap.panel.Grid = function(config)
35535 {
35536     
35537       
35538     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
35539         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
35540
35541     config.el = this.wrapper;
35542     //this.el = this.wrapper;
35543     
35544       if (config.container) {
35545         // ctor'ed from a Border/panel.grid
35546         
35547         
35548         this.wrapper.setStyle("overflow", "hidden");
35549         this.wrapper.addClass('roo-grid-container');
35550
35551     }
35552     
35553     
35554     if(config.toolbar){
35555         var tool_el = this.wrapper.createChild();    
35556         this.toolbar = Roo.factory(config.toolbar);
35557         var ti = [];
35558         if (config.toolbar.items) {
35559             ti = config.toolbar.items ;
35560             delete config.toolbar.items ;
35561         }
35562         
35563         var nitems = [];
35564         this.toolbar.render(tool_el);
35565         for(var i =0;i < ti.length;i++) {
35566           //  Roo.log(['add child', items[i]]);
35567             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
35568         }
35569         this.toolbar.items = nitems;
35570         
35571         delete config.toolbar;
35572     }
35573     
35574     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
35575     config.grid.scrollBody = true;;
35576     config.grid.monitorWindowResize = false; // turn off autosizing
35577     config.grid.autoHeight = false;
35578     config.grid.autoWidth = false;
35579     
35580     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
35581     
35582     if (config.background) {
35583         // render grid on panel activation (if panel background)
35584         this.on('activate', function(gp) {
35585             if (!gp.grid.rendered) {
35586                 gp.grid.render(this.wrapper);
35587                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
35588             }
35589         });
35590             
35591     } else {
35592         this.grid.render(this.wrapper);
35593         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
35594
35595     }
35596     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
35597     // ??? needed ??? config.el = this.wrapper;
35598     
35599     
35600     
35601   
35602     // xtype created footer. - not sure if will work as we normally have to render first..
35603     if (this.footer && !this.footer.el && this.footer.xtype) {
35604         
35605         var ctr = this.grid.getView().getFooterPanel(true);
35606         this.footer.dataSource = this.grid.dataSource;
35607         this.footer = Roo.factory(this.footer, Roo);
35608         this.footer.render(ctr);
35609         
35610     }
35611     
35612     
35613     
35614     
35615      
35616 };
35617
35618 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
35619     getId : function(){
35620         return this.grid.id;
35621     },
35622     
35623     /**
35624      * Returns the grid for this panel
35625      * @return {Roo.bootstrap.Table} 
35626      */
35627     getGrid : function(){
35628         return this.grid;    
35629     },
35630     
35631     setSize : function(width, height){
35632         if(!this.ignoreResize(width, height)){
35633             var grid = this.grid;
35634             var size = this.adjustForComponents(width, height);
35635             var gridel = grid.getGridEl();
35636             gridel.setSize(size.width, size.height);
35637             /*
35638             var thd = grid.getGridEl().select('thead',true).first();
35639             var tbd = grid.getGridEl().select('tbody', true).first();
35640             if (tbd) {
35641                 tbd.setSize(width, height - thd.getHeight());
35642             }
35643             */
35644             grid.autoSize();
35645         }
35646     },
35647      
35648     
35649     
35650     beforeSlide : function(){
35651         this.grid.getView().scroller.clip();
35652     },
35653     
35654     afterSlide : function(){
35655         this.grid.getView().scroller.unclip();
35656     },
35657     
35658     destroy : function(){
35659         this.grid.destroy();
35660         delete this.grid;
35661         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
35662     }
35663 });
35664
35665 /**
35666  * @class Roo.bootstrap.panel.Nest
35667  * @extends Roo.bootstrap.panel.Content
35668  * @constructor
35669  * Create a new Panel, that can contain a layout.Border.
35670  * 
35671  * 
35672  * @param {Roo.BorderLayout} layout The layout for this panel
35673  * @param {String/Object} config A string to set only the title or a config object
35674  */
35675 Roo.bootstrap.panel.Nest = function(config)
35676 {
35677     // construct with only one argument..
35678     /* FIXME - implement nicer consturctors
35679     if (layout.layout) {
35680         config = layout;
35681         layout = config.layout;
35682         delete config.layout;
35683     }
35684     if (layout.xtype && !layout.getEl) {
35685         // then layout needs constructing..
35686         layout = Roo.factory(layout, Roo);
35687     }
35688     */
35689     
35690     config.el =  config.layout.getEl();
35691     
35692     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
35693     
35694     config.layout.monitorWindowResize = false; // turn off autosizing
35695     this.layout = config.layout;
35696     this.layout.getEl().addClass("roo-layout-nested-layout");
35697     
35698     
35699     
35700     
35701 };
35702
35703 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
35704
35705     setSize : function(width, height){
35706         if(!this.ignoreResize(width, height)){
35707             var size = this.adjustForComponents(width, height);
35708             var el = this.layout.getEl();
35709             if (size.height < 1) {
35710                 el.setWidth(size.width);   
35711             } else {
35712                 el.setSize(size.width, size.height);
35713             }
35714             var touch = el.dom.offsetWidth;
35715             this.layout.layout();
35716             // ie requires a double layout on the first pass
35717             if(Roo.isIE && !this.initialized){
35718                 this.initialized = true;
35719                 this.layout.layout();
35720             }
35721         }
35722     },
35723     
35724     // activate all subpanels if not currently active..
35725     
35726     setActiveState : function(active){
35727         this.active = active;
35728         this.setActiveClass(active);
35729         
35730         if(!active){
35731             this.fireEvent("deactivate", this);
35732             return;
35733         }
35734         
35735         this.fireEvent("activate", this);
35736         // not sure if this should happen before or after..
35737         if (!this.layout) {
35738             return; // should not happen..
35739         }
35740         var reg = false;
35741         for (var r in this.layout.regions) {
35742             reg = this.layout.getRegion(r);
35743             if (reg.getActivePanel()) {
35744                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
35745                 reg.setActivePanel(reg.getActivePanel());
35746                 continue;
35747             }
35748             if (!reg.panels.length) {
35749                 continue;
35750             }
35751             reg.showPanel(reg.getPanel(0));
35752         }
35753         
35754         
35755         
35756         
35757     },
35758     
35759     /**
35760      * Returns the nested BorderLayout for this panel
35761      * @return {Roo.BorderLayout} 
35762      */
35763     getLayout : function(){
35764         return this.layout;
35765     },
35766     
35767      /**
35768      * Adds a xtype elements to the layout of the nested panel
35769      * <pre><code>
35770
35771 panel.addxtype({
35772        xtype : 'ContentPanel',
35773        region: 'west',
35774        items: [ .... ]
35775    }
35776 );
35777
35778 panel.addxtype({
35779         xtype : 'NestedLayoutPanel',
35780         region: 'west',
35781         layout: {
35782            center: { },
35783            west: { }   
35784         },
35785         items : [ ... list of content panels or nested layout panels.. ]
35786    }
35787 );
35788 </code></pre>
35789      * @param {Object} cfg Xtype definition of item to add.
35790      */
35791     addxtype : function(cfg) {
35792         return this.layout.addxtype(cfg);
35793     
35794     }
35795 });        /*
35796  * Based on:
35797  * Ext JS Library 1.1.1
35798  * Copyright(c) 2006-2007, Ext JS, LLC.
35799  *
35800  * Originally Released Under LGPL - original licence link has changed is not relivant.
35801  *
35802  * Fork - LGPL
35803  * <script type="text/javascript">
35804  */
35805 /**
35806  * @class Roo.TabPanel
35807  * @extends Roo.util.Observable
35808  * A lightweight tab container.
35809  * <br><br>
35810  * Usage:
35811  * <pre><code>
35812 // basic tabs 1, built from existing content
35813 var tabs = new Roo.TabPanel("tabs1");
35814 tabs.addTab("script", "View Script");
35815 tabs.addTab("markup", "View Markup");
35816 tabs.activate("script");
35817
35818 // more advanced tabs, built from javascript
35819 var jtabs = new Roo.TabPanel("jtabs");
35820 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
35821
35822 // set up the UpdateManager
35823 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
35824 var updater = tab2.getUpdateManager();
35825 updater.setDefaultUrl("ajax1.htm");
35826 tab2.on('activate', updater.refresh, updater, true);
35827
35828 // Use setUrl for Ajax loading
35829 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
35830 tab3.setUrl("ajax2.htm", null, true);
35831
35832 // Disabled tab
35833 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
35834 tab4.disable();
35835
35836 jtabs.activate("jtabs-1");
35837  * </code></pre>
35838  * @constructor
35839  * Create a new TabPanel.
35840  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
35841  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
35842  */
35843 Roo.bootstrap.panel.Tabs = function(config){
35844     /**
35845     * The container element for this TabPanel.
35846     * @type Roo.Element
35847     */
35848     this.el = Roo.get(config.el);
35849     delete config.el;
35850     if(config){
35851         if(typeof config == "boolean"){
35852             this.tabPosition = config ? "bottom" : "top";
35853         }else{
35854             Roo.apply(this, config);
35855         }
35856     }
35857     
35858     if(this.tabPosition == "bottom"){
35859         this.bodyEl = Roo.get(this.createBody(this.el.dom));
35860         this.el.addClass("roo-tabs-bottom");
35861     }
35862     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
35863     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
35864     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
35865     if(Roo.isIE){
35866         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
35867     }
35868     if(this.tabPosition != "bottom"){
35869         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
35870          * @type Roo.Element
35871          */
35872         this.bodyEl = Roo.get(this.createBody(this.el.dom));
35873         this.el.addClass("roo-tabs-top");
35874     }
35875     this.items = [];
35876
35877     this.bodyEl.setStyle("position", "relative");
35878
35879     this.active = null;
35880     this.activateDelegate = this.activate.createDelegate(this);
35881
35882     this.addEvents({
35883         /**
35884          * @event tabchange
35885          * Fires when the active tab changes
35886          * @param {Roo.TabPanel} this
35887          * @param {Roo.TabPanelItem} activePanel The new active tab
35888          */
35889         "tabchange": true,
35890         /**
35891          * @event beforetabchange
35892          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
35893          * @param {Roo.TabPanel} this
35894          * @param {Object} e Set cancel to true on this object to cancel the tab change
35895          * @param {Roo.TabPanelItem} tab The tab being changed to
35896          */
35897         "beforetabchange" : true
35898     });
35899
35900     Roo.EventManager.onWindowResize(this.onResize, this);
35901     this.cpad = this.el.getPadding("lr");
35902     this.hiddenCount = 0;
35903
35904
35905     // toolbar on the tabbar support...
35906     if (this.toolbar) {
35907         alert("no toolbar support yet");
35908         this.toolbar  = false;
35909         /*
35910         var tcfg = this.toolbar;
35911         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
35912         this.toolbar = new Roo.Toolbar(tcfg);
35913         if (Roo.isSafari) {
35914             var tbl = tcfg.container.child('table', true);
35915             tbl.setAttribute('width', '100%');
35916         }
35917         */
35918         
35919     }
35920    
35921
35922
35923     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
35924 };
35925
35926 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
35927     /*
35928      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
35929      */
35930     tabPosition : "top",
35931     /*
35932      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
35933      */
35934     currentTabWidth : 0,
35935     /*
35936      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
35937      */
35938     minTabWidth : 40,
35939     /*
35940      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
35941      */
35942     maxTabWidth : 250,
35943     /*
35944      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
35945      */
35946     preferredTabWidth : 175,
35947     /*
35948      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
35949      */
35950     resizeTabs : false,
35951     /*
35952      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
35953      */
35954     monitorResize : true,
35955     /*
35956      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
35957      */
35958     toolbar : false,
35959
35960     /**
35961      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
35962      * @param {String} id The id of the div to use <b>or create</b>
35963      * @param {String} text The text for the tab
35964      * @param {String} content (optional) Content to put in the TabPanelItem body
35965      * @param {Boolean} closable (optional) True to create a close icon on the tab
35966      * @return {Roo.TabPanelItem} The created TabPanelItem
35967      */
35968     addTab : function(id, text, content, closable, tpl)
35969     {
35970         var item = new Roo.bootstrap.panel.TabItem({
35971             panel: this,
35972             id : id,
35973             text : text,
35974             closable : closable,
35975             tpl : tpl
35976         });
35977         this.addTabItem(item);
35978         if(content){
35979             item.setContent(content);
35980         }
35981         return item;
35982     },
35983
35984     /**
35985      * Returns the {@link Roo.TabPanelItem} with the specified id/index
35986      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
35987      * @return {Roo.TabPanelItem}
35988      */
35989     getTab : function(id){
35990         return this.items[id];
35991     },
35992
35993     /**
35994      * Hides the {@link Roo.TabPanelItem} with the specified id/index
35995      * @param {String/Number} id The id or index of the TabPanelItem to hide.
35996      */
35997     hideTab : function(id){
35998         var t = this.items[id];
35999         if(!t.isHidden()){
36000            t.setHidden(true);
36001            this.hiddenCount++;
36002            this.autoSizeTabs();
36003         }
36004     },
36005
36006     /**
36007      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
36008      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
36009      */
36010     unhideTab : function(id){
36011         var t = this.items[id];
36012         if(t.isHidden()){
36013            t.setHidden(false);
36014            this.hiddenCount--;
36015            this.autoSizeTabs();
36016         }
36017     },
36018
36019     /**
36020      * Adds an existing {@link Roo.TabPanelItem}.
36021      * @param {Roo.TabPanelItem} item The TabPanelItem to add
36022      */
36023     addTabItem : function(item){
36024         this.items[item.id] = item;
36025         this.items.push(item);
36026       //  if(this.resizeTabs){
36027     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
36028   //         this.autoSizeTabs();
36029 //        }else{
36030 //            item.autoSize();
36031        // }
36032     },
36033
36034     /**
36035      * Removes a {@link Roo.TabPanelItem}.
36036      * @param {String/Number} id The id or index of the TabPanelItem to remove.
36037      */
36038     removeTab : function(id){
36039         var items = this.items;
36040         var tab = items[id];
36041         if(!tab) { return; }
36042         var index = items.indexOf(tab);
36043         if(this.active == tab && items.length > 1){
36044             var newTab = this.getNextAvailable(index);
36045             if(newTab) {
36046                 newTab.activate();
36047             }
36048         }
36049         this.stripEl.dom.removeChild(tab.pnode.dom);
36050         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
36051             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
36052         }
36053         items.splice(index, 1);
36054         delete this.items[tab.id];
36055         tab.fireEvent("close", tab);
36056         tab.purgeListeners();
36057         this.autoSizeTabs();
36058     },
36059
36060     getNextAvailable : function(start){
36061         var items = this.items;
36062         var index = start;
36063         // look for a next tab that will slide over to
36064         // replace the one being removed
36065         while(index < items.length){
36066             var item = items[++index];
36067             if(item && !item.isHidden()){
36068                 return item;
36069             }
36070         }
36071         // if one isn't found select the previous tab (on the left)
36072         index = start;
36073         while(index >= 0){
36074             var item = items[--index];
36075             if(item && !item.isHidden()){
36076                 return item;
36077             }
36078         }
36079         return null;
36080     },
36081
36082     /**
36083      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
36084      * @param {String/Number} id The id or index of the TabPanelItem to disable.
36085      */
36086     disableTab : function(id){
36087         var tab = this.items[id];
36088         if(tab && this.active != tab){
36089             tab.disable();
36090         }
36091     },
36092
36093     /**
36094      * Enables a {@link Roo.TabPanelItem} that is disabled.
36095      * @param {String/Number} id The id or index of the TabPanelItem to enable.
36096      */
36097     enableTab : function(id){
36098         var tab = this.items[id];
36099         tab.enable();
36100     },
36101
36102     /**
36103      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
36104      * @param {String/Number} id The id or index of the TabPanelItem to activate.
36105      * @return {Roo.TabPanelItem} The TabPanelItem.
36106      */
36107     activate : function(id){
36108         var tab = this.items[id];
36109         if(!tab){
36110             return null;
36111         }
36112         if(tab == this.active || tab.disabled){
36113             return tab;
36114         }
36115         var e = {};
36116         this.fireEvent("beforetabchange", this, e, tab);
36117         if(e.cancel !== true && !tab.disabled){
36118             if(this.active){
36119                 this.active.hide();
36120             }
36121             this.active = this.items[id];
36122             this.active.show();
36123             this.fireEvent("tabchange", this, this.active);
36124         }
36125         return tab;
36126     },
36127
36128     /**
36129      * Gets the active {@link Roo.TabPanelItem}.
36130      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
36131      */
36132     getActiveTab : function(){
36133         return this.active;
36134     },
36135
36136     /**
36137      * Updates the tab body element to fit the height of the container element
36138      * for overflow scrolling
36139      * @param {Number} targetHeight (optional) Override the starting height from the elements height
36140      */
36141     syncHeight : function(targetHeight){
36142         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
36143         var bm = this.bodyEl.getMargins();
36144         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
36145         this.bodyEl.setHeight(newHeight);
36146         return newHeight;
36147     },
36148
36149     onResize : function(){
36150         if(this.monitorResize){
36151             this.autoSizeTabs();
36152         }
36153     },
36154
36155     /**
36156      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
36157      */
36158     beginUpdate : function(){
36159         this.updating = true;
36160     },
36161
36162     /**
36163      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
36164      */
36165     endUpdate : function(){
36166         this.updating = false;
36167         this.autoSizeTabs();
36168     },
36169
36170     /**
36171      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
36172      */
36173     autoSizeTabs : function(){
36174         var count = this.items.length;
36175         var vcount = count - this.hiddenCount;
36176         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
36177             return;
36178         }
36179         var w = Math.max(this.el.getWidth() - this.cpad, 10);
36180         var availWidth = Math.floor(w / vcount);
36181         var b = this.stripBody;
36182         if(b.getWidth() > w){
36183             var tabs = this.items;
36184             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
36185             if(availWidth < this.minTabWidth){
36186                 /*if(!this.sleft){    // incomplete scrolling code
36187                     this.createScrollButtons();
36188                 }
36189                 this.showScroll();
36190                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
36191             }
36192         }else{
36193             if(this.currentTabWidth < this.preferredTabWidth){
36194                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
36195             }
36196         }
36197     },
36198
36199     /**
36200      * Returns the number of tabs in this TabPanel.
36201      * @return {Number}
36202      */
36203      getCount : function(){
36204          return this.items.length;
36205      },
36206
36207     /**
36208      * Resizes all the tabs to the passed width
36209      * @param {Number} The new width
36210      */
36211     setTabWidth : function(width){
36212         this.currentTabWidth = width;
36213         for(var i = 0, len = this.items.length; i < len; i++) {
36214                 if(!this.items[i].isHidden()) {
36215                 this.items[i].setWidth(width);
36216             }
36217         }
36218     },
36219
36220     /**
36221      * Destroys this TabPanel
36222      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
36223      */
36224     destroy : function(removeEl){
36225         Roo.EventManager.removeResizeListener(this.onResize, this);
36226         for(var i = 0, len = this.items.length; i < len; i++){
36227             this.items[i].purgeListeners();
36228         }
36229         if(removeEl === true){
36230             this.el.update("");
36231             this.el.remove();
36232         }
36233     },
36234     
36235     createStrip : function(container)
36236     {
36237         var strip = document.createElement("nav");
36238         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
36239         container.appendChild(strip);
36240         return strip;
36241     },
36242     
36243     createStripList : function(strip)
36244     {
36245         // div wrapper for retard IE
36246         // returns the "tr" element.
36247         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
36248         //'<div class="x-tabs-strip-wrap">'+
36249           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
36250           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
36251         return strip.firstChild; //.firstChild.firstChild.firstChild;
36252     },
36253     createBody : function(container)
36254     {
36255         var body = document.createElement("div");
36256         Roo.id(body, "tab-body");
36257         //Roo.fly(body).addClass("x-tabs-body");
36258         Roo.fly(body).addClass("tab-content");
36259         container.appendChild(body);
36260         return body;
36261     },
36262     createItemBody :function(bodyEl, id){
36263         var body = Roo.getDom(id);
36264         if(!body){
36265             body = document.createElement("div");
36266             body.id = id;
36267         }
36268         //Roo.fly(body).addClass("x-tabs-item-body");
36269         Roo.fly(body).addClass("tab-pane");
36270          bodyEl.insertBefore(body, bodyEl.firstChild);
36271         return body;
36272     },
36273     /** @private */
36274     createStripElements :  function(stripEl, text, closable, tpl)
36275     {
36276         var td = document.createElement("li"); // was td..
36277         
36278         
36279         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
36280         
36281         
36282         stripEl.appendChild(td);
36283         /*if(closable){
36284             td.className = "x-tabs-closable";
36285             if(!this.closeTpl){
36286                 this.closeTpl = new Roo.Template(
36287                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
36288                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
36289                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
36290                 );
36291             }
36292             var el = this.closeTpl.overwrite(td, {"text": text});
36293             var close = el.getElementsByTagName("div")[0];
36294             var inner = el.getElementsByTagName("em")[0];
36295             return {"el": el, "close": close, "inner": inner};
36296         } else {
36297         */
36298         // not sure what this is..
36299 //            if(!this.tabTpl){
36300                 //this.tabTpl = new Roo.Template(
36301                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
36302                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
36303                 //);
36304 //                this.tabTpl = new Roo.Template(
36305 //                   '<a href="#">' +
36306 //                   '<span unselectable="on"' +
36307 //                            (this.disableTooltips ? '' : ' title="{text}"') +
36308 //                            ' >{text}</span></a>'
36309 //                );
36310 //                
36311 //            }
36312
36313
36314             var template = tpl || this.tabTpl || false;
36315             
36316             if(!template){
36317                 
36318                 template = new Roo.Template(
36319                    '<a href="#">' +
36320                    '<span unselectable="on"' +
36321                             (this.disableTooltips ? '' : ' title="{text}"') +
36322                             ' >{text}</span></a>'
36323                 );
36324             }
36325             
36326             switch (typeof(template)) {
36327                 case 'object' :
36328                     break;
36329                 case 'string' :
36330                     template = new Roo.Template(template);
36331                     break;
36332                 default :
36333                     break;
36334             }
36335             
36336             var el = template.overwrite(td, {"text": text});
36337             
36338             var inner = el.getElementsByTagName("span")[0];
36339             
36340             return {"el": el, "inner": inner};
36341             
36342     }
36343         
36344     
36345 });
36346
36347 /**
36348  * @class Roo.TabPanelItem
36349  * @extends Roo.util.Observable
36350  * Represents an individual item (tab plus body) in a TabPanel.
36351  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
36352  * @param {String} id The id of this TabPanelItem
36353  * @param {String} text The text for the tab of this TabPanelItem
36354  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
36355  */
36356 Roo.bootstrap.panel.TabItem = function(config){
36357     /**
36358      * The {@link Roo.TabPanel} this TabPanelItem belongs to
36359      * @type Roo.TabPanel
36360      */
36361     this.tabPanel = config.panel;
36362     /**
36363      * The id for this TabPanelItem
36364      * @type String
36365      */
36366     this.id = config.id;
36367     /** @private */
36368     this.disabled = false;
36369     /** @private */
36370     this.text = config.text;
36371     /** @private */
36372     this.loaded = false;
36373     this.closable = config.closable;
36374
36375     /**
36376      * The body element for this TabPanelItem.
36377      * @type Roo.Element
36378      */
36379     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
36380     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
36381     this.bodyEl.setStyle("display", "block");
36382     this.bodyEl.setStyle("zoom", "1");
36383     //this.hideAction();
36384
36385     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
36386     /** @private */
36387     this.el = Roo.get(els.el);
36388     this.inner = Roo.get(els.inner, true);
36389     this.textEl = Roo.get(this.el.dom.firstChild, true);
36390     this.pnode = Roo.get(els.el.parentNode, true);
36391     this.el.on("mousedown", this.onTabMouseDown, this);
36392     this.el.on("click", this.onTabClick, this);
36393     /** @private */
36394     if(config.closable){
36395         var c = Roo.get(els.close, true);
36396         c.dom.title = this.closeText;
36397         c.addClassOnOver("close-over");
36398         c.on("click", this.closeClick, this);
36399      }
36400
36401     this.addEvents({
36402          /**
36403          * @event activate
36404          * Fires when this tab becomes the active tab.
36405          * @param {Roo.TabPanel} tabPanel The parent TabPanel
36406          * @param {Roo.TabPanelItem} this
36407          */
36408         "activate": true,
36409         /**
36410          * @event beforeclose
36411          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
36412          * @param {Roo.TabPanelItem} this
36413          * @param {Object} e Set cancel to true on this object to cancel the close.
36414          */
36415         "beforeclose": true,
36416         /**
36417          * @event close
36418          * Fires when this tab is closed.
36419          * @param {Roo.TabPanelItem} this
36420          */
36421          "close": true,
36422         /**
36423          * @event deactivate
36424          * Fires when this tab is no longer the active tab.
36425          * @param {Roo.TabPanel} tabPanel The parent TabPanel
36426          * @param {Roo.TabPanelItem} this
36427          */
36428          "deactivate" : true
36429     });
36430     this.hidden = false;
36431
36432     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
36433 };
36434
36435 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
36436            {
36437     purgeListeners : function(){
36438        Roo.util.Observable.prototype.purgeListeners.call(this);
36439        this.el.removeAllListeners();
36440     },
36441     /**
36442      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
36443      */
36444     show : function(){
36445         this.pnode.addClass("active");
36446         this.showAction();
36447         if(Roo.isOpera){
36448             this.tabPanel.stripWrap.repaint();
36449         }
36450         this.fireEvent("activate", this.tabPanel, this);
36451     },
36452
36453     /**
36454      * Returns true if this tab is the active tab.
36455      * @return {Boolean}
36456      */
36457     isActive : function(){
36458         return this.tabPanel.getActiveTab() == this;
36459     },
36460
36461     /**
36462      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
36463      */
36464     hide : function(){
36465         this.pnode.removeClass("active");
36466         this.hideAction();
36467         this.fireEvent("deactivate", this.tabPanel, this);
36468     },
36469
36470     hideAction : function(){
36471         this.bodyEl.hide();
36472         this.bodyEl.setStyle("position", "absolute");
36473         this.bodyEl.setLeft("-20000px");
36474         this.bodyEl.setTop("-20000px");
36475     },
36476
36477     showAction : function(){
36478         this.bodyEl.setStyle("position", "relative");
36479         this.bodyEl.setTop("");
36480         this.bodyEl.setLeft("");
36481         this.bodyEl.show();
36482     },
36483
36484     /**
36485      * Set the tooltip for the tab.
36486      * @param {String} tooltip The tab's tooltip
36487      */
36488     setTooltip : function(text){
36489         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
36490             this.textEl.dom.qtip = text;
36491             this.textEl.dom.removeAttribute('title');
36492         }else{
36493             this.textEl.dom.title = text;
36494         }
36495     },
36496
36497     onTabClick : function(e){
36498         e.preventDefault();
36499         this.tabPanel.activate(this.id);
36500     },
36501
36502     onTabMouseDown : function(e){
36503         e.preventDefault();
36504         this.tabPanel.activate(this.id);
36505     },
36506 /*
36507     getWidth : function(){
36508         return this.inner.getWidth();
36509     },
36510
36511     setWidth : function(width){
36512         var iwidth = width - this.pnode.getPadding("lr");
36513         this.inner.setWidth(iwidth);
36514         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
36515         this.pnode.setWidth(width);
36516     },
36517 */
36518     /**
36519      * Show or hide the tab
36520      * @param {Boolean} hidden True to hide or false to show.
36521      */
36522     setHidden : function(hidden){
36523         this.hidden = hidden;
36524         this.pnode.setStyle("display", hidden ? "none" : "");
36525     },
36526
36527     /**
36528      * Returns true if this tab is "hidden"
36529      * @return {Boolean}
36530      */
36531     isHidden : function(){
36532         return this.hidden;
36533     },
36534
36535     /**
36536      * Returns the text for this tab
36537      * @return {String}
36538      */
36539     getText : function(){
36540         return this.text;
36541     },
36542     /*
36543     autoSize : function(){
36544         //this.el.beginMeasure();
36545         this.textEl.setWidth(1);
36546         /*
36547          *  #2804 [new] Tabs in Roojs
36548          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
36549          */
36550         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
36551         //this.el.endMeasure();
36552     //},
36553
36554     /**
36555      * Sets the text for the tab (Note: this also sets the tooltip text)
36556      * @param {String} text The tab's text and tooltip
36557      */
36558     setText : function(text){
36559         this.text = text;
36560         this.textEl.update(text);
36561         this.setTooltip(text);
36562         //if(!this.tabPanel.resizeTabs){
36563         //    this.autoSize();
36564         //}
36565     },
36566     /**
36567      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
36568      */
36569     activate : function(){
36570         this.tabPanel.activate(this.id);
36571     },
36572
36573     /**
36574      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
36575      */
36576     disable : function(){
36577         if(this.tabPanel.active != this){
36578             this.disabled = true;
36579             this.pnode.addClass("disabled");
36580         }
36581     },
36582
36583     /**
36584      * Enables this TabPanelItem if it was previously disabled.
36585      */
36586     enable : function(){
36587         this.disabled = false;
36588         this.pnode.removeClass("disabled");
36589     },
36590
36591     /**
36592      * Sets the content for this TabPanelItem.
36593      * @param {String} content The content
36594      * @param {Boolean} loadScripts true to look for and load scripts
36595      */
36596     setContent : function(content, loadScripts){
36597         this.bodyEl.update(content, loadScripts);
36598     },
36599
36600     /**
36601      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
36602      * @return {Roo.UpdateManager} The UpdateManager
36603      */
36604     getUpdateManager : function(){
36605         return this.bodyEl.getUpdateManager();
36606     },
36607
36608     /**
36609      * Set a URL to be used to load the content for this TabPanelItem.
36610      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
36611      * @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)
36612      * @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)
36613      * @return {Roo.UpdateManager} The UpdateManager
36614      */
36615     setUrl : function(url, params, loadOnce){
36616         if(this.refreshDelegate){
36617             this.un('activate', this.refreshDelegate);
36618         }
36619         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36620         this.on("activate", this.refreshDelegate);
36621         return this.bodyEl.getUpdateManager();
36622     },
36623
36624     /** @private */
36625     _handleRefresh : function(url, params, loadOnce){
36626         if(!loadOnce || !this.loaded){
36627             var updater = this.bodyEl.getUpdateManager();
36628             updater.update(url, params, this._setLoaded.createDelegate(this));
36629         }
36630     },
36631
36632     /**
36633      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
36634      *   Will fail silently if the setUrl method has not been called.
36635      *   This does not activate the panel, just updates its content.
36636      */
36637     refresh : function(){
36638         if(this.refreshDelegate){
36639            this.loaded = false;
36640            this.refreshDelegate();
36641         }
36642     },
36643
36644     /** @private */
36645     _setLoaded : function(){
36646         this.loaded = true;
36647     },
36648
36649     /** @private */
36650     closeClick : function(e){
36651         var o = {};
36652         e.stopEvent();
36653         this.fireEvent("beforeclose", this, o);
36654         if(o.cancel !== true){
36655             this.tabPanel.removeTab(this.id);
36656         }
36657     },
36658     /**
36659      * The text displayed in the tooltip for the close icon.
36660      * @type String
36661      */
36662     closeText : "Close this tab"
36663 });