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 (  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         this.el.select('img', true).first().dom.src =  url;
1528     }
1529     
1530     
1531    
1532 });
1533
1534  /*
1535  * - LGPL
1536  *
1537  * image
1538  * 
1539  */
1540
1541
1542 /**
1543  * @class Roo.bootstrap.Link
1544  * @extends Roo.bootstrap.Component
1545  * Bootstrap Link Class
1546  * @cfg {String} alt image alternative text
1547  * @cfg {String} href a tag href
1548  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1549  * @cfg {String} html the content of the link.
1550  * @cfg {String} anchor name for the anchor link
1551  * @cfg {String} fa - favicon
1552
1553  * @cfg {Boolean} preventDefault (true | false) default false
1554
1555  * 
1556  * @constructor
1557  * Create a new Input
1558  * @param {Object} config The config object
1559  */
1560
1561 Roo.bootstrap.Link = function(config){
1562     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1563     
1564     this.addEvents({
1565         // img events
1566         /**
1567          * @event click
1568          * The img click event for the img.
1569          * @param {Roo.EventObject} e
1570          */
1571         "click" : true
1572     });
1573 };
1574
1575 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1576     
1577     href: false,
1578     target: false,
1579     preventDefault: false,
1580     anchor : false,
1581     alt : false,
1582     fa: false,
1583
1584
1585     getAutoCreate : function()
1586     {
1587         var html = this.html || '';
1588         
1589         if (this.fa !== false) {
1590             html = '<i class="fa fa-' + this.fa + '"></i>';
1591         }
1592         var cfg = {
1593             tag: 'a'
1594         };
1595         // anchor's do not require html/href...
1596         if (this.anchor === false) {
1597             cfg.html = html;
1598             cfg.href = this.href || '#';
1599         } else {
1600             cfg.name = this.anchor;
1601             if (this.html !== false || this.fa !== false) {
1602                 cfg.html = html;
1603             }
1604             if (this.href !== false) {
1605                 cfg.href = this.href;
1606             }
1607         }
1608         
1609         if(this.alt !== false){
1610             cfg.alt = this.alt;
1611         }
1612         
1613         
1614         if(this.target !== false) {
1615             cfg.target = this.target;
1616         }
1617         
1618         return cfg;
1619     },
1620     
1621     initEvents: function() {
1622         
1623         if(!this.href || this.preventDefault){
1624             this.el.on('click', this.onClick, this);
1625         }
1626     },
1627     
1628     onClick : function(e)
1629     {
1630         if(this.preventDefault){
1631             e.preventDefault();
1632         }
1633         //Roo.log('img onclick');
1634         this.fireEvent('click', this, e);
1635     }
1636    
1637 });
1638
1639  /*
1640  * - LGPL
1641  *
1642  * header
1643  * 
1644  */
1645
1646 /**
1647  * @class Roo.bootstrap.Header
1648  * @extends Roo.bootstrap.Component
1649  * Bootstrap Header class
1650  * @cfg {String} html content of header
1651  * @cfg {Number} level (1|2|3|4|5|6) default 1
1652  * 
1653  * @constructor
1654  * Create a new Header
1655  * @param {Object} config The config object
1656  */
1657
1658
1659 Roo.bootstrap.Header  = function(config){
1660     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1661 };
1662
1663 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1664     
1665     //href : false,
1666     html : false,
1667     level : 1,
1668     
1669     
1670     
1671     getAutoCreate : function(){
1672         
1673         
1674         
1675         var cfg = {
1676             tag: 'h' + (1 *this.level),
1677             html: this.html || ''
1678         } ;
1679         
1680         return cfg;
1681     }
1682    
1683 });
1684
1685  
1686
1687  /*
1688  * Based on:
1689  * Ext JS Library 1.1.1
1690  * Copyright(c) 2006-2007, Ext JS, LLC.
1691  *
1692  * Originally Released Under LGPL - original licence link has changed is not relivant.
1693  *
1694  * Fork - LGPL
1695  * <script type="text/javascript">
1696  */
1697  
1698 /**
1699  * @class Roo.bootstrap.MenuMgr
1700  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1701  * @singleton
1702  */
1703 Roo.bootstrap.MenuMgr = function(){
1704    var menus, active, groups = {}, attached = false, lastShow = new Date();
1705
1706    // private - called when first menu is created
1707    function init(){
1708        menus = {};
1709        active = new Roo.util.MixedCollection();
1710        Roo.get(document).addKeyListener(27, function(){
1711            if(active.length > 0){
1712                hideAll();
1713            }
1714        });
1715    }
1716
1717    // private
1718    function hideAll(){
1719        if(active && active.length > 0){
1720            var c = active.clone();
1721            c.each(function(m){
1722                m.hide();
1723            });
1724        }
1725    }
1726
1727    // private
1728    function onHide(m){
1729        active.remove(m);
1730        if(active.length < 1){
1731            Roo.get(document).un("mouseup", onMouseDown);
1732             
1733            attached = false;
1734        }
1735    }
1736
1737    // private
1738    function onShow(m){
1739        var last = active.last();
1740        lastShow = new Date();
1741        active.add(m);
1742        if(!attached){
1743           Roo.get(document).on("mouseup", onMouseDown);
1744            
1745            attached = true;
1746        }
1747        if(m.parentMenu){
1748           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1749           m.parentMenu.activeChild = m;
1750        }else if(last && last.isVisible()){
1751           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1752        }
1753    }
1754
1755    // private
1756    function onBeforeHide(m){
1757        if(m.activeChild){
1758            m.activeChild.hide();
1759        }
1760        if(m.autoHideTimer){
1761            clearTimeout(m.autoHideTimer);
1762            delete m.autoHideTimer;
1763        }
1764    }
1765
1766    // private
1767    function onBeforeShow(m){
1768        var pm = m.parentMenu;
1769        if(!pm && !m.allowOtherMenus){
1770            hideAll();
1771        }else if(pm && pm.activeChild && active != m){
1772            pm.activeChild.hide();
1773        }
1774    }
1775
1776    // private this should really trigger on mouseup..
1777    function onMouseDown(e){
1778         Roo.log("on Mouse Up");
1779         
1780         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1781             Roo.log("MenuManager hideAll");
1782             hideAll();
1783             e.stopEvent();
1784         }
1785         
1786         
1787    }
1788
1789    // private
1790    function onBeforeCheck(mi, state){
1791        if(state){
1792            var g = groups[mi.group];
1793            for(var i = 0, l = g.length; i < l; i++){
1794                if(g[i] != mi){
1795                    g[i].setChecked(false);
1796                }
1797            }
1798        }
1799    }
1800
1801    return {
1802
1803        /**
1804         * Hides all menus that are currently visible
1805         */
1806        hideAll : function(){
1807             hideAll();  
1808        },
1809
1810        // private
1811        register : function(menu){
1812            if(!menus){
1813                init();
1814            }
1815            menus[menu.id] = menu;
1816            menu.on("beforehide", onBeforeHide);
1817            menu.on("hide", onHide);
1818            menu.on("beforeshow", onBeforeShow);
1819            menu.on("show", onShow);
1820            var g = menu.group;
1821            if(g && menu.events["checkchange"]){
1822                if(!groups[g]){
1823                    groups[g] = [];
1824                }
1825                groups[g].push(menu);
1826                menu.on("checkchange", onCheck);
1827            }
1828        },
1829
1830         /**
1831          * Returns a {@link Roo.menu.Menu} object
1832          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1833          * be used to generate and return a new Menu instance.
1834          */
1835        get : function(menu){
1836            if(typeof menu == "string"){ // menu id
1837                return menus[menu];
1838            }else if(menu.events){  // menu instance
1839                return menu;
1840            }
1841            /*else if(typeof menu.length == 'number'){ // array of menu items?
1842                return new Roo.bootstrap.Menu({items:menu});
1843            }else{ // otherwise, must be a config
1844                return new Roo.bootstrap.Menu(menu);
1845            }
1846            */
1847            return false;
1848        },
1849
1850        // private
1851        unregister : function(menu){
1852            delete menus[menu.id];
1853            menu.un("beforehide", onBeforeHide);
1854            menu.un("hide", onHide);
1855            menu.un("beforeshow", onBeforeShow);
1856            menu.un("show", onShow);
1857            var g = menu.group;
1858            if(g && menu.events["checkchange"]){
1859                groups[g].remove(menu);
1860                menu.un("checkchange", onCheck);
1861            }
1862        },
1863
1864        // private
1865        registerCheckable : function(menuItem){
1866            var g = menuItem.group;
1867            if(g){
1868                if(!groups[g]){
1869                    groups[g] = [];
1870                }
1871                groups[g].push(menuItem);
1872                menuItem.on("beforecheckchange", onBeforeCheck);
1873            }
1874        },
1875
1876        // private
1877        unregisterCheckable : function(menuItem){
1878            var g = menuItem.group;
1879            if(g){
1880                groups[g].remove(menuItem);
1881                menuItem.un("beforecheckchange", onBeforeCheck);
1882            }
1883        }
1884    };
1885 }();/*
1886  * - LGPL
1887  *
1888  * menu
1889  * 
1890  */
1891
1892 /**
1893  * @class Roo.bootstrap.Menu
1894  * @extends Roo.bootstrap.Component
1895  * Bootstrap Menu class - container for MenuItems
1896  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1897  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1898  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1899  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1900  * 
1901  * @constructor
1902  * Create a new Menu
1903  * @param {Object} config The config object
1904  */
1905
1906
1907 Roo.bootstrap.Menu = function(config){
1908     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1909     if (this.registerMenu && this.type != 'treeview')  {
1910         Roo.bootstrap.MenuMgr.register(this);
1911     }
1912     this.addEvents({
1913         /**
1914          * @event beforeshow
1915          * Fires before this menu is displayed
1916          * @param {Roo.menu.Menu} this
1917          */
1918         beforeshow : true,
1919         /**
1920          * @event beforehide
1921          * Fires before this menu is hidden
1922          * @param {Roo.menu.Menu} this
1923          */
1924         beforehide : true,
1925         /**
1926          * @event show
1927          * Fires after this menu is displayed
1928          * @param {Roo.menu.Menu} this
1929          */
1930         show : true,
1931         /**
1932          * @event hide
1933          * Fires after this menu is hidden
1934          * @param {Roo.menu.Menu} this
1935          */
1936         hide : true,
1937         /**
1938          * @event click
1939          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1940          * @param {Roo.menu.Menu} this
1941          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1942          * @param {Roo.EventObject} e
1943          */
1944         click : true,
1945         /**
1946          * @event mouseover
1947          * Fires when the mouse is hovering over this menu
1948          * @param {Roo.menu.Menu} this
1949          * @param {Roo.EventObject} e
1950          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1951          */
1952         mouseover : true,
1953         /**
1954          * @event mouseout
1955          * Fires when the mouse exits this menu
1956          * @param {Roo.menu.Menu} this
1957          * @param {Roo.EventObject} e
1958          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1959          */
1960         mouseout : true,
1961         /**
1962          * @event itemclick
1963          * Fires when a menu item contained in this menu is clicked
1964          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1965          * @param {Roo.EventObject} e
1966          */
1967         itemclick: true
1968     });
1969     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1970 };
1971
1972 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1973     
1974    /// html : false,
1975     //align : '',
1976     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1977     type: false,
1978     /**
1979      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1980      */
1981     registerMenu : true,
1982     
1983     menuItems :false, // stores the menu items..
1984     
1985     hidden:true,
1986         
1987     parentMenu : false,
1988     
1989     stopEvent : true,
1990     
1991     isLink : false,
1992     
1993     getChildContainer : function() {
1994         return this.el;  
1995     },
1996     
1997     getAutoCreate : function(){
1998          
1999         //if (['right'].indexOf(this.align)!==-1) {
2000         //    cfg.cn[1].cls += ' pull-right'
2001         //}
2002         
2003         
2004         var cfg = {
2005             tag : 'ul',
2006             cls : 'dropdown-menu' ,
2007             style : 'z-index:1000'
2008             
2009         };
2010         
2011         if (this.type === 'submenu') {
2012             cfg.cls = 'submenu active';
2013         }
2014         if (this.type === 'treeview') {
2015             cfg.cls = 'treeview-menu';
2016         }
2017         
2018         return cfg;
2019     },
2020     initEvents : function() {
2021         
2022        // Roo.log("ADD event");
2023        // Roo.log(this.triggerEl.dom);
2024         
2025         this.triggerEl.on('click', this.onTriggerClick, this);
2026         
2027         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2028         
2029         this.triggerEl.addClass('dropdown-toggle');
2030         
2031         if (Roo.isTouch) {
2032             this.el.on('touchstart'  , this.onTouch, this);
2033         }
2034         this.el.on('click' , this.onClick, this);
2035
2036         this.el.on("mouseover", this.onMouseOver, this);
2037         this.el.on("mouseout", this.onMouseOut, this);
2038         
2039     },
2040     
2041     findTargetItem : function(e)
2042     {
2043         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2044         if(!t){
2045             return false;
2046         }
2047         //Roo.log(t);         Roo.log(t.id);
2048         if(t && t.id){
2049             //Roo.log(this.menuitems);
2050             return this.menuitems.get(t.id);
2051             
2052             //return this.items.get(t.menuItemId);
2053         }
2054         
2055         return false;
2056     },
2057     
2058     onTouch : function(e) 
2059     {
2060         Roo.log("menu.onTouch");
2061         //e.stopEvent(); this make the user popdown broken
2062         this.onClick(e);
2063     },
2064     
2065     onClick : function(e)
2066     {
2067         Roo.log("menu.onClick");
2068         
2069         var t = this.findTargetItem(e);
2070         if(!t || t.isContainer){
2071             return;
2072         }
2073         Roo.log(e);
2074         /*
2075         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2076             if(t == this.activeItem && t.shouldDeactivate(e)){
2077                 this.activeItem.deactivate();
2078                 delete this.activeItem;
2079                 return;
2080             }
2081             if(t.canActivate){
2082                 this.setActiveItem(t, true);
2083             }
2084             return;
2085             
2086             
2087         }
2088         */
2089        
2090         Roo.log('pass click event');
2091         
2092         t.onClick(e);
2093         
2094         this.fireEvent("click", this, t, e);
2095         
2096         var _this = this;
2097         
2098         (function() { _this.hide(); }).defer(100);
2099     },
2100     
2101     onMouseOver : function(e){
2102         var t  = this.findTargetItem(e);
2103         //Roo.log(t);
2104         //if(t){
2105         //    if(t.canActivate && !t.disabled){
2106         //        this.setActiveItem(t, true);
2107         //    }
2108         //}
2109         
2110         this.fireEvent("mouseover", this, e, t);
2111     },
2112     isVisible : function(){
2113         return !this.hidden;
2114     },
2115      onMouseOut : function(e){
2116         var t  = this.findTargetItem(e);
2117         
2118         //if(t ){
2119         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2120         //        this.activeItem.deactivate();
2121         //        delete this.activeItem;
2122         //    }
2123         //}
2124         this.fireEvent("mouseout", this, e, t);
2125     },
2126     
2127     
2128     /**
2129      * Displays this menu relative to another element
2130      * @param {String/HTMLElement/Roo.Element} element The element to align to
2131      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2132      * the element (defaults to this.defaultAlign)
2133      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2134      */
2135     show : function(el, pos, parentMenu){
2136         this.parentMenu = parentMenu;
2137         if(!this.el){
2138             this.render();
2139         }
2140         this.fireEvent("beforeshow", this);
2141         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2142     },
2143      /**
2144      * Displays this menu at a specific xy position
2145      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2146      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2147      */
2148     showAt : function(xy, parentMenu, /* private: */_e){
2149         this.parentMenu = parentMenu;
2150         if(!this.el){
2151             this.render();
2152         }
2153         if(_e !== false){
2154             this.fireEvent("beforeshow", this);
2155             //xy = this.el.adjustForConstraints(xy);
2156         }
2157         
2158         //this.el.show();
2159         this.hideMenuItems();
2160         this.hidden = false;
2161         this.triggerEl.addClass('open');
2162         
2163         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2164             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2165         }
2166         
2167         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2168             this.el.setXY(xy);
2169         }
2170         
2171         this.focus();
2172         this.fireEvent("show", this);
2173     },
2174     
2175     focus : function(){
2176         return;
2177         if(!this.hidden){
2178             this.doFocus.defer(50, this);
2179         }
2180     },
2181
2182     doFocus : function(){
2183         if(!this.hidden){
2184             this.focusEl.focus();
2185         }
2186     },
2187
2188     /**
2189      * Hides this menu and optionally all parent menus
2190      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2191      */
2192     hide : function(deep)
2193     {
2194         
2195         this.hideMenuItems();
2196         if(this.el && this.isVisible()){
2197             this.fireEvent("beforehide", this);
2198             if(this.activeItem){
2199                 this.activeItem.deactivate();
2200                 this.activeItem = null;
2201             }
2202             this.triggerEl.removeClass('open');;
2203             this.hidden = true;
2204             this.fireEvent("hide", this);
2205         }
2206         if(deep === true && this.parentMenu){
2207             this.parentMenu.hide(true);
2208         }
2209     },
2210     
2211     onTriggerClick : function(e)
2212     {
2213         Roo.log('trigger click');
2214         
2215         var target = e.getTarget();
2216         
2217         Roo.log(target.nodeName.toLowerCase());
2218         
2219         if(target.nodeName.toLowerCase() === 'i'){
2220             e.preventDefault();
2221         }
2222         
2223     },
2224     
2225     onTriggerPress  : function(e)
2226     {
2227         Roo.log('trigger press');
2228         //Roo.log(e.getTarget());
2229        // Roo.log(this.triggerEl.dom);
2230        
2231         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2232         var pel = Roo.get(e.getTarget());
2233         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2234             Roo.log('is treeview or dropdown?');
2235             return;
2236         }
2237         
2238         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2239             return;
2240         }
2241         
2242         if (this.isVisible()) {
2243             Roo.log('hide');
2244             this.hide();
2245         } else {
2246             Roo.log('show');
2247             this.show(this.triggerEl, false, false);
2248         }
2249         
2250         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2251             e.stopEvent();
2252         }
2253         
2254     },
2255        
2256     
2257     hideMenuItems : function()
2258     {
2259         Roo.log("hide Menu Items");
2260         if (!this.el) { 
2261             return;
2262         }
2263         //$(backdrop).remove()
2264         this.el.select('.open',true).each(function(aa) {
2265             
2266             aa.removeClass('open');
2267           //var parent = getParent($(this))
2268           //var relatedTarget = { relatedTarget: this }
2269           
2270            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2271           //if (e.isDefaultPrevented()) return
2272            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2273         });
2274     },
2275     addxtypeChild : function (tree, cntr) {
2276         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2277           
2278         this.menuitems.add(comp);
2279         return comp;
2280
2281     },
2282     getEl : function()
2283     {
2284         Roo.log(this.el);
2285         return this.el;
2286     }
2287 });
2288
2289  
2290  /*
2291  * - LGPL
2292  *
2293  * menu item
2294  * 
2295  */
2296
2297
2298 /**
2299  * @class Roo.bootstrap.MenuItem
2300  * @extends Roo.bootstrap.Component
2301  * Bootstrap MenuItem class
2302  * @cfg {String} html the menu label
2303  * @cfg {String} href the link
2304  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2305  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2306  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2307  * @cfg {String} fa favicon to show on left of menu item.
2308  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2309  * 
2310  * 
2311  * @constructor
2312  * Create a new MenuItem
2313  * @param {Object} config The config object
2314  */
2315
2316
2317 Roo.bootstrap.MenuItem = function(config){
2318     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2319     this.addEvents({
2320         // raw events
2321         /**
2322          * @event click
2323          * The raw click event for the entire grid.
2324          * @param {Roo.bootstrap.MenuItem} this
2325          * @param {Roo.EventObject} e
2326          */
2327         "click" : true
2328     });
2329 };
2330
2331 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2332     
2333     href : false,
2334     html : false,
2335     preventDefault: false,
2336     isContainer : false,
2337     active : false,
2338     fa: false,
2339     
2340     getAutoCreate : function(){
2341         
2342         if(this.isContainer){
2343             return {
2344                 tag: 'li',
2345                 cls: 'dropdown-menu-item'
2346             };
2347         }
2348         var ctag = {
2349             tag: 'span',
2350             html: 'Link'
2351         };
2352         
2353         var anc = {
2354             tag : 'a',
2355             href : '#',
2356             cn : [  ]
2357         };
2358         
2359         if (this.fa !== false) {
2360             anc.cn.push({
2361                 tag : 'i',
2362                 cls : 'fa fa-' + this.fa
2363             });
2364         }
2365         
2366         anc.cn.push(ctag);
2367         
2368         
2369         var cfg= {
2370             tag: 'li',
2371             cls: 'dropdown-menu-item',
2372             cn: [ anc ]
2373         };
2374         if (this.parent().type == 'treeview') {
2375             cfg.cls = 'treeview-menu';
2376         }
2377         if (this.active) {
2378             cfg.cls += ' active';
2379         }
2380         
2381         
2382         
2383         anc.href = this.href || cfg.cn[0].href ;
2384         ctag.html = this.html || cfg.cn[0].html ;
2385         return cfg;
2386     },
2387     
2388     initEvents: function()
2389     {
2390         if (this.parent().type == 'treeview') {
2391             this.el.select('a').on('click', this.onClick, this);
2392         }
2393         if (this.menu) {
2394             this.menu.parentType = this.xtype;
2395             this.menu.triggerEl = this.el;
2396             this.menu = this.addxtype(Roo.apply({}, this.menu));
2397         }
2398         
2399     },
2400     onClick : function(e)
2401     {
2402         Roo.log('item on click ');
2403         
2404         if(this.preventDefault){
2405             e.preventDefault();
2406         }
2407         //this.parent().hideMenuItems();
2408         
2409         this.fireEvent('click', this, e);
2410     },
2411     getEl : function()
2412     {
2413         return this.el;
2414     } 
2415 });
2416
2417  
2418
2419  /*
2420  * - LGPL
2421  *
2422  * menu separator
2423  * 
2424  */
2425
2426
2427 /**
2428  * @class Roo.bootstrap.MenuSeparator
2429  * @extends Roo.bootstrap.Component
2430  * Bootstrap MenuSeparator class
2431  * 
2432  * @constructor
2433  * Create a new MenuItem
2434  * @param {Object} config The config object
2435  */
2436
2437
2438 Roo.bootstrap.MenuSeparator = function(config){
2439     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2440 };
2441
2442 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2443     
2444     getAutoCreate : function(){
2445         var cfg = {
2446             cls: 'divider',
2447             tag : 'li'
2448         };
2449         
2450         return cfg;
2451     }
2452    
2453 });
2454
2455  
2456
2457  
2458 /*
2459 * Licence: LGPL
2460 */
2461
2462 /**
2463  * @class Roo.bootstrap.Modal
2464  * @extends Roo.bootstrap.Component
2465  * Bootstrap Modal class
2466  * @cfg {String} title Title of dialog
2467  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2468  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2469  * @cfg {Boolean} specificTitle default false
2470  * @cfg {Array} buttons Array of buttons or standard button set..
2471  * @cfg {String} buttonPosition (left|right|center) default right
2472  * @cfg {Boolean} animate default true
2473  * @cfg {Boolean} allow_close default true
2474  * @cfg {Boolean} fitwindow default false
2475  * @cfg {String} size (sm|lg) default empty
2476  *
2477  *
2478  * @constructor
2479  * Create a new Modal Dialog
2480  * @param {Object} config The config object
2481  */
2482
2483 Roo.bootstrap.Modal = function(config){
2484     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2485     this.addEvents({
2486         // raw events
2487         /**
2488          * @event btnclick
2489          * The raw btnclick event for the button
2490          * @param {Roo.EventObject} e
2491          */
2492         "btnclick" : true
2493     });
2494     this.buttons = this.buttons || [];
2495
2496     if (this.tmpl) {
2497         this.tmpl = Roo.factory(this.tmpl);
2498     }
2499
2500 };
2501
2502 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2503
2504     title : 'test dialog',
2505
2506     buttons : false,
2507
2508     // set on load...
2509
2510     html: false,
2511
2512     tmp: false,
2513
2514     specificTitle: false,
2515
2516     buttonPosition: 'right',
2517
2518     allow_close : true,
2519
2520     animate : true,
2521
2522     fitwindow: false,
2523
2524
2525      // private
2526     dialogEl: false,
2527     bodyEl:  false,
2528     footerEl:  false,
2529     titleEl:  false,
2530     closeEl:  false,
2531
2532     size: '',
2533
2534
2535     onRender : function(ct, position)
2536     {
2537         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2538
2539         if(!this.el){
2540             var cfg = Roo.apply({},  this.getAutoCreate());
2541             cfg.id = Roo.id();
2542             //if(!cfg.name){
2543             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2544             //}
2545             //if (!cfg.name.length) {
2546             //    delete cfg.name;
2547            // }
2548             if (this.cls) {
2549                 cfg.cls += ' ' + this.cls;
2550             }
2551             if (this.style) {
2552                 cfg.style = this.style;
2553             }
2554             this.el = Roo.get(document.body).createChild(cfg, position);
2555         }
2556         //var type = this.el.dom.type;
2557
2558
2559         if(this.tabIndex !== undefined){
2560             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2561         }
2562
2563         this.dialogEl = this.el.select('.modal-dialog',true).first();
2564         this.bodyEl = this.el.select('.modal-body',true).first();
2565         this.closeEl = this.el.select('.modal-header .close', true).first();
2566         this.footerEl = this.el.select('.modal-footer',true).first();
2567         this.titleEl = this.el.select('.modal-title',true).first();
2568
2569
2570
2571         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2572         this.maskEl.enableDisplayMode("block");
2573         this.maskEl.hide();
2574         //this.el.addClass("x-dlg-modal");
2575
2576         if (this.buttons.length) {
2577             Roo.each(this.buttons, function(bb) {
2578                 var b = Roo.apply({}, bb);
2579                 b.xns = b.xns || Roo.bootstrap;
2580                 b.xtype = b.xtype || 'Button';
2581                 if (typeof(b.listeners) == 'undefined') {
2582                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2583                 }
2584
2585                 var btn = Roo.factory(b);
2586
2587                 btn.render(this.el.select('.modal-footer div').first());
2588
2589             },this);
2590         }
2591         // render the children.
2592         var nitems = [];
2593
2594         if(typeof(this.items) != 'undefined'){
2595             var items = this.items;
2596             delete this.items;
2597
2598             for(var i =0;i < items.length;i++) {
2599                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2600             }
2601         }
2602
2603         this.items = nitems;
2604
2605         // where are these used - they used to be body/close/footer
2606
2607
2608         this.initEvents();
2609         //this.el.addClass([this.fieldClass, this.cls]);
2610
2611     },
2612
2613     getAutoCreate : function(){
2614
2615
2616         var bdy = {
2617                 cls : 'modal-body',
2618                 html : this.html || ''
2619         };
2620
2621         var title = {
2622             tag: 'h4',
2623             cls : 'modal-title',
2624             html : this.title
2625         };
2626
2627         if(this.specificTitle){
2628             title = this.title;
2629
2630         };
2631
2632         var header = [];
2633         if (this.allow_close) {
2634             header.push({
2635                 tag: 'button',
2636                 cls : 'close',
2637                 html : '&times'
2638             });
2639         }
2640
2641         header.push(title);
2642
2643         var size = '';
2644
2645         if(this.size.length){
2646             size = 'modal-' + this.size;
2647         }
2648
2649         var modal = {
2650             cls: "modal",
2651             style : 'display: none',
2652             cn : [
2653                 {
2654                     cls: "modal-dialog " + size,
2655                     cn : [
2656                         {
2657                             cls : "modal-content",
2658                             cn : [
2659                                 {
2660                                     cls : 'modal-header',
2661                                     cn : header
2662                                 },
2663                                 bdy,
2664                                 {
2665                                     cls : 'modal-footer',
2666                                     cn : [
2667                                         {
2668                                             tag: 'div',
2669                                             cls: 'btn-' + this.buttonPosition
2670                                         }
2671                                     ]
2672
2673                                 }
2674
2675
2676                             ]
2677
2678                         }
2679                     ]
2680
2681                 }
2682             ]
2683         };
2684
2685         if(this.animate){
2686             modal.cls += ' fade';
2687         }
2688
2689         return modal;
2690
2691     },
2692     getChildContainer : function() {
2693
2694          return this.bodyEl;
2695
2696     },
2697     getButtonContainer : function() {
2698          return this.el.select('.modal-footer div',true).first();
2699
2700     },
2701     initEvents : function()
2702     {
2703         if (this.allow_close) {
2704             this.closeEl.on('click', this.hide, this);
2705         }
2706         Roo.EventManager.onWindowResize(this.resize, this, true);
2707
2708
2709     },
2710
2711     resize : function()
2712     {
2713         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2714         if (this.fitwindow) {
2715             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2716             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2717             this.setSize(w,h);
2718         }
2719     },
2720
2721     setSize : function(w,h)
2722     {
2723         if (!w && !h) {
2724             return;
2725         }
2726         this.resizeTo(w,h);
2727     },
2728
2729     show : function() {
2730
2731         if (!this.rendered) {
2732             this.render();
2733         }
2734
2735         this.el.setStyle('display', 'block');
2736
2737         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2738             var _this = this;
2739             (function(){
2740                 this.el.addClass('in');
2741             }).defer(50, this);
2742         }else{
2743             this.el.addClass('in');
2744
2745         }
2746
2747         // not sure how we can show data in here..
2748         //if (this.tmpl) {
2749         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2750         //}
2751
2752         Roo.get(document.body).addClass("x-body-masked");
2753         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2754         this.maskEl.show();
2755         this.el.setStyle('zIndex', '10001');
2756
2757         this.fireEvent('show', this);
2758
2759         this.resize();
2760
2761         (function () {
2762             this.items.forEach( function(e) {
2763                 e.layout ? e.layout() : false;
2764
2765             });
2766         }).defer(100,this);
2767
2768     },
2769     hide : function()
2770     {
2771         if(this.fireEvent("beforehide", this) !== false){
2772             this.maskEl.hide();
2773             Roo.get(document.body).removeClass("x-body-masked");
2774             this.el.removeClass('in');
2775             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2776
2777             if(this.animate){ // why
2778                 var _this = this;
2779                 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2780             }else{
2781                 this.el.setStyle('display', 'none');
2782             }
2783             this.fireEvent('hide', this);
2784         }
2785     },
2786
2787     addButton : function(str, cb)
2788     {
2789
2790
2791         var b = Roo.apply({}, { html : str } );
2792         b.xns = b.xns || Roo.bootstrap;
2793         b.xtype = b.xtype || 'Button';
2794         if (typeof(b.listeners) == 'undefined') {
2795             b.listeners = { click : cb.createDelegate(this)  };
2796         }
2797
2798         var btn = Roo.factory(b);
2799
2800         btn.render(this.el.select('.modal-footer div').first());
2801
2802         return btn;
2803
2804     },
2805
2806     setDefaultButton : function(btn)
2807     {
2808         //this.el.select('.modal-footer').()
2809     },
2810     diff : false,
2811
2812     resizeTo: function(w,h)
2813     {
2814         // skip.. ?? why??
2815
2816         this.dialogEl.setWidth(w);
2817         if (this.diff === false) {
2818             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2819         }
2820
2821         this.bodyEl.setHeight(h-this.diff);
2822
2823
2824     },
2825     setContentSize  : function(w, h)
2826     {
2827
2828     },
2829     onButtonClick: function(btn,e)
2830     {
2831         //Roo.log([a,b,c]);
2832         this.fireEvent('btnclick', btn.name, e);
2833     },
2834      /**
2835      * Set the title of the Dialog
2836      * @param {String} str new Title
2837      */
2838     setTitle: function(str) {
2839         this.titleEl.dom.innerHTML = str;
2840     },
2841     /**
2842      * Set the body of the Dialog
2843      * @param {String} str new Title
2844      */
2845     setBody: function(str) {
2846         this.bodyEl.dom.innerHTML = str;
2847     },
2848     /**
2849      * Set the body of the Dialog using the template
2850      * @param {Obj} data - apply this data to the template and replace the body contents.
2851      */
2852     applyBody: function(obj)
2853     {
2854         if (!this.tmpl) {
2855             Roo.log("Error - using apply Body without a template");
2856             //code
2857         }
2858         this.tmpl.overwrite(this.bodyEl, obj);
2859     }
2860
2861 });
2862
2863
2864 Roo.apply(Roo.bootstrap.Modal,  {
2865     /**
2866          * Button config that displays a single OK button
2867          * @type Object
2868          */
2869         OK :  [{
2870             name : 'ok',
2871             weight : 'primary',
2872             html : 'OK'
2873         }],
2874         /**
2875          * Button config that displays Yes and No buttons
2876          * @type Object
2877          */
2878         YESNO : [
2879             {
2880                 name  : 'no',
2881                 html : 'No'
2882             },
2883             {
2884                 name  :'yes',
2885                 weight : 'primary',
2886                 html : 'Yes'
2887             }
2888         ],
2889
2890         /**
2891          * Button config that displays OK and Cancel buttons
2892          * @type Object
2893          */
2894         OKCANCEL : [
2895             {
2896                name : 'cancel',
2897                 html : 'Cancel'
2898             },
2899             {
2900                 name : 'ok',
2901                 weight : 'primary',
2902                 html : 'OK'
2903             }
2904         ],
2905         /**
2906          * Button config that displays Yes, No and Cancel buttons
2907          * @type Object
2908          */
2909         YESNOCANCEL : [
2910             {
2911                 name : 'yes',
2912                 weight : 'primary',
2913                 html : 'Yes'
2914             },
2915             {
2916                 name : 'no',
2917                 html : 'No'
2918             },
2919             {
2920                 name : 'cancel',
2921                 html : 'Cancel'
2922             }
2923         ]
2924 });
2925 /*
2926  * - LGPL
2927  *
2928  * messagebox - can be used as a replace
2929  * 
2930  */
2931 /**
2932  * @class Roo.MessageBox
2933  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2934  * Example usage:
2935  *<pre><code>
2936 // Basic alert:
2937 Roo.Msg.alert('Status', 'Changes saved successfully.');
2938
2939 // Prompt for user data:
2940 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2941     if (btn == 'ok'){
2942         // process text value...
2943     }
2944 });
2945
2946 // Show a dialog using config options:
2947 Roo.Msg.show({
2948    title:'Save Changes?',
2949    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2950    buttons: Roo.Msg.YESNOCANCEL,
2951    fn: processResult,
2952    animEl: 'elId'
2953 });
2954 </code></pre>
2955  * @singleton
2956  */
2957 Roo.bootstrap.MessageBox = function(){
2958     var dlg, opt, mask, waitTimer;
2959     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2960     var buttons, activeTextEl, bwidth;
2961
2962     
2963     // private
2964     var handleButton = function(button){
2965         dlg.hide();
2966         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2967     };
2968
2969     // private
2970     var handleHide = function(){
2971         if(opt && opt.cls){
2972             dlg.el.removeClass(opt.cls);
2973         }
2974         //if(waitTimer){
2975         //    Roo.TaskMgr.stop(waitTimer);
2976         //    waitTimer = null;
2977         //}
2978     };
2979
2980     // private
2981     var updateButtons = function(b){
2982         var width = 0;
2983         if(!b){
2984             buttons["ok"].hide();
2985             buttons["cancel"].hide();
2986             buttons["yes"].hide();
2987             buttons["no"].hide();
2988             //dlg.footer.dom.style.display = 'none';
2989             return width;
2990         }
2991         dlg.footerEl.dom.style.display = '';
2992         for(var k in buttons){
2993             if(typeof buttons[k] != "function"){
2994                 if(b[k]){
2995                     buttons[k].show();
2996                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2997                     width += buttons[k].el.getWidth()+15;
2998                 }else{
2999                     buttons[k].hide();
3000                 }
3001             }
3002         }
3003         return width;
3004     };
3005
3006     // private
3007     var handleEsc = function(d, k, e){
3008         if(opt && opt.closable !== false){
3009             dlg.hide();
3010         }
3011         if(e){
3012             e.stopEvent();
3013         }
3014     };
3015
3016     return {
3017         /**
3018          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3019          * @return {Roo.BasicDialog} The BasicDialog element
3020          */
3021         getDialog : function(){
3022            if(!dlg){
3023                 dlg = new Roo.bootstrap.Modal( {
3024                     //draggable: true,
3025                     //resizable:false,
3026                     //constraintoviewport:false,
3027                     //fixedcenter:true,
3028                     //collapsible : false,
3029                     //shim:true,
3030                     //modal: true,
3031                   //  width:400,
3032                   //  height:100,
3033                     //buttonAlign:"center",
3034                     closeClick : function(){
3035                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3036                             handleButton("no");
3037                         }else{
3038                             handleButton("cancel");
3039                         }
3040                     }
3041                 });
3042                 dlg.render();
3043                 dlg.on("hide", handleHide);
3044                 mask = dlg.mask;
3045                 //dlg.addKeyListener(27, handleEsc);
3046                 buttons = {};
3047                 this.buttons = buttons;
3048                 var bt = this.buttonText;
3049                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3050                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3051                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3052                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3053                 //Roo.log(buttons);
3054                 bodyEl = dlg.bodyEl.createChild({
3055
3056                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3057                         '<textarea class="roo-mb-textarea"></textarea>' +
3058                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3059                 });
3060                 msgEl = bodyEl.dom.firstChild;
3061                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3062                 textboxEl.enableDisplayMode();
3063                 textboxEl.addKeyListener([10,13], function(){
3064                     if(dlg.isVisible() && opt && opt.buttons){
3065                         if(opt.buttons.ok){
3066                             handleButton("ok");
3067                         }else if(opt.buttons.yes){
3068                             handleButton("yes");
3069                         }
3070                     }
3071                 });
3072                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3073                 textareaEl.enableDisplayMode();
3074                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3075                 progressEl.enableDisplayMode();
3076                 var pf = progressEl.dom.firstChild;
3077                 if (pf) {
3078                     pp = Roo.get(pf.firstChild);
3079                     pp.setHeight(pf.offsetHeight);
3080                 }
3081                 
3082             }
3083             return dlg;
3084         },
3085
3086         /**
3087          * Updates the message box body text
3088          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3089          * the XHTML-compliant non-breaking space character '&amp;#160;')
3090          * @return {Roo.MessageBox} This message box
3091          */
3092         updateText : function(text){
3093             if(!dlg.isVisible() && !opt.width){
3094                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
3095             }
3096             msgEl.innerHTML = text || '&#160;';
3097       
3098             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3099             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3100             var w = Math.max(
3101                     Math.min(opt.width || cw , this.maxWidth), 
3102                     Math.max(opt.minWidth || this.minWidth, bwidth)
3103             );
3104             if(opt.prompt){
3105                 activeTextEl.setWidth(w);
3106             }
3107             if(dlg.isVisible()){
3108                 dlg.fixedcenter = false;
3109             }
3110             // to big, make it scroll. = But as usual stupid IE does not support
3111             // !important..
3112             
3113             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3114                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3115                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3116             } else {
3117                 bodyEl.dom.style.height = '';
3118                 bodyEl.dom.style.overflowY = '';
3119             }
3120             if (cw > w) {
3121                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3122             } else {
3123                 bodyEl.dom.style.overflowX = '';
3124             }
3125             
3126             dlg.setContentSize(w, bodyEl.getHeight());
3127             if(dlg.isVisible()){
3128                 dlg.fixedcenter = true;
3129             }
3130             return this;
3131         },
3132
3133         /**
3134          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3135          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3136          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3137          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3138          * @return {Roo.MessageBox} This message box
3139          */
3140         updateProgress : function(value, text){
3141             if(text){
3142                 this.updateText(text);
3143             }
3144             if (pp) { // weird bug on my firefox - for some reason this is not defined
3145                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3146             }
3147             return this;
3148         },        
3149
3150         /**
3151          * Returns true if the message box is currently displayed
3152          * @return {Boolean} True if the message box is visible, else false
3153          */
3154         isVisible : function(){
3155             return dlg && dlg.isVisible();  
3156         },
3157
3158         /**
3159          * Hides the message box if it is displayed
3160          */
3161         hide : function(){
3162             if(this.isVisible()){
3163                 dlg.hide();
3164             }  
3165         },
3166
3167         /**
3168          * Displays a new message box, or reinitializes an existing message box, based on the config options
3169          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3170          * The following config object properties are supported:
3171          * <pre>
3172 Property    Type             Description
3173 ----------  ---------------  ------------------------------------------------------------------------------------
3174 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3175                                    closes (defaults to undefined)
3176 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3177                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3178 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3179                                    progress and wait dialogs will ignore this property and always hide the
3180                                    close button as they can only be closed programmatically.
3181 cls               String           A custom CSS class to apply to the message box element
3182 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3183                                    displayed (defaults to 75)
3184 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3185                                    function will be btn (the name of the button that was clicked, if applicable,
3186                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3187                                    Progress and wait dialogs will ignore this option since they do not respond to
3188                                    user actions and can only be closed programmatically, so any required function
3189                                    should be called by the same code after it closes the dialog.
3190 icon              String           A CSS class that provides a background image to be used as an icon for
3191                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3192 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3193 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3194 modal             Boolean          False to allow user interaction with the page while the message box is
3195                                    displayed (defaults to true)
3196 msg               String           A string that will replace the existing message box body text (defaults
3197                                    to the XHTML-compliant non-breaking space character '&#160;')
3198 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3199 progress          Boolean          True to display a progress bar (defaults to false)
3200 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3201 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3202 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3203 title             String           The title text
3204 value             String           The string value to set into the active textbox element if displayed
3205 wait              Boolean          True to display a progress bar (defaults to false)
3206 width             Number           The width of the dialog in pixels
3207 </pre>
3208          *
3209          * Example usage:
3210          * <pre><code>
3211 Roo.Msg.show({
3212    title: 'Address',
3213    msg: 'Please enter your address:',
3214    width: 300,
3215    buttons: Roo.MessageBox.OKCANCEL,
3216    multiline: true,
3217    fn: saveAddress,
3218    animEl: 'addAddressBtn'
3219 });
3220 </code></pre>
3221          * @param {Object} config Configuration options
3222          * @return {Roo.MessageBox} This message box
3223          */
3224         show : function(options)
3225         {
3226             
3227             // this causes nightmares if you show one dialog after another
3228             // especially on callbacks..
3229              
3230             if(this.isVisible()){
3231                 
3232                 this.hide();
3233                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3234                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3235                 Roo.log("New Dialog Message:" +  options.msg )
3236                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3237                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3238                 
3239             }
3240             var d = this.getDialog();
3241             opt = options;
3242             d.setTitle(opt.title || "&#160;");
3243             d.closeEl.setDisplayed(opt.closable !== false);
3244             activeTextEl = textboxEl;
3245             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3246             if(opt.prompt){
3247                 if(opt.multiline){
3248                     textboxEl.hide();
3249                     textareaEl.show();
3250                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3251                         opt.multiline : this.defaultTextHeight);
3252                     activeTextEl = textareaEl;
3253                 }else{
3254                     textboxEl.show();
3255                     textareaEl.hide();
3256                 }
3257             }else{
3258                 textboxEl.hide();
3259                 textareaEl.hide();
3260             }
3261             progressEl.setDisplayed(opt.progress === true);
3262             this.updateProgress(0);
3263             activeTextEl.dom.value = opt.value || "";
3264             if(opt.prompt){
3265                 dlg.setDefaultButton(activeTextEl);
3266             }else{
3267                 var bs = opt.buttons;
3268                 var db = null;
3269                 if(bs && bs.ok){
3270                     db = buttons["ok"];
3271                 }else if(bs && bs.yes){
3272                     db = buttons["yes"];
3273                 }
3274                 dlg.setDefaultButton(db);
3275             }
3276             bwidth = updateButtons(opt.buttons);
3277             this.updateText(opt.msg);
3278             if(opt.cls){
3279                 d.el.addClass(opt.cls);
3280             }
3281             d.proxyDrag = opt.proxyDrag === true;
3282             d.modal = opt.modal !== false;
3283             d.mask = opt.modal !== false ? mask : false;
3284             if(!d.isVisible()){
3285                 // force it to the end of the z-index stack so it gets a cursor in FF
3286                 document.body.appendChild(dlg.el.dom);
3287                 d.animateTarget = null;
3288                 d.show(options.animEl);
3289             }
3290             return this;
3291         },
3292
3293         /**
3294          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3295          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3296          * and closing the message box when the process is complete.
3297          * @param {String} title The title bar text
3298          * @param {String} msg The message box body text
3299          * @return {Roo.MessageBox} This message box
3300          */
3301         progress : function(title, msg){
3302             this.show({
3303                 title : title,
3304                 msg : msg,
3305                 buttons: false,
3306                 progress:true,
3307                 closable:false,
3308                 minWidth: this.minProgressWidth,
3309                 modal : true
3310             });
3311             return this;
3312         },
3313
3314         /**
3315          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3316          * If a callback function is passed it will be called after the user clicks the button, and the
3317          * id of the button that was clicked will be passed as the only parameter to the callback
3318          * (could also be the top-right close button).
3319          * @param {String} title The title bar text
3320          * @param {String} msg The message box body text
3321          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3322          * @param {Object} scope (optional) The scope of the callback function
3323          * @return {Roo.MessageBox} This message box
3324          */
3325         alert : function(title, msg, fn, scope){
3326             this.show({
3327                 title : title,
3328                 msg : msg,
3329                 buttons: this.OK,
3330                 fn: fn,
3331                 scope : scope,
3332                 modal : true
3333             });
3334             return this;
3335         },
3336
3337         /**
3338          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3339          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3340          * You are responsible for closing the message box when the process is complete.
3341          * @param {String} msg The message box body text
3342          * @param {String} title (optional) The title bar text
3343          * @return {Roo.MessageBox} This message box
3344          */
3345         wait : function(msg, title){
3346             this.show({
3347                 title : title,
3348                 msg : msg,
3349                 buttons: false,
3350                 closable:false,
3351                 progress:true,
3352                 modal:true,
3353                 width:300,
3354                 wait:true
3355             });
3356             waitTimer = Roo.TaskMgr.start({
3357                 run: function(i){
3358                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3359                 },
3360                 interval: 1000
3361             });
3362             return this;
3363         },
3364
3365         /**
3366          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3367          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3368          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3369          * @param {String} title The title bar text
3370          * @param {String} msg The message box body text
3371          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3372          * @param {Object} scope (optional) The scope of the callback function
3373          * @return {Roo.MessageBox} This message box
3374          */
3375         confirm : function(title, msg, fn, scope){
3376             this.show({
3377                 title : title,
3378                 msg : msg,
3379                 buttons: this.YESNO,
3380                 fn: fn,
3381                 scope : scope,
3382                 modal : true
3383             });
3384             return this;
3385         },
3386
3387         /**
3388          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3389          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3390          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3391          * (could also be the top-right close button) and the text that was entered will be passed as the two
3392          * parameters to the callback.
3393          * @param {String} title The title bar text
3394          * @param {String} msg The message box body text
3395          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3396          * @param {Object} scope (optional) The scope of the callback function
3397          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3398          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3399          * @return {Roo.MessageBox} This message box
3400          */
3401         prompt : function(title, msg, fn, scope, multiline){
3402             this.show({
3403                 title : title,
3404                 msg : msg,
3405                 buttons: this.OKCANCEL,
3406                 fn: fn,
3407                 minWidth:250,
3408                 scope : scope,
3409                 prompt:true,
3410                 multiline: multiline,
3411                 modal : true
3412             });
3413             return this;
3414         },
3415
3416         /**
3417          * Button config that displays a single OK button
3418          * @type Object
3419          */
3420         OK : {ok:true},
3421         /**
3422          * Button config that displays Yes and No buttons
3423          * @type Object
3424          */
3425         YESNO : {yes:true, no:true},
3426         /**
3427          * Button config that displays OK and Cancel buttons
3428          * @type Object
3429          */
3430         OKCANCEL : {ok:true, cancel:true},
3431         /**
3432          * Button config that displays Yes, No and Cancel buttons
3433          * @type Object
3434          */
3435         YESNOCANCEL : {yes:true, no:true, cancel:true},
3436
3437         /**
3438          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3439          * @type Number
3440          */
3441         defaultTextHeight : 75,
3442         /**
3443          * The maximum width in pixels of the message box (defaults to 600)
3444          * @type Number
3445          */
3446         maxWidth : 600,
3447         /**
3448          * The minimum width in pixels of the message box (defaults to 100)
3449          * @type Number
3450          */
3451         minWidth : 100,
3452         /**
3453          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3454          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3455          * @type Number
3456          */
3457         minProgressWidth : 250,
3458         /**
3459          * An object containing the default button text strings that can be overriden for localized language support.
3460          * Supported properties are: ok, cancel, yes and no.
3461          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3462          * @type Object
3463          */
3464         buttonText : {
3465             ok : "OK",
3466             cancel : "Cancel",
3467             yes : "Yes",
3468             no : "No"
3469         }
3470     };
3471 }();
3472
3473 /**
3474  * Shorthand for {@link Roo.MessageBox}
3475  */
3476 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3477 Roo.Msg = Roo.Msg || Roo.MessageBox;
3478 /*
3479  * - LGPL
3480  *
3481  * navbar
3482  * 
3483  */
3484
3485 /**
3486  * @class Roo.bootstrap.Navbar
3487  * @extends Roo.bootstrap.Component
3488  * Bootstrap Navbar class
3489
3490  * @constructor
3491  * Create a new Navbar
3492  * @param {Object} config The config object
3493  */
3494
3495
3496 Roo.bootstrap.Navbar = function(config){
3497     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3498     this.addEvents({
3499         // raw events
3500         /**
3501          * @event beforetoggle
3502          * Fire before toggle the menu
3503          * @param {Roo.EventObject} e
3504          */
3505         "beforetoggle" : true
3506     });
3507 };
3508
3509 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3510     
3511     
3512    
3513     // private
3514     navItems : false,
3515     loadMask : false,
3516     
3517     
3518     getAutoCreate : function(){
3519         
3520         
3521         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3522         
3523     },
3524     
3525     initEvents :function ()
3526     {
3527         //Roo.log(this.el.select('.navbar-toggle',true));
3528         this.el.select('.navbar-toggle',true).on('click', function() {
3529             if(this.fireEvent('beforetoggle', this) !== false){
3530                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3531             }
3532             
3533         }, this);
3534         
3535         var mark = {
3536             tag: "div",
3537             cls:"x-dlg-mask"
3538         };
3539         
3540         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3541         
3542         var size = this.el.getSize();
3543         this.maskEl.setSize(size.width, size.height);
3544         this.maskEl.enableDisplayMode("block");
3545         this.maskEl.hide();
3546         
3547         if(this.loadMask){
3548             this.maskEl.show();
3549         }
3550     },
3551     
3552     
3553     getChildContainer : function()
3554     {
3555         if (this.el.select('.collapse').getCount()) {
3556             return this.el.select('.collapse',true).first();
3557         }
3558         
3559         return this.el;
3560     },
3561     
3562     mask : function()
3563     {
3564         this.maskEl.show();
3565     },
3566     
3567     unmask : function()
3568     {
3569         this.maskEl.hide();
3570     } 
3571     
3572     
3573     
3574     
3575 });
3576
3577
3578
3579  
3580
3581  /*
3582  * - LGPL
3583  *
3584  * navbar
3585  * 
3586  */
3587
3588 /**
3589  * @class Roo.bootstrap.NavSimplebar
3590  * @extends Roo.bootstrap.Navbar
3591  * Bootstrap Sidebar class
3592  *
3593  * @cfg {Boolean} inverse is inverted color
3594  * 
3595  * @cfg {String} type (nav | pills | tabs)
3596  * @cfg {Boolean} arrangement stacked | justified
3597  * @cfg {String} align (left | right) alignment
3598  * 
3599  * @cfg {Boolean} main (true|false) main nav bar? default false
3600  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3601  * 
3602  * @cfg {String} tag (header|footer|nav|div) default is nav 
3603
3604  * 
3605  * 
3606  * 
3607  * @constructor
3608  * Create a new Sidebar
3609  * @param {Object} config The config object
3610  */
3611
3612
3613 Roo.bootstrap.NavSimplebar = function(config){
3614     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3615 };
3616
3617 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3618     
3619     inverse: false,
3620     
3621     type: false,
3622     arrangement: '',
3623     align : false,
3624     
3625     
3626     
3627     main : false,
3628     
3629     
3630     tag : false,
3631     
3632     
3633     getAutoCreate : function(){
3634         
3635         
3636         var cfg = {
3637             tag : this.tag || 'div',
3638             cls : 'navbar'
3639         };
3640           
3641         
3642         cfg.cn = [
3643             {
3644                 cls: 'nav',
3645                 tag : 'ul'
3646             }
3647         ];
3648         
3649          
3650         this.type = this.type || 'nav';
3651         if (['tabs','pills'].indexOf(this.type)!==-1) {
3652             cfg.cn[0].cls += ' nav-' + this.type
3653         
3654         
3655         } else {
3656             if (this.type!=='nav') {
3657                 Roo.log('nav type must be nav/tabs/pills')
3658             }
3659             cfg.cn[0].cls += ' navbar-nav'
3660         }
3661         
3662         
3663         
3664         
3665         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3666             cfg.cn[0].cls += ' nav-' + this.arrangement;
3667         }
3668         
3669         
3670         if (this.align === 'right') {
3671             cfg.cn[0].cls += ' navbar-right';
3672         }
3673         
3674         if (this.inverse) {
3675             cfg.cls += ' navbar-inverse';
3676             
3677         }
3678         
3679         
3680         return cfg;
3681     
3682         
3683     }
3684     
3685     
3686     
3687 });
3688
3689
3690
3691  
3692
3693  
3694        /*
3695  * - LGPL
3696  *
3697  * navbar
3698  * 
3699  */
3700
3701 /**
3702  * @class Roo.bootstrap.NavHeaderbar
3703  * @extends Roo.bootstrap.NavSimplebar
3704  * Bootstrap Sidebar class
3705  *
3706  * @cfg {String} brand what is brand
3707  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3708  * @cfg {String} brand_href href of the brand
3709  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3710  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3711  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3712  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3713  * 
3714  * @constructor
3715  * Create a new Sidebar
3716  * @param {Object} config The config object
3717  */
3718
3719
3720 Roo.bootstrap.NavHeaderbar = function(config){
3721     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3722       
3723 };
3724
3725 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3726     
3727     position: '',
3728     brand: '',
3729     brand_href: false,
3730     srButton : true,
3731     autohide : false,
3732     desktopCenter : false,
3733    
3734     
3735     getAutoCreate : function(){
3736         
3737         var   cfg = {
3738             tag: this.nav || 'nav',
3739             cls: 'navbar',
3740             role: 'navigation',
3741             cn: []
3742         };
3743         
3744         var cn = cfg.cn;
3745         if (this.desktopCenter) {
3746             cn.push({cls : 'container', cn : []});
3747             cn = cn[0].cn;
3748         }
3749         
3750         if(this.srButton){
3751             cn.push({
3752                 tag: 'div',
3753                 cls: 'navbar-header',
3754                 cn: [
3755                     {
3756                         tag: 'button',
3757                         type: 'button',
3758                         cls: 'navbar-toggle',
3759                         'data-toggle': 'collapse',
3760                         cn: [
3761                             {
3762                                 tag: 'span',
3763                                 cls: 'sr-only',
3764                                 html: 'Toggle navigation'
3765                             },
3766                             {
3767                                 tag: 'span',
3768                                 cls: 'icon-bar'
3769                             },
3770                             {
3771                                 tag: 'span',
3772                                 cls: 'icon-bar'
3773                             },
3774                             {
3775                                 tag: 'span',
3776                                 cls: 'icon-bar'
3777                             }
3778                         ]
3779                     }
3780                 ]
3781             });
3782         }
3783         
3784         cn.push({
3785             tag: 'div',
3786             cls: 'collapse navbar-collapse',
3787             cn : []
3788         });
3789         
3790         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3791         
3792         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3793             cfg.cls += ' navbar-' + this.position;
3794             
3795             // tag can override this..
3796             
3797             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3798         }
3799         
3800         if (this.brand !== '') {
3801             cn[0].cn.push({
3802                 tag: 'a',
3803                 href: this.brand_href ? this.brand_href : '#',
3804                 cls: 'navbar-brand',
3805                 cn: [
3806                 this.brand
3807                 ]
3808             });
3809         }
3810         
3811         if(this.main){
3812             cfg.cls += ' main-nav';
3813         }
3814         
3815         
3816         return cfg;
3817
3818         
3819     },
3820     getHeaderChildContainer : function()
3821     {
3822         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3823             return this.el.select('.navbar-header',true).first();
3824         }
3825         
3826         return this.getChildContainer();
3827     },
3828     
3829     
3830     initEvents : function()
3831     {
3832         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3833         
3834         if (this.autohide) {
3835             
3836             var prevScroll = 0;
3837             var ft = this.el;
3838             
3839             Roo.get(document).on('scroll',function(e) {
3840                 var ns = Roo.get(document).getScroll().top;
3841                 var os = prevScroll;
3842                 prevScroll = ns;
3843                 
3844                 if(ns > os){
3845                     ft.removeClass('slideDown');
3846                     ft.addClass('slideUp');
3847                     return;
3848                 }
3849                 ft.removeClass('slideUp');
3850                 ft.addClass('slideDown');
3851                  
3852               
3853           },this);
3854         }
3855     }    
3856     
3857 });
3858
3859
3860
3861  
3862
3863  /*
3864  * - LGPL
3865  *
3866  * navbar
3867  * 
3868  */
3869
3870 /**
3871  * @class Roo.bootstrap.NavSidebar
3872  * @extends Roo.bootstrap.Navbar
3873  * Bootstrap Sidebar class
3874  * 
3875  * @constructor
3876  * Create a new Sidebar
3877  * @param {Object} config The config object
3878  */
3879
3880
3881 Roo.bootstrap.NavSidebar = function(config){
3882     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3883 };
3884
3885 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3886     
3887     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3888     
3889     getAutoCreate : function(){
3890         
3891         
3892         return  {
3893             tag: 'div',
3894             cls: 'sidebar sidebar-nav'
3895         };
3896     
3897         
3898     }
3899     
3900     
3901     
3902 });
3903
3904
3905
3906  
3907
3908  /*
3909  * - LGPL
3910  *
3911  * nav group
3912  * 
3913  */
3914
3915 /**
3916  * @class Roo.bootstrap.NavGroup
3917  * @extends Roo.bootstrap.Component
3918  * Bootstrap NavGroup class
3919  * @cfg {String} align (left|right)
3920  * @cfg {Boolean} inverse
3921  * @cfg {String} type (nav|pills|tab) default nav
3922  * @cfg {String} navId - reference Id for navbar.
3923
3924  * 
3925  * @constructor
3926  * Create a new nav group
3927  * @param {Object} config The config object
3928  */
3929
3930 Roo.bootstrap.NavGroup = function(config){
3931     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3932     this.navItems = [];
3933    
3934     Roo.bootstrap.NavGroup.register(this);
3935      this.addEvents({
3936         /**
3937              * @event changed
3938              * Fires when the active item changes
3939              * @param {Roo.bootstrap.NavGroup} this
3940              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3941              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3942          */
3943         'changed': true
3944      });
3945     
3946 };
3947
3948 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3949     
3950     align: '',
3951     inverse: false,
3952     form: false,
3953     type: 'nav',
3954     navId : '',
3955     // private
3956     
3957     navItems : false, 
3958     
3959     getAutoCreate : function()
3960     {
3961         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3962         
3963         cfg = {
3964             tag : 'ul',
3965             cls: 'nav' 
3966         };
3967         
3968         if (['tabs','pills'].indexOf(this.type)!==-1) {
3969             cfg.cls += ' nav-' + this.type
3970         } else {
3971             if (this.type!=='nav') {
3972                 Roo.log('nav type must be nav/tabs/pills')
3973             }
3974             cfg.cls += ' navbar-nav'
3975         }
3976         
3977         if (this.parent().sidebar) {
3978             cfg = {
3979                 tag: 'ul',
3980                 cls: 'dashboard-menu sidebar-menu'
3981             };
3982             
3983             return cfg;
3984         }
3985         
3986         if (this.form === true) {
3987             cfg = {
3988                 tag: 'form',
3989                 cls: 'navbar-form'
3990             };
3991             
3992             if (this.align === 'right') {
3993                 cfg.cls += ' navbar-right';
3994             } else {
3995                 cfg.cls += ' navbar-left';
3996             }
3997         }
3998         
3999         if (this.align === 'right') {
4000             cfg.cls += ' navbar-right';
4001         }
4002         
4003         if (this.inverse) {
4004             cfg.cls += ' navbar-inverse';
4005             
4006         }
4007         
4008         
4009         return cfg;
4010     },
4011     /**
4012     * sets the active Navigation item
4013     * @param {Roo.bootstrap.NavItem} the new current navitem
4014     */
4015     setActiveItem : function(item)
4016     {
4017         var prev = false;
4018         Roo.each(this.navItems, function(v){
4019             if (v == item) {
4020                 return ;
4021             }
4022             if (v.isActive()) {
4023                 v.setActive(false, true);
4024                 prev = v;
4025                 
4026             }
4027             
4028         });
4029
4030         item.setActive(true, true);
4031         this.fireEvent('changed', this, item, prev);
4032         
4033         
4034     },
4035     /**
4036     * gets the active Navigation item
4037     * @return {Roo.bootstrap.NavItem} the current navitem
4038     */
4039     getActive : function()
4040     {
4041         
4042         var prev = false;
4043         Roo.each(this.navItems, function(v){
4044             
4045             if (v.isActive()) {
4046                 prev = v;
4047                 
4048             }
4049             
4050         });
4051         return prev;
4052     },
4053     
4054     indexOfNav : function()
4055     {
4056         
4057         var prev = false;
4058         Roo.each(this.navItems, function(v,i){
4059             
4060             if (v.isActive()) {
4061                 prev = i;
4062                 
4063             }
4064             
4065         });
4066         return prev;
4067     },
4068     /**
4069     * adds a Navigation item
4070     * @param {Roo.bootstrap.NavItem} the navitem to add
4071     */
4072     addItem : function(cfg)
4073     {
4074         var cn = new Roo.bootstrap.NavItem(cfg);
4075         this.register(cn);
4076         cn.parentId = this.id;
4077         cn.onRender(this.el, null);
4078         return cn;
4079     },
4080     /**
4081     * register a Navigation item
4082     * @param {Roo.bootstrap.NavItem} the navitem to add
4083     */
4084     register : function(item)
4085     {
4086         this.navItems.push( item);
4087         item.navId = this.navId;
4088     
4089     },
4090     
4091     /**
4092     * clear all the Navigation item
4093     */
4094    
4095     clearAll : function()
4096     {
4097         this.navItems = [];
4098         this.el.dom.innerHTML = '';
4099     },
4100     
4101     getNavItem: function(tabId)
4102     {
4103         var ret = false;
4104         Roo.each(this.navItems, function(e) {
4105             if (e.tabId == tabId) {
4106                ret =  e;
4107                return false;
4108             }
4109             return true;
4110             
4111         });
4112         return ret;
4113     },
4114     
4115     setActiveNext : function()
4116     {
4117         var i = this.indexOfNav(this.getActive());
4118         if (i > this.navItems.length) {
4119             return;
4120         }
4121         this.setActiveItem(this.navItems[i+1]);
4122     },
4123     setActivePrev : function()
4124     {
4125         var i = this.indexOfNav(this.getActive());
4126         if (i  < 1) {
4127             return;
4128         }
4129         this.setActiveItem(this.navItems[i-1]);
4130     },
4131     clearWasActive : function(except) {
4132         Roo.each(this.navItems, function(e) {
4133             if (e.tabId != except.tabId && e.was_active) {
4134                e.was_active = false;
4135                return false;
4136             }
4137             return true;
4138             
4139         });
4140     },
4141     getWasActive : function ()
4142     {
4143         var r = false;
4144         Roo.each(this.navItems, function(e) {
4145             if (e.was_active) {
4146                r = e;
4147                return false;
4148             }
4149             return true;
4150             
4151         });
4152         return r;
4153     }
4154     
4155     
4156 });
4157
4158  
4159 Roo.apply(Roo.bootstrap.NavGroup, {
4160     
4161     groups: {},
4162      /**
4163     * register a Navigation Group
4164     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4165     */
4166     register : function(navgrp)
4167     {
4168         this.groups[navgrp.navId] = navgrp;
4169         
4170     },
4171     /**
4172     * fetch a Navigation Group based on the navigation ID
4173     * @param {string} the navgroup to add
4174     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4175     */
4176     get: function(navId) {
4177         if (typeof(this.groups[navId]) == 'undefined') {
4178             return false;
4179             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4180         }
4181         return this.groups[navId] ;
4182     }
4183     
4184     
4185     
4186 });
4187
4188  /*
4189  * - LGPL
4190  *
4191  * row
4192  * 
4193  */
4194
4195 /**
4196  * @class Roo.bootstrap.NavItem
4197  * @extends Roo.bootstrap.Component
4198  * Bootstrap Navbar.NavItem class
4199  * @cfg {String} href  link to
4200  * @cfg {String} html content of button
4201  * @cfg {String} badge text inside badge
4202  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4203  * @cfg {String} glyphicon name of glyphicon
4204  * @cfg {String} icon name of font awesome icon
4205  * @cfg {Boolean} active Is item active
4206  * @cfg {Boolean} disabled Is item disabled
4207  
4208  * @cfg {Boolean} preventDefault (true | false) default false
4209  * @cfg {String} tabId the tab that this item activates.
4210  * @cfg {String} tagtype (a|span) render as a href or span?
4211  * @cfg {Boolean} animateRef (true|false) link to element default false  
4212   
4213  * @constructor
4214  * Create a new Navbar Item
4215  * @param {Object} config The config object
4216  */
4217 Roo.bootstrap.NavItem = function(config){
4218     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4219     this.addEvents({
4220         // raw events
4221         /**
4222          * @event click
4223          * The raw click event for the entire grid.
4224          * @param {Roo.EventObject} e
4225          */
4226         "click" : true,
4227          /**
4228             * @event changed
4229             * Fires when the active item active state changes
4230             * @param {Roo.bootstrap.NavItem} this
4231             * @param {boolean} state the new state
4232              
4233          */
4234         'changed': true,
4235         /**
4236             * @event scrollto
4237             * Fires when scroll to element
4238             * @param {Roo.bootstrap.NavItem} this
4239             * @param {Object} options
4240             * @param {Roo.EventObject} e
4241              
4242          */
4243         'scrollto': true
4244     });
4245    
4246 };
4247
4248 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4249     
4250     href: false,
4251     html: '',
4252     badge: '',
4253     icon: false,
4254     glyphicon: false,
4255     active: false,
4256     preventDefault : false,
4257     tabId : false,
4258     tagtype : 'a',
4259     disabled : false,
4260     animateRef : false,
4261     was_active : false,
4262     
4263     getAutoCreate : function(){
4264          
4265         var cfg = {
4266             tag: 'li',
4267             cls: 'nav-item'
4268             
4269         };
4270         
4271         if (this.active) {
4272             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4273         }
4274         if (this.disabled) {
4275             cfg.cls += ' disabled';
4276         }
4277         
4278         if (this.href || this.html || this.glyphicon || this.icon) {
4279             cfg.cn = [
4280                 {
4281                     tag: this.tagtype,
4282                     href : this.href || "#",
4283                     html: this.html || ''
4284                 }
4285             ];
4286             
4287             if (this.icon) {
4288                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4289             }
4290
4291             if(this.glyphicon) {
4292                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4293             }
4294             
4295             if (this.menu) {
4296                 
4297                 cfg.cn[0].html += " <span class='caret'></span>";
4298              
4299             }
4300             
4301             if (this.badge !== '') {
4302                  
4303                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4304             }
4305         }
4306         
4307         
4308         
4309         return cfg;
4310     },
4311     initEvents: function() 
4312     {
4313         if (typeof (this.menu) != 'undefined') {
4314             this.menu.parentType = this.xtype;
4315             this.menu.triggerEl = this.el;
4316             this.menu = this.addxtype(Roo.apply({}, this.menu));
4317         }
4318         
4319         this.el.select('a',true).on('click', this.onClick, this);
4320         
4321         if(this.tagtype == 'span'){
4322             this.el.select('span',true).on('click', this.onClick, this);
4323         }
4324        
4325         // at this point parent should be available..
4326         this.parent().register(this);
4327     },
4328     
4329     onClick : function(e)
4330     {
4331         if (e.getTarget('.dropdown-menu-item')) {
4332             // did you click on a menu itemm.... - then don't trigger onclick..
4333             return;
4334         }
4335         
4336         if(
4337                 this.preventDefault || 
4338                 this.href == '#' 
4339         ){
4340             Roo.log("NavItem - prevent Default?");
4341             e.preventDefault();
4342         }
4343         
4344         if (this.disabled) {
4345             return;
4346         }
4347         
4348         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4349         if (tg && tg.transition) {
4350             Roo.log("waiting for the transitionend");
4351             return;
4352         }
4353         
4354         
4355         
4356         //Roo.log("fire event clicked");
4357         if(this.fireEvent('click', this, e) === false){
4358             return;
4359         };
4360         
4361         if(this.tagtype == 'span'){
4362             return;
4363         }
4364         
4365         //Roo.log(this.href);
4366         var ael = this.el.select('a',true).first();
4367         //Roo.log(ael);
4368         
4369         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4370             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4371             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4372                 return; // ignore... - it's a 'hash' to another page.
4373             }
4374             Roo.log("NavItem - prevent Default?");
4375             e.preventDefault();
4376             this.scrollToElement(e);
4377         }
4378         
4379         
4380         var p =  this.parent();
4381    
4382         if (['tabs','pills'].indexOf(p.type)!==-1) {
4383             if (typeof(p.setActiveItem) !== 'undefined') {
4384                 p.setActiveItem(this);
4385             }
4386         }
4387         
4388         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4389         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4390             // remove the collapsed menu expand...
4391             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4392         }
4393     },
4394     
4395     isActive: function () {
4396         return this.active
4397     },
4398     setActive : function(state, fire, is_was_active)
4399     {
4400         if (this.active && !state && this.navId) {
4401             this.was_active = true;
4402             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4403             if (nv) {
4404                 nv.clearWasActive(this);
4405             }
4406             
4407         }
4408         this.active = state;
4409         
4410         if (!state ) {
4411             this.el.removeClass('active');
4412         } else if (!this.el.hasClass('active')) {
4413             this.el.addClass('active');
4414         }
4415         if (fire) {
4416             this.fireEvent('changed', this, state);
4417         }
4418         
4419         // show a panel if it's registered and related..
4420         
4421         if (!this.navId || !this.tabId || !state || is_was_active) {
4422             return;
4423         }
4424         
4425         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4426         if (!tg) {
4427             return;
4428         }
4429         var pan = tg.getPanelByName(this.tabId);
4430         if (!pan) {
4431             return;
4432         }
4433         // if we can not flip to new panel - go back to old nav highlight..
4434         if (false == tg.showPanel(pan)) {
4435             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4436             if (nv) {
4437                 var onav = nv.getWasActive();
4438                 if (onav) {
4439                     onav.setActive(true, false, true);
4440                 }
4441             }
4442             
4443         }
4444         
4445         
4446         
4447     },
4448      // this should not be here...
4449     setDisabled : function(state)
4450     {
4451         this.disabled = state;
4452         if (!state ) {
4453             this.el.removeClass('disabled');
4454         } else if (!this.el.hasClass('disabled')) {
4455             this.el.addClass('disabled');
4456         }
4457         
4458     },
4459     
4460     /**
4461      * Fetch the element to display the tooltip on.
4462      * @return {Roo.Element} defaults to this.el
4463      */
4464     tooltipEl : function()
4465     {
4466         return this.el.select('' + this.tagtype + '', true).first();
4467     },
4468     
4469     scrollToElement : function(e)
4470     {
4471         var c = document.body;
4472         
4473         /*
4474          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4475          */
4476         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4477             c = document.documentElement;
4478         }
4479         
4480         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4481         
4482         if(!target){
4483             return;
4484         }
4485
4486         var o = target.calcOffsetsTo(c);
4487         
4488         var options = {
4489             target : target,
4490             value : o[1]
4491         };
4492         
4493         this.fireEvent('scrollto', this, options, e);
4494         
4495         Roo.get(c).scrollTo('top', options.value, true);
4496         
4497         return;
4498     }
4499 });
4500  
4501
4502  /*
4503  * - LGPL
4504  *
4505  * sidebar item
4506  *
4507  *  li
4508  *    <span> icon </span>
4509  *    <span> text </span>
4510  *    <span>badge </span>
4511  */
4512
4513 /**
4514  * @class Roo.bootstrap.NavSidebarItem
4515  * @extends Roo.bootstrap.NavItem
4516  * Bootstrap Navbar.NavSidebarItem class
4517  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4518  * {bool} open is the menu open
4519  * @constructor
4520  * Create a new Navbar Button
4521  * @param {Object} config The config object
4522  */
4523 Roo.bootstrap.NavSidebarItem = function(config){
4524     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4525     this.addEvents({
4526         // raw events
4527         /**
4528          * @event click
4529          * The raw click event for the entire grid.
4530          * @param {Roo.EventObject} e
4531          */
4532         "click" : true,
4533          /**
4534             * @event changed
4535             * Fires when the active item active state changes
4536             * @param {Roo.bootstrap.NavSidebarItem} this
4537             * @param {boolean} state the new state
4538              
4539          */
4540         'changed': true
4541     });
4542    
4543 };
4544
4545 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4546     
4547     badgeWeight : 'default',
4548     
4549     open: false,
4550     
4551     getAutoCreate : function(){
4552         
4553         
4554         var a = {
4555                 tag: 'a',
4556                 href : this.href || '#',
4557                 cls: '',
4558                 html : '',
4559                 cn : []
4560         };
4561         var cfg = {
4562             tag: 'li',
4563             cls: '',
4564             cn: [ a ]
4565         };
4566         var span = {
4567             tag: 'span',
4568             html : this.html || ''
4569         };
4570         
4571         
4572         if (this.active) {
4573             cfg.cls += ' active';
4574         }
4575         
4576         if (this.disabled) {
4577             cfg.cls += ' disabled';
4578         }
4579         if (this.open) {
4580             cfg.cls += ' open x-open';
4581         }
4582         // left icon..
4583         if (this.glyphicon || this.icon) {
4584             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4585             a.cn.push({ tag : 'i', cls : c }) ;
4586         }
4587         // html..
4588         a.cn.push(span);
4589         // then badge..
4590         if (this.badge !== '') {
4591             
4592             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4593         }
4594         // fi
4595         if (this.menu) {
4596             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4597             a.cls += 'dropdown-toggle treeview' ;
4598         }
4599         
4600         return cfg;
4601          
4602            
4603     },
4604     
4605     initEvents : function()
4606     { 
4607         if (typeof (this.menu) != 'undefined') {
4608             this.menu.parentType = this.xtype;
4609             this.menu.triggerEl = this.el;
4610             this.menu = this.addxtype(Roo.apply({}, this.menu));
4611         }
4612         
4613         this.el.on('click', this.onClick, this);
4614        
4615     
4616         if(this.badge !== ''){
4617  
4618             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4619         }
4620         
4621     },
4622     
4623     onClick : function(e)
4624     {
4625         if(this.disabled){
4626             e.preventDefault();
4627             return;
4628         }
4629         
4630         if(this.preventDefault){
4631             e.preventDefault();
4632         }
4633         
4634         this.fireEvent('click', this);
4635     },
4636     
4637     disable : function()
4638     {
4639         this.setDisabled(true);
4640     },
4641     
4642     enable : function()
4643     {
4644         this.setDisabled(false);
4645     },
4646     
4647     setDisabled : function(state)
4648     {
4649         if(this.disabled == state){
4650             return;
4651         }
4652         
4653         this.disabled = state;
4654         
4655         if (state) {
4656             this.el.addClass('disabled');
4657             return;
4658         }
4659         
4660         this.el.removeClass('disabled');
4661         
4662         return;
4663     },
4664     
4665     setActive : function(state)
4666     {
4667         if(this.active == state){
4668             return;
4669         }
4670         
4671         this.active = state;
4672         
4673         if (state) {
4674             this.el.addClass('active');
4675             return;
4676         }
4677         
4678         this.el.removeClass('active');
4679         
4680         return;
4681     },
4682     
4683     isActive: function () 
4684     {
4685         return this.active;
4686     },
4687     
4688     setBadge : function(str)
4689     {
4690         if(!this.badgeEl){
4691             return;
4692         }
4693         
4694         this.badgeEl.dom.innerHTML = str;
4695     }
4696     
4697    
4698      
4699  
4700 });
4701  
4702
4703  /*
4704  * - LGPL
4705  *
4706  * row
4707  * 
4708  */
4709
4710 /**
4711  * @class Roo.bootstrap.Row
4712  * @extends Roo.bootstrap.Component
4713  * Bootstrap Row class (contains columns...)
4714  * 
4715  * @constructor
4716  * Create a new Row
4717  * @param {Object} config The config object
4718  */
4719
4720 Roo.bootstrap.Row = function(config){
4721     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4722 };
4723
4724 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4725     
4726     getAutoCreate : function(){
4727        return {
4728             cls: 'row clearfix'
4729        };
4730     }
4731     
4732     
4733 });
4734
4735  
4736
4737  /*
4738  * - LGPL
4739  *
4740  * element
4741  * 
4742  */
4743
4744 /**
4745  * @class Roo.bootstrap.Element
4746  * @extends Roo.bootstrap.Component
4747  * Bootstrap Element class
4748  * @cfg {String} html contents of the element
4749  * @cfg {String} tag tag of the element
4750  * @cfg {String} cls class of the element
4751  * @cfg {Boolean} preventDefault (true|false) default false
4752  * @cfg {Boolean} clickable (true|false) default false
4753  * 
4754  * @constructor
4755  * Create a new Element
4756  * @param {Object} config The config object
4757  */
4758
4759 Roo.bootstrap.Element = function(config){
4760     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4761     
4762     this.addEvents({
4763         // raw events
4764         /**
4765          * @event click
4766          * When a element is chick
4767          * @param {Roo.bootstrap.Element} this
4768          * @param {Roo.EventObject} e
4769          */
4770         "click" : true
4771     });
4772 };
4773
4774 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4775     
4776     tag: 'div',
4777     cls: '',
4778     html: '',
4779     preventDefault: false, 
4780     clickable: false,
4781     
4782     getAutoCreate : function(){
4783         
4784         var cfg = {
4785             tag: this.tag,
4786             cls: this.cls,
4787             html: this.html
4788         };
4789         
4790         return cfg;
4791     },
4792     
4793     initEvents: function() 
4794     {
4795         Roo.bootstrap.Element.superclass.initEvents.call(this);
4796         
4797         if(this.clickable){
4798             this.el.on('click', this.onClick, this);
4799         }
4800         
4801     },
4802     
4803     onClick : function(e)
4804     {
4805         if(this.preventDefault){
4806             e.preventDefault();
4807         }
4808         
4809         this.fireEvent('click', this, e);
4810     },
4811     
4812     getValue : function()
4813     {
4814         return this.el.dom.innerHTML;
4815     },
4816     
4817     setValue : function(value)
4818     {
4819         this.el.dom.innerHTML = value;
4820     }
4821    
4822 });
4823
4824  
4825
4826  /*
4827  * - LGPL
4828  *
4829  * pagination
4830  * 
4831  */
4832
4833 /**
4834  * @class Roo.bootstrap.Pagination
4835  * @extends Roo.bootstrap.Component
4836  * Bootstrap Pagination class
4837  * @cfg {String} size xs | sm | md | lg
4838  * @cfg {Boolean} inverse false | true
4839  * 
4840  * @constructor
4841  * Create a new Pagination
4842  * @param {Object} config The config object
4843  */
4844
4845 Roo.bootstrap.Pagination = function(config){
4846     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4847 };
4848
4849 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4850     
4851     cls: false,
4852     size: false,
4853     inverse: false,
4854     
4855     getAutoCreate : function(){
4856         var cfg = {
4857             tag: 'ul',
4858                 cls: 'pagination'
4859         };
4860         if (this.inverse) {
4861             cfg.cls += ' inverse';
4862         }
4863         if (this.html) {
4864             cfg.html=this.html;
4865         }
4866         if (this.cls) {
4867             cfg.cls += " " + this.cls;
4868         }
4869         return cfg;
4870     }
4871    
4872 });
4873
4874  
4875
4876  /*
4877  * - LGPL
4878  *
4879  * Pagination item
4880  * 
4881  */
4882
4883
4884 /**
4885  * @class Roo.bootstrap.PaginationItem
4886  * @extends Roo.bootstrap.Component
4887  * Bootstrap PaginationItem class
4888  * @cfg {String} html text
4889  * @cfg {String} href the link
4890  * @cfg {Boolean} preventDefault (true | false) default true
4891  * @cfg {Boolean} active (true | false) default false
4892  * @cfg {Boolean} disabled default false
4893  * 
4894  * 
4895  * @constructor
4896  * Create a new PaginationItem
4897  * @param {Object} config The config object
4898  */
4899
4900
4901 Roo.bootstrap.PaginationItem = function(config){
4902     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4903     this.addEvents({
4904         // raw events
4905         /**
4906          * @event click
4907          * The raw click event for the entire grid.
4908          * @param {Roo.EventObject} e
4909          */
4910         "click" : true
4911     });
4912 };
4913
4914 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4915     
4916     href : false,
4917     html : false,
4918     preventDefault: true,
4919     active : false,
4920     cls : false,
4921     disabled: false,
4922     
4923     getAutoCreate : function(){
4924         var cfg= {
4925             tag: 'li',
4926             cn: [
4927                 {
4928                     tag : 'a',
4929                     href : this.href ? this.href : '#',
4930                     html : this.html ? this.html : ''
4931                 }
4932             ]
4933         };
4934         
4935         if(this.cls){
4936             cfg.cls = this.cls;
4937         }
4938         
4939         if(this.disabled){
4940             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4941         }
4942         
4943         if(this.active){
4944             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4945         }
4946         
4947         return cfg;
4948     },
4949     
4950     initEvents: function() {
4951         
4952         this.el.on('click', this.onClick, this);
4953         
4954     },
4955     onClick : function(e)
4956     {
4957         Roo.log('PaginationItem on click ');
4958         if(this.preventDefault){
4959             e.preventDefault();
4960         }
4961         
4962         if(this.disabled){
4963             return;
4964         }
4965         
4966         this.fireEvent('click', this, e);
4967     }
4968    
4969 });
4970
4971  
4972
4973  /*
4974  * - LGPL
4975  *
4976  * slider
4977  * 
4978  */
4979
4980
4981 /**
4982  * @class Roo.bootstrap.Slider
4983  * @extends Roo.bootstrap.Component
4984  * Bootstrap Slider class
4985  *    
4986  * @constructor
4987  * Create a new Slider
4988  * @param {Object} config The config object
4989  */
4990
4991 Roo.bootstrap.Slider = function(config){
4992     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4993 };
4994
4995 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4996     
4997     getAutoCreate : function(){
4998         
4999         var cfg = {
5000             tag: 'div',
5001             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5002             cn: [
5003                 {
5004                     tag: 'a',
5005                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5006                 }
5007             ]
5008         };
5009         
5010         return cfg;
5011     }
5012    
5013 });
5014
5015  /*
5016  * Based on:
5017  * Ext JS Library 1.1.1
5018  * Copyright(c) 2006-2007, Ext JS, LLC.
5019  *
5020  * Originally Released Under LGPL - original licence link has changed is not relivant.
5021  *
5022  * Fork - LGPL
5023  * <script type="text/javascript">
5024  */
5025  
5026
5027 /**
5028  * @class Roo.grid.ColumnModel
5029  * @extends Roo.util.Observable
5030  * This is the default implementation of a ColumnModel used by the Grid. It defines
5031  * the columns in the grid.
5032  * <br>Usage:<br>
5033  <pre><code>
5034  var colModel = new Roo.grid.ColumnModel([
5035         {header: "Ticker", width: 60, sortable: true, locked: true},
5036         {header: "Company Name", width: 150, sortable: true},
5037         {header: "Market Cap.", width: 100, sortable: true},
5038         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5039         {header: "Employees", width: 100, sortable: true, resizable: false}
5040  ]);
5041  </code></pre>
5042  * <p>
5043  
5044  * The config options listed for this class are options which may appear in each
5045  * individual column definition.
5046  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5047  * @constructor
5048  * @param {Object} config An Array of column config objects. See this class's
5049  * config objects for details.
5050 */
5051 Roo.grid.ColumnModel = function(config){
5052         /**
5053      * The config passed into the constructor
5054      */
5055     this.config = config;
5056     this.lookup = {};
5057
5058     // if no id, create one
5059     // if the column does not have a dataIndex mapping,
5060     // map it to the order it is in the config
5061     for(var i = 0, len = config.length; i < len; i++){
5062         var c = config[i];
5063         if(typeof c.dataIndex == "undefined"){
5064             c.dataIndex = i;
5065         }
5066         if(typeof c.renderer == "string"){
5067             c.renderer = Roo.util.Format[c.renderer];
5068         }
5069         if(typeof c.id == "undefined"){
5070             c.id = Roo.id();
5071         }
5072         if(c.editor && c.editor.xtype){
5073             c.editor  = Roo.factory(c.editor, Roo.grid);
5074         }
5075         if(c.editor && c.editor.isFormField){
5076             c.editor = new Roo.grid.GridEditor(c.editor);
5077         }
5078         this.lookup[c.id] = c;
5079     }
5080
5081     /**
5082      * The width of columns which have no width specified (defaults to 100)
5083      * @type Number
5084      */
5085     this.defaultWidth = 100;
5086
5087     /**
5088      * Default sortable of columns which have no sortable specified (defaults to false)
5089      * @type Boolean
5090      */
5091     this.defaultSortable = false;
5092
5093     this.addEvents({
5094         /**
5095              * @event widthchange
5096              * Fires when the width of a column changes.
5097              * @param {ColumnModel} this
5098              * @param {Number} columnIndex The column index
5099              * @param {Number} newWidth The new width
5100              */
5101             "widthchange": true,
5102         /**
5103              * @event headerchange
5104              * Fires when the text of a header changes.
5105              * @param {ColumnModel} this
5106              * @param {Number} columnIndex The column index
5107              * @param {Number} newText The new header text
5108              */
5109             "headerchange": true,
5110         /**
5111              * @event hiddenchange
5112              * Fires when a column is hidden or "unhidden".
5113              * @param {ColumnModel} this
5114              * @param {Number} columnIndex The column index
5115              * @param {Boolean} hidden true if hidden, false otherwise
5116              */
5117             "hiddenchange": true,
5118             /**
5119          * @event columnmoved
5120          * Fires when a column is moved.
5121          * @param {ColumnModel} this
5122          * @param {Number} oldIndex
5123          * @param {Number} newIndex
5124          */
5125         "columnmoved" : true,
5126         /**
5127          * @event columlockchange
5128          * Fires when a column's locked state is changed
5129          * @param {ColumnModel} this
5130          * @param {Number} colIndex
5131          * @param {Boolean} locked true if locked
5132          */
5133         "columnlockchange" : true
5134     });
5135     Roo.grid.ColumnModel.superclass.constructor.call(this);
5136 };
5137 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5138     /**
5139      * @cfg {String} header The header text to display in the Grid view.
5140      */
5141     /**
5142      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5143      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5144      * specified, the column's index is used as an index into the Record's data Array.
5145      */
5146     /**
5147      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5148      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5149      */
5150     /**
5151      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5152      * Defaults to the value of the {@link #defaultSortable} property.
5153      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5154      */
5155     /**
5156      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5157      */
5158     /**
5159      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5160      */
5161     /**
5162      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5163      */
5164     /**
5165      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5166      */
5167     /**
5168      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5169      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5170      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5171      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5172      */
5173        /**
5174      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5175      */
5176     /**
5177      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5178      */
5179     /**
5180      * @cfg {String} cursor (Optional)
5181      */
5182     /**
5183      * @cfg {String} tooltip (Optional)
5184      */
5185     /**
5186      * @cfg {Number} xs (Optional)
5187      */
5188     /**
5189      * @cfg {Number} sm (Optional)
5190      */
5191     /**
5192      * @cfg {Number} md (Optional)
5193      */
5194     /**
5195      * @cfg {Number} lg (Optional)
5196      */
5197     /**
5198      * Returns the id of the column at the specified index.
5199      * @param {Number} index The column index
5200      * @return {String} the id
5201      */
5202     getColumnId : function(index){
5203         return this.config[index].id;
5204     },
5205
5206     /**
5207      * Returns the column for a specified id.
5208      * @param {String} id The column id
5209      * @return {Object} the column
5210      */
5211     getColumnById : function(id){
5212         return this.lookup[id];
5213     },
5214
5215     
5216     /**
5217      * Returns the column for a specified dataIndex.
5218      * @param {String} dataIndex The column dataIndex
5219      * @return {Object|Boolean} the column or false if not found
5220      */
5221     getColumnByDataIndex: function(dataIndex){
5222         var index = this.findColumnIndex(dataIndex);
5223         return index > -1 ? this.config[index] : false;
5224     },
5225     
5226     /**
5227      * Returns the index for a specified column id.
5228      * @param {String} id The column id
5229      * @return {Number} the index, or -1 if not found
5230      */
5231     getIndexById : function(id){
5232         for(var i = 0, len = this.config.length; i < len; i++){
5233             if(this.config[i].id == id){
5234                 return i;
5235             }
5236         }
5237         return -1;
5238     },
5239     
5240     /**
5241      * Returns the index for a specified column dataIndex.
5242      * @param {String} dataIndex The column dataIndex
5243      * @return {Number} the index, or -1 if not found
5244      */
5245     
5246     findColumnIndex : function(dataIndex){
5247         for(var i = 0, len = this.config.length; i < len; i++){
5248             if(this.config[i].dataIndex == dataIndex){
5249                 return i;
5250             }
5251         }
5252         return -1;
5253     },
5254     
5255     
5256     moveColumn : function(oldIndex, newIndex){
5257         var c = this.config[oldIndex];
5258         this.config.splice(oldIndex, 1);
5259         this.config.splice(newIndex, 0, c);
5260         this.dataMap = null;
5261         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5262     },
5263
5264     isLocked : function(colIndex){
5265         return this.config[colIndex].locked === true;
5266     },
5267
5268     setLocked : function(colIndex, value, suppressEvent){
5269         if(this.isLocked(colIndex) == value){
5270             return;
5271         }
5272         this.config[colIndex].locked = value;
5273         if(!suppressEvent){
5274             this.fireEvent("columnlockchange", this, colIndex, value);
5275         }
5276     },
5277
5278     getTotalLockedWidth : function(){
5279         var totalWidth = 0;
5280         for(var i = 0; i < this.config.length; i++){
5281             if(this.isLocked(i) && !this.isHidden(i)){
5282                 this.totalWidth += this.getColumnWidth(i);
5283             }
5284         }
5285         return totalWidth;
5286     },
5287
5288     getLockedCount : function(){
5289         for(var i = 0, len = this.config.length; i < len; i++){
5290             if(!this.isLocked(i)){
5291                 return i;
5292             }
5293         }
5294         
5295         return this.config.length;
5296     },
5297
5298     /**
5299      * Returns the number of columns.
5300      * @return {Number}
5301      */
5302     getColumnCount : function(visibleOnly){
5303         if(visibleOnly === true){
5304             var c = 0;
5305             for(var i = 0, len = this.config.length; i < len; i++){
5306                 if(!this.isHidden(i)){
5307                     c++;
5308                 }
5309             }
5310             return c;
5311         }
5312         return this.config.length;
5313     },
5314
5315     /**
5316      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5317      * @param {Function} fn
5318      * @param {Object} scope (optional)
5319      * @return {Array} result
5320      */
5321     getColumnsBy : function(fn, scope){
5322         var r = [];
5323         for(var i = 0, len = this.config.length; i < len; i++){
5324             var c = this.config[i];
5325             if(fn.call(scope||this, c, i) === true){
5326                 r[r.length] = c;
5327             }
5328         }
5329         return r;
5330     },
5331
5332     /**
5333      * Returns true if the specified column is sortable.
5334      * @param {Number} col The column index
5335      * @return {Boolean}
5336      */
5337     isSortable : function(col){
5338         if(typeof this.config[col].sortable == "undefined"){
5339             return this.defaultSortable;
5340         }
5341         return this.config[col].sortable;
5342     },
5343
5344     /**
5345      * Returns the rendering (formatting) function defined for the column.
5346      * @param {Number} col The column index.
5347      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5348      */
5349     getRenderer : function(col){
5350         if(!this.config[col].renderer){
5351             return Roo.grid.ColumnModel.defaultRenderer;
5352         }
5353         return this.config[col].renderer;
5354     },
5355
5356     /**
5357      * Sets the rendering (formatting) function for a column.
5358      * @param {Number} col The column index
5359      * @param {Function} fn The function to use to process the cell's raw data
5360      * to return HTML markup for the grid view. The render function is called with
5361      * the following parameters:<ul>
5362      * <li>Data value.</li>
5363      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5364      * <li>css A CSS style string to apply to the table cell.</li>
5365      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5366      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5367      * <li>Row index</li>
5368      * <li>Column index</li>
5369      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5370      */
5371     setRenderer : function(col, fn){
5372         this.config[col].renderer = fn;
5373     },
5374
5375     /**
5376      * Returns the width for the specified column.
5377      * @param {Number} col The column index
5378      * @return {Number}
5379      */
5380     getColumnWidth : function(col){
5381         return this.config[col].width * 1 || this.defaultWidth;
5382     },
5383
5384     /**
5385      * Sets the width for a column.
5386      * @param {Number} col The column index
5387      * @param {Number} width The new width
5388      */
5389     setColumnWidth : function(col, width, suppressEvent){
5390         this.config[col].width = width;
5391         this.totalWidth = null;
5392         if(!suppressEvent){
5393              this.fireEvent("widthchange", this, col, width);
5394         }
5395     },
5396
5397     /**
5398      * Returns the total width of all columns.
5399      * @param {Boolean} includeHidden True to include hidden column widths
5400      * @return {Number}
5401      */
5402     getTotalWidth : function(includeHidden){
5403         if(!this.totalWidth){
5404             this.totalWidth = 0;
5405             for(var i = 0, len = this.config.length; i < len; i++){
5406                 if(includeHidden || !this.isHidden(i)){
5407                     this.totalWidth += this.getColumnWidth(i);
5408                 }
5409             }
5410         }
5411         return this.totalWidth;
5412     },
5413
5414     /**
5415      * Returns the header for the specified column.
5416      * @param {Number} col The column index
5417      * @return {String}
5418      */
5419     getColumnHeader : function(col){
5420         return this.config[col].header;
5421     },
5422
5423     /**
5424      * Sets the header for a column.
5425      * @param {Number} col The column index
5426      * @param {String} header The new header
5427      */
5428     setColumnHeader : function(col, header){
5429         this.config[col].header = header;
5430         this.fireEvent("headerchange", this, col, header);
5431     },
5432
5433     /**
5434      * Returns the tooltip for the specified column.
5435      * @param {Number} col The column index
5436      * @return {String}
5437      */
5438     getColumnTooltip : function(col){
5439             return this.config[col].tooltip;
5440     },
5441     /**
5442      * Sets the tooltip for a column.
5443      * @param {Number} col The column index
5444      * @param {String} tooltip The new tooltip
5445      */
5446     setColumnTooltip : function(col, tooltip){
5447             this.config[col].tooltip = tooltip;
5448     },
5449
5450     /**
5451      * Returns the dataIndex for the specified column.
5452      * @param {Number} col The column index
5453      * @return {Number}
5454      */
5455     getDataIndex : function(col){
5456         return this.config[col].dataIndex;
5457     },
5458
5459     /**
5460      * Sets the dataIndex for a column.
5461      * @param {Number} col The column index
5462      * @param {Number} dataIndex The new dataIndex
5463      */
5464     setDataIndex : function(col, dataIndex){
5465         this.config[col].dataIndex = dataIndex;
5466     },
5467
5468     
5469     
5470     /**
5471      * Returns true if the cell is editable.
5472      * @param {Number} colIndex The column index
5473      * @param {Number} rowIndex The row index - this is nto actually used..?
5474      * @return {Boolean}
5475      */
5476     isCellEditable : function(colIndex, rowIndex){
5477         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5478     },
5479
5480     /**
5481      * Returns the editor defined for the cell/column.
5482      * return false or null to disable editing.
5483      * @param {Number} colIndex The column index
5484      * @param {Number} rowIndex The row index
5485      * @return {Object}
5486      */
5487     getCellEditor : function(colIndex, rowIndex){
5488         return this.config[colIndex].editor;
5489     },
5490
5491     /**
5492      * Sets if a column is editable.
5493      * @param {Number} col The column index
5494      * @param {Boolean} editable True if the column is editable
5495      */
5496     setEditable : function(col, editable){
5497         this.config[col].editable = editable;
5498     },
5499
5500
5501     /**
5502      * Returns true if the column is hidden.
5503      * @param {Number} colIndex The column index
5504      * @return {Boolean}
5505      */
5506     isHidden : function(colIndex){
5507         return this.config[colIndex].hidden;
5508     },
5509
5510
5511     /**
5512      * Returns true if the column width cannot be changed
5513      */
5514     isFixed : function(colIndex){
5515         return this.config[colIndex].fixed;
5516     },
5517
5518     /**
5519      * Returns true if the column can be resized
5520      * @return {Boolean}
5521      */
5522     isResizable : function(colIndex){
5523         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5524     },
5525     /**
5526      * Sets if a column is hidden.
5527      * @param {Number} colIndex The column index
5528      * @param {Boolean} hidden True if the column is hidden
5529      */
5530     setHidden : function(colIndex, hidden){
5531         this.config[colIndex].hidden = hidden;
5532         this.totalWidth = null;
5533         this.fireEvent("hiddenchange", this, colIndex, hidden);
5534     },
5535
5536     /**
5537      * Sets the editor for a column.
5538      * @param {Number} col The column index
5539      * @param {Object} editor The editor object
5540      */
5541     setEditor : function(col, editor){
5542         this.config[col].editor = editor;
5543     }
5544 });
5545
5546 Roo.grid.ColumnModel.defaultRenderer = function(value)
5547 {
5548     if(typeof value == "object") {
5549         return value;
5550     }
5551         if(typeof value == "string" && value.length < 1){
5552             return "&#160;";
5553         }
5554     
5555         return String.format("{0}", value);
5556 };
5557
5558 // Alias for backwards compatibility
5559 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5560 /*
5561  * Based on:
5562  * Ext JS Library 1.1.1
5563  * Copyright(c) 2006-2007, Ext JS, LLC.
5564  *
5565  * Originally Released Under LGPL - original licence link has changed is not relivant.
5566  *
5567  * Fork - LGPL
5568  * <script type="text/javascript">
5569  */
5570  
5571 /**
5572  * @class Roo.LoadMask
5573  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5574  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5575  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5576  * element's UpdateManager load indicator and will be destroyed after the initial load.
5577  * @constructor
5578  * Create a new LoadMask
5579  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5580  * @param {Object} config The config object
5581  */
5582 Roo.LoadMask = function(el, config){
5583     this.el = Roo.get(el);
5584     Roo.apply(this, config);
5585     if(this.store){
5586         this.store.on('beforeload', this.onBeforeLoad, this);
5587         this.store.on('load', this.onLoad, this);
5588         this.store.on('loadexception', this.onLoadException, this);
5589         this.removeMask = false;
5590     }else{
5591         var um = this.el.getUpdateManager();
5592         um.showLoadIndicator = false; // disable the default indicator
5593         um.on('beforeupdate', this.onBeforeLoad, this);
5594         um.on('update', this.onLoad, this);
5595         um.on('failure', this.onLoad, this);
5596         this.removeMask = true;
5597     }
5598 };
5599
5600 Roo.LoadMask.prototype = {
5601     /**
5602      * @cfg {Boolean} removeMask
5603      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5604      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5605      */
5606     /**
5607      * @cfg {String} msg
5608      * The text to display in a centered loading message box (defaults to 'Loading...')
5609      */
5610     msg : 'Loading...',
5611     /**
5612      * @cfg {String} msgCls
5613      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5614      */
5615     msgCls : 'x-mask-loading',
5616
5617     /**
5618      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5619      * @type Boolean
5620      */
5621     disabled: false,
5622
5623     /**
5624      * Disables the mask to prevent it from being displayed
5625      */
5626     disable : function(){
5627        this.disabled = true;
5628     },
5629
5630     /**
5631      * Enables the mask so that it can be displayed
5632      */
5633     enable : function(){
5634         this.disabled = false;
5635     },
5636     
5637     onLoadException : function()
5638     {
5639         Roo.log(arguments);
5640         
5641         if (typeof(arguments[3]) != 'undefined') {
5642             Roo.MessageBox.alert("Error loading",arguments[3]);
5643         } 
5644         /*
5645         try {
5646             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5647                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5648             }   
5649         } catch(e) {
5650             
5651         }
5652         */
5653     
5654         
5655         
5656         this.el.unmask(this.removeMask);
5657     },
5658     // private
5659     onLoad : function()
5660     {
5661         this.el.unmask(this.removeMask);
5662     },
5663
5664     // private
5665     onBeforeLoad : function(){
5666         if(!this.disabled){
5667             this.el.mask(this.msg, this.msgCls);
5668         }
5669     },
5670
5671     // private
5672     destroy : function(){
5673         if(this.store){
5674             this.store.un('beforeload', this.onBeforeLoad, this);
5675             this.store.un('load', this.onLoad, this);
5676             this.store.un('loadexception', this.onLoadException, this);
5677         }else{
5678             var um = this.el.getUpdateManager();
5679             um.un('beforeupdate', this.onBeforeLoad, this);
5680             um.un('update', this.onLoad, this);
5681             um.un('failure', this.onLoad, this);
5682         }
5683     }
5684 };/*
5685  * - LGPL
5686  *
5687  * table
5688  * 
5689  */
5690
5691 /**
5692  * @class Roo.bootstrap.Table
5693  * @extends Roo.bootstrap.Component
5694  * Bootstrap Table class
5695  * @cfg {String} cls table class
5696  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5697  * @cfg {String} bgcolor Specifies the background color for a table
5698  * @cfg {Number} border Specifies whether the table cells should have borders or not
5699  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5700  * @cfg {Number} cellspacing Specifies the space between cells
5701  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5702  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5703  * @cfg {String} sortable Specifies that the table should be sortable
5704  * @cfg {String} summary Specifies a summary of the content of a table
5705  * @cfg {Number} width Specifies the width of a table
5706  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5707  * 
5708  * @cfg {boolean} striped Should the rows be alternative striped
5709  * @cfg {boolean} bordered Add borders to the table
5710  * @cfg {boolean} hover Add hover highlighting
5711  * @cfg {boolean} condensed Format condensed
5712  * @cfg {boolean} responsive Format condensed
5713  * @cfg {Boolean} loadMask (true|false) default false
5714  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5715  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5716  * @cfg {Boolean} rowSelection (true|false) default false
5717  * @cfg {Boolean} cellSelection (true|false) default false
5718  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5719  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5720  
5721  * 
5722  * @constructor
5723  * Create a new Table
5724  * @param {Object} config The config object
5725  */
5726
5727 Roo.bootstrap.Table = function(config){
5728     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5729     
5730   
5731     
5732     // BC...
5733     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5734     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5735     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5736     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5737     
5738     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5739     if (this.sm) {
5740         this.sm.grid = this;
5741         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5742         this.sm = this.selModel;
5743         this.sm.xmodule = this.xmodule || false;
5744     }
5745     
5746     if (this.cm && typeof(this.cm.config) == 'undefined') {
5747         this.colModel = new Roo.grid.ColumnModel(this.cm);
5748         this.cm = this.colModel;
5749         this.cm.xmodule = this.xmodule || false;
5750     }
5751     if (this.store) {
5752         this.store= Roo.factory(this.store, Roo.data);
5753         this.ds = this.store;
5754         this.ds.xmodule = this.xmodule || false;
5755          
5756     }
5757     if (this.footer && this.store) {
5758         this.footer.dataSource = this.ds;
5759         this.footer = Roo.factory(this.footer);
5760     }
5761     
5762     /** @private */
5763     this.addEvents({
5764         /**
5765          * @event cellclick
5766          * Fires when a cell is clicked
5767          * @param {Roo.bootstrap.Table} this
5768          * @param {Roo.Element} el
5769          * @param {Number} rowIndex
5770          * @param {Number} columnIndex
5771          * @param {Roo.EventObject} e
5772          */
5773         "cellclick" : true,
5774         /**
5775          * @event celldblclick
5776          * Fires when a cell is double clicked
5777          * @param {Roo.bootstrap.Table} this
5778          * @param {Roo.Element} el
5779          * @param {Number} rowIndex
5780          * @param {Number} columnIndex
5781          * @param {Roo.EventObject} e
5782          */
5783         "celldblclick" : true,
5784         /**
5785          * @event rowclick
5786          * Fires when a row is clicked
5787          * @param {Roo.bootstrap.Table} this
5788          * @param {Roo.Element} el
5789          * @param {Number} rowIndex
5790          * @param {Roo.EventObject} e
5791          */
5792         "rowclick" : true,
5793         /**
5794          * @event rowdblclick
5795          * Fires when a row is double clicked
5796          * @param {Roo.bootstrap.Table} this
5797          * @param {Roo.Element} el
5798          * @param {Number} rowIndex
5799          * @param {Roo.EventObject} e
5800          */
5801         "rowdblclick" : true,
5802         /**
5803          * @event mouseover
5804          * Fires when a mouseover occur
5805          * @param {Roo.bootstrap.Table} this
5806          * @param {Roo.Element} el
5807          * @param {Number} rowIndex
5808          * @param {Number} columnIndex
5809          * @param {Roo.EventObject} e
5810          */
5811         "mouseover" : true,
5812         /**
5813          * @event mouseout
5814          * Fires when a mouseout occur
5815          * @param {Roo.bootstrap.Table} this
5816          * @param {Roo.Element} el
5817          * @param {Number} rowIndex
5818          * @param {Number} columnIndex
5819          * @param {Roo.EventObject} e
5820          */
5821         "mouseout" : true,
5822         /**
5823          * @event rowclass
5824          * Fires when a row is rendered, so you can change add a style to it.
5825          * @param {Roo.bootstrap.Table} this
5826          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5827          */
5828         'rowclass' : true,
5829           /**
5830          * @event rowsrendered
5831          * Fires when all the  rows have been rendered
5832          * @param {Roo.bootstrap.Table} this
5833          */
5834         'rowsrendered' : true,
5835         /**
5836          * @event contextmenu
5837          * The raw contextmenu event for the entire grid.
5838          * @param {Roo.EventObject} e
5839          */
5840         "contextmenu" : true,
5841         /**
5842          * @event rowcontextmenu
5843          * Fires when a row is right clicked
5844          * @param {Roo.bootstrap.Table} this
5845          * @param {Number} rowIndex
5846          * @param {Roo.EventObject} e
5847          */
5848         "rowcontextmenu" : true,
5849         /**
5850          * @event cellcontextmenu
5851          * Fires when a cell is right clicked
5852          * @param {Roo.bootstrap.Table} this
5853          * @param {Number} rowIndex
5854          * @param {Number} cellIndex
5855          * @param {Roo.EventObject} e
5856          */
5857          "cellcontextmenu" : true,
5858          /**
5859          * @event headercontextmenu
5860          * Fires when a header is right clicked
5861          * @param {Roo.bootstrap.Table} this
5862          * @param {Number} columnIndex
5863          * @param {Roo.EventObject} e
5864          */
5865         "headercontextmenu" : true
5866     });
5867 };
5868
5869 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5870     
5871     cls: false,
5872     align: false,
5873     bgcolor: false,
5874     border: false,
5875     cellpadding: false,
5876     cellspacing: false,
5877     frame: false,
5878     rules: false,
5879     sortable: false,
5880     summary: false,
5881     width: false,
5882     striped : false,
5883     scrollBody : false,
5884     bordered: false,
5885     hover:  false,
5886     condensed : false,
5887     responsive : false,
5888     sm : false,
5889     cm : false,
5890     store : false,
5891     loadMask : false,
5892     footerShow : true,
5893     headerShow : true,
5894   
5895     rowSelection : false,
5896     cellSelection : false,
5897     layout : false,
5898     
5899     // Roo.Element - the tbody
5900     mainBody: false,
5901     // Roo.Element - thead element
5902     mainHead: false,
5903     
5904     container: false, // used by gridpanel...
5905     
5906     getAutoCreate : function()
5907     {
5908         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5909         
5910         cfg = {
5911             tag: 'table',
5912             cls : 'table',
5913             cn : []
5914         };
5915         if (this.scrollBody) {
5916             cfg.cls += ' table-body-fixed';
5917         }    
5918         if (this.striped) {
5919             cfg.cls += ' table-striped';
5920         }
5921         
5922         if (this.hover) {
5923             cfg.cls += ' table-hover';
5924         }
5925         if (this.bordered) {
5926             cfg.cls += ' table-bordered';
5927         }
5928         if (this.condensed) {
5929             cfg.cls += ' table-condensed';
5930         }
5931         if (this.responsive) {
5932             cfg.cls += ' table-responsive';
5933         }
5934         
5935         if (this.cls) {
5936             cfg.cls+=  ' ' +this.cls;
5937         }
5938         
5939         // this lot should be simplifed...
5940         
5941         if (this.align) {
5942             cfg.align=this.align;
5943         }
5944         if (this.bgcolor) {
5945             cfg.bgcolor=this.bgcolor;
5946         }
5947         if (this.border) {
5948             cfg.border=this.border;
5949         }
5950         if (this.cellpadding) {
5951             cfg.cellpadding=this.cellpadding;
5952         }
5953         if (this.cellspacing) {
5954             cfg.cellspacing=this.cellspacing;
5955         }
5956         if (this.frame) {
5957             cfg.frame=this.frame;
5958         }
5959         if (this.rules) {
5960             cfg.rules=this.rules;
5961         }
5962         if (this.sortable) {
5963             cfg.sortable=this.sortable;
5964         }
5965         if (this.summary) {
5966             cfg.summary=this.summary;
5967         }
5968         if (this.width) {
5969             cfg.width=this.width;
5970         }
5971         if (this.layout) {
5972             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5973         }
5974         
5975         if(this.store || this.cm){
5976             if(this.headerShow){
5977                 cfg.cn.push(this.renderHeader());
5978             }
5979             
5980             cfg.cn.push(this.renderBody());
5981             
5982             if(this.footerShow){
5983                 cfg.cn.push(this.renderFooter());
5984             }
5985             // where does this come from?
5986             //cfg.cls+=  ' TableGrid';
5987         }
5988         
5989         return { cn : [ cfg ] };
5990     },
5991     
5992     initEvents : function()
5993     {   
5994         if(!this.store || !this.cm){
5995             return;
5996         }
5997         if (this.selModel) {
5998             this.selModel.initEvents();
5999         }
6000         
6001         
6002         //Roo.log('initEvents with ds!!!!');
6003         
6004         this.mainBody = this.el.select('tbody', true).first();
6005         this.mainHead = this.el.select('thead', true).first();
6006         
6007         
6008         
6009         
6010         var _this = this;
6011         
6012         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6013             e.on('click', _this.sort, _this);
6014         });
6015         
6016         this.mainBody.on("click", this.onClick, this);
6017         this.mainBody.on("dblclick", this.onDblClick, this);
6018         
6019         // why is this done????? = it breaks dialogs??
6020         //this.parent().el.setStyle('position', 'relative');
6021         
6022         
6023         if (this.footer) {
6024             this.footer.parentId = this.id;
6025             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
6026         } 
6027         
6028         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6029         
6030         this.store.on('load', this.onLoad, this);
6031         this.store.on('beforeload', this.onBeforeLoad, this);
6032         this.store.on('update', this.onUpdate, this);
6033         this.store.on('add', this.onAdd, this);
6034         this.store.on("clear", this.clear, this);
6035         
6036         this.el.on("contextmenu", this.onContextMenu, this);
6037         
6038         this.mainBody.on('scroll', this.onBodyScroll, this);
6039         
6040         
6041     },
6042     
6043     onContextMenu : function(e, t)
6044     {
6045         this.processEvent("contextmenu", e);
6046     },
6047     
6048     processEvent : function(name, e)
6049     {
6050         if (name != 'touchstart' ) {
6051             this.fireEvent(name, e);    
6052         }
6053         
6054         var t = e.getTarget();
6055         
6056         var cell = Roo.get(t);
6057         
6058         if(!cell){
6059             return;
6060         }
6061         
6062         if(cell.findParent('tfoot', false, true)){
6063             return;
6064         }
6065         
6066         if(cell.findParent('thead', false, true)){
6067             
6068             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6069                 cell = Roo.get(t).findParent('th', false, true);
6070                 if (!cell) {
6071                     Roo.log("failed to find th in thead?");
6072                     Roo.log(e.getTarget());
6073                     return;
6074                 }
6075             }
6076             
6077             var cellIndex = cell.dom.cellIndex;
6078             
6079             var ename = name == 'touchstart' ? 'click' : name;
6080             this.fireEvent("header" + ename, this, cellIndex, e);
6081             
6082             return;
6083         }
6084         
6085         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6086             cell = Roo.get(t).findParent('td', false, true);
6087             if (!cell) {
6088                 Roo.log("failed to find th in tbody?");
6089                 Roo.log(e.getTarget());
6090                 return;
6091             }
6092         }
6093         
6094         var row = cell.findParent('tr', false, true);
6095         var cellIndex = cell.dom.cellIndex;
6096         var rowIndex = row.dom.rowIndex - 1;
6097         
6098         if(row !== false){
6099             
6100             this.fireEvent("row" + name, this, rowIndex, e);
6101             
6102             if(cell !== false){
6103             
6104                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6105             }
6106         }
6107         
6108     },
6109     
6110     onMouseover : function(e, el)
6111     {
6112         var cell = Roo.get(el);
6113         
6114         if(!cell){
6115             return;
6116         }
6117         
6118         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6119             cell = cell.findParent('td', false, true);
6120         }
6121         
6122         var row = cell.findParent('tr', false, true);
6123         var cellIndex = cell.dom.cellIndex;
6124         var rowIndex = row.dom.rowIndex - 1; // start from 0
6125         
6126         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6127         
6128     },
6129     
6130     onMouseout : function(e, el)
6131     {
6132         var cell = Roo.get(el);
6133         
6134         if(!cell){
6135             return;
6136         }
6137         
6138         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6139             cell = cell.findParent('td', false, true);
6140         }
6141         
6142         var row = cell.findParent('tr', false, true);
6143         var cellIndex = cell.dom.cellIndex;
6144         var rowIndex = row.dom.rowIndex - 1; // start from 0
6145         
6146         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6147         
6148     },
6149     
6150     onClick : function(e, el)
6151     {
6152         var cell = Roo.get(el);
6153         
6154         if(!cell || (!this.cellSelection && !this.rowSelection)){
6155             return;
6156         }
6157         
6158         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6159             cell = cell.findParent('td', false, true);
6160         }
6161         
6162         if(!cell || typeof(cell) == 'undefined'){
6163             return;
6164         }
6165         
6166         var row = cell.findParent('tr', false, true);
6167         
6168         if(!row || typeof(row) == 'undefined'){
6169             return;
6170         }
6171         
6172         var cellIndex = cell.dom.cellIndex;
6173         var rowIndex = this.getRowIndex(row);
6174         
6175         // why??? - should these not be based on SelectionModel?
6176         if(this.cellSelection){
6177             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6178         }
6179         
6180         if(this.rowSelection){
6181             this.fireEvent('rowclick', this, row, rowIndex, e);
6182         }
6183         
6184         
6185     },
6186         
6187     onDblClick : function(e,el)
6188     {
6189         var cell = Roo.get(el);
6190         
6191         if(!cell || (!this.cellSelection && !this.rowSelection)){
6192             return;
6193         }
6194         
6195         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6196             cell = cell.findParent('td', false, true);
6197         }
6198         
6199         if(!cell || typeof(cell) == 'undefined'){
6200             return;
6201         }
6202         
6203         var row = cell.findParent('tr', false, true);
6204         
6205         if(!row || typeof(row) == 'undefined'){
6206             return;
6207         }
6208         
6209         var cellIndex = cell.dom.cellIndex;
6210         var rowIndex = this.getRowIndex(row);
6211         
6212         if(this.cellSelection){
6213             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6214         }
6215         
6216         if(this.rowSelection){
6217             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6218         }
6219     },
6220     
6221     sort : function(e,el)
6222     {
6223         var col = Roo.get(el);
6224         
6225         if(!col.hasClass('sortable')){
6226             return;
6227         }
6228         
6229         var sort = col.attr('sort');
6230         var dir = 'ASC';
6231         
6232         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6233             dir = 'DESC';
6234         }
6235         
6236         this.store.sortInfo = {field : sort, direction : dir};
6237         
6238         if (this.footer) {
6239             Roo.log("calling footer first");
6240             this.footer.onClick('first');
6241         } else {
6242         
6243             this.store.load({ params : { start : 0 } });
6244         }
6245     },
6246     
6247     renderHeader : function()
6248     {
6249         var header = {
6250             tag: 'thead',
6251             cn : []
6252         };
6253         
6254         var cm = this.cm;
6255         this.totalWidth = 0;
6256         
6257         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6258             
6259             var config = cm.config[i];
6260             
6261             var c = {
6262                 tag: 'th',
6263                 style : '',
6264                 html: cm.getColumnHeader(i)
6265             };
6266             
6267             var hh = '';
6268             
6269             if(typeof(config.sortable) != 'undefined' && config.sortable){
6270                 c.cls = 'sortable';
6271                 c.html = '<i class="glyphicon"></i>' + c.html;
6272             }
6273             
6274             if(typeof(config.lgHeader) != 'undefined'){
6275                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6276             }
6277             
6278             if(typeof(config.mdHeader) != 'undefined'){
6279                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6280             }
6281             
6282             if(typeof(config.smHeader) != 'undefined'){
6283                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6284             }
6285             
6286             if(typeof(config.xsHeader) != 'undefined'){
6287                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6288             }
6289             
6290             if(hh.length){
6291                 c.html = hh;
6292             }
6293             
6294             if(typeof(config.tooltip) != 'undefined'){
6295                 c.tooltip = config.tooltip;
6296             }
6297             
6298             if(typeof(config.colspan) != 'undefined'){
6299                 c.colspan = config.colspan;
6300             }
6301             
6302             if(typeof(config.hidden) != 'undefined' && config.hidden){
6303                 c.style += ' display:none;';
6304             }
6305             
6306             if(typeof(config.dataIndex) != 'undefined'){
6307                 c.sort = config.dataIndex;
6308             }
6309             
6310            
6311             
6312             if(typeof(config.align) != 'undefined' && config.align.length){
6313                 c.style += ' text-align:' + config.align + ';';
6314             }
6315             
6316             if(typeof(config.width) != 'undefined'){
6317                 c.style += ' width:' + config.width + 'px;';
6318                 this.totalWidth += config.width;
6319             } else {
6320                 this.totalWidth += 100; // assume minimum of 100 per column?
6321             }
6322             
6323             if(typeof(config.cls) != 'undefined'){
6324                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6325             }
6326             
6327             ['xs','sm','md','lg'].map(function(size){
6328                 
6329                 if(typeof(config[size]) == 'undefined'){
6330                     return;
6331                 }
6332                 
6333                 if (!config[size]) { // 0 = hidden
6334                     c.cls += ' hidden-' + size;
6335                     return;
6336                 }
6337                 
6338                 c.cls += ' col-' + size + '-' + config[size];
6339
6340             });
6341             
6342             header.cn.push(c)
6343         }
6344         
6345         return header;
6346     },
6347     
6348     renderBody : function()
6349     {
6350         var body = {
6351             tag: 'tbody',
6352             cn : [
6353                 {
6354                     tag: 'tr',
6355                     cn : [
6356                         {
6357                             tag : 'td',
6358                             colspan :  this.cm.getColumnCount()
6359                         }
6360                     ]
6361                 }
6362             ]
6363         };
6364         
6365         return body;
6366     },
6367     
6368     renderFooter : function()
6369     {
6370         var footer = {
6371             tag: 'tfoot',
6372             cn : [
6373                 {
6374                     tag: 'tr',
6375                     cn : [
6376                         {
6377                             tag : 'td',
6378                             colspan :  this.cm.getColumnCount()
6379                         }
6380                     ]
6381                 }
6382             ]
6383         };
6384         
6385         return footer;
6386     },
6387     
6388     
6389     
6390     onLoad : function()
6391     {
6392 //        Roo.log('ds onload');
6393         this.clear();
6394         
6395         var _this = this;
6396         var cm = this.cm;
6397         var ds = this.store;
6398         
6399         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6400             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6401             if (_this.store.sortInfo) {
6402                     
6403                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6404                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6405                 }
6406                 
6407                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6408                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6409                 }
6410             }
6411         });
6412         
6413         var tbody =  this.mainBody;
6414               
6415         if(ds.getCount() > 0){
6416             ds.data.each(function(d,rowIndex){
6417                 var row =  this.renderRow(cm, ds, rowIndex);
6418                 
6419                 tbody.createChild(row);
6420                 
6421                 var _this = this;
6422                 
6423                 if(row.cellObjects.length){
6424                     Roo.each(row.cellObjects, function(r){
6425                         _this.renderCellObject(r);
6426                     })
6427                 }
6428                 
6429             }, this);
6430         }
6431         
6432         Roo.each(this.el.select('tbody td', true).elements, function(e){
6433             e.on('mouseover', _this.onMouseover, _this);
6434         });
6435         
6436         Roo.each(this.el.select('tbody td', true).elements, function(e){
6437             e.on('mouseout', _this.onMouseout, _this);
6438         });
6439         this.fireEvent('rowsrendered', this);
6440         //if(this.loadMask){
6441         //    this.maskEl.hide();
6442         //}
6443         
6444         this.autoSize();
6445     },
6446     
6447     
6448     onUpdate : function(ds,record)
6449     {
6450         this.refreshRow(record);
6451         this.autoSize();
6452     },
6453     
6454     onRemove : function(ds, record, index, isUpdate){
6455         if(isUpdate !== true){
6456             this.fireEvent("beforerowremoved", this, index, record);
6457         }
6458         var bt = this.mainBody.dom;
6459         
6460         var rows = this.el.select('tbody > tr', true).elements;
6461         
6462         if(typeof(rows[index]) != 'undefined'){
6463             bt.removeChild(rows[index].dom);
6464         }
6465         
6466 //        if(bt.rows[index]){
6467 //            bt.removeChild(bt.rows[index]);
6468 //        }
6469         
6470         if(isUpdate !== true){
6471             //this.stripeRows(index);
6472             //this.syncRowHeights(index, index);
6473             //this.layout();
6474             this.fireEvent("rowremoved", this, index, record);
6475         }
6476     },
6477     
6478     onAdd : function(ds, records, rowIndex)
6479     {
6480         //Roo.log('on Add called');
6481         // - note this does not handle multiple adding very well..
6482         var bt = this.mainBody.dom;
6483         for (var i =0 ; i < records.length;i++) {
6484             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6485             //Roo.log(records[i]);
6486             //Roo.log(this.store.getAt(rowIndex+i));
6487             this.insertRow(this.store, rowIndex + i, false);
6488             return;
6489         }
6490         
6491     },
6492     
6493     
6494     refreshRow : function(record){
6495         var ds = this.store, index;
6496         if(typeof record == 'number'){
6497             index = record;
6498             record = ds.getAt(index);
6499         }else{
6500             index = ds.indexOf(record);
6501         }
6502         this.insertRow(ds, index, true);
6503         this.autoSize();
6504         this.onRemove(ds, record, index+1, true);
6505         this.autoSize();
6506         //this.syncRowHeights(index, index);
6507         //this.layout();
6508         this.fireEvent("rowupdated", this, index, record);
6509     },
6510     
6511     insertRow : function(dm, rowIndex, isUpdate){
6512         
6513         if(!isUpdate){
6514             this.fireEvent("beforerowsinserted", this, rowIndex);
6515         }
6516             //var s = this.getScrollState();
6517         var row = this.renderRow(this.cm, this.store, rowIndex);
6518         // insert before rowIndex..
6519         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6520         
6521         var _this = this;
6522                 
6523         if(row.cellObjects.length){
6524             Roo.each(row.cellObjects, function(r){
6525                 _this.renderCellObject(r);
6526             })
6527         }
6528             
6529         if(!isUpdate){
6530             this.fireEvent("rowsinserted", this, rowIndex);
6531             //this.syncRowHeights(firstRow, lastRow);
6532             //this.stripeRows(firstRow);
6533             //this.layout();
6534         }
6535         
6536     },
6537     
6538     
6539     getRowDom : function(rowIndex)
6540     {
6541         var rows = this.el.select('tbody > tr', true).elements;
6542         
6543         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6544         
6545     },
6546     // returns the object tree for a tr..
6547   
6548     
6549     renderRow : function(cm, ds, rowIndex) 
6550     {
6551         
6552         var d = ds.getAt(rowIndex);
6553         
6554         var row = {
6555             tag : 'tr',
6556             cn : []
6557         };
6558             
6559         var cellObjects = [];
6560         
6561         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6562             var config = cm.config[i];
6563             
6564             var renderer = cm.getRenderer(i);
6565             var value = '';
6566             var id = false;
6567             
6568             if(typeof(renderer) !== 'undefined'){
6569                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6570             }
6571             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6572             // and are rendered into the cells after the row is rendered - using the id for the element.
6573             
6574             if(typeof(value) === 'object'){
6575                 id = Roo.id();
6576                 cellObjects.push({
6577                     container : id,
6578                     cfg : value 
6579                 })
6580             }
6581             
6582             var rowcfg = {
6583                 record: d,
6584                 rowIndex : rowIndex,
6585                 colIndex : i,
6586                 rowClass : ''
6587             };
6588
6589             this.fireEvent('rowclass', this, rowcfg);
6590             
6591             var td = {
6592                 tag: 'td',
6593                 cls : rowcfg.rowClass,
6594                 style: '',
6595                 html: (typeof(value) === 'object') ? '' : value
6596             };
6597             
6598             if (id) {
6599                 td.id = id;
6600             }
6601             
6602             if(typeof(config.colspan) != 'undefined'){
6603                 td.colspan = config.colspan;
6604             }
6605             
6606             if(typeof(config.hidden) != 'undefined' && config.hidden){
6607                 td.style += ' display:none;';
6608             }
6609             
6610             if(typeof(config.align) != 'undefined' && config.align.length){
6611                 td.style += ' text-align:' + config.align + ';';
6612             }
6613             
6614             if(typeof(config.width) != 'undefined'){
6615                 td.style += ' width:' +  config.width + 'px;';
6616             }
6617             
6618             if(typeof(config.cursor) != 'undefined'){
6619                 td.style += ' cursor:' +  config.cursor + ';';
6620             }
6621             
6622             if(typeof(config.cls) != 'undefined'){
6623                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6624             }
6625             
6626             ['xs','sm','md','lg'].map(function(size){
6627                 
6628                 if(typeof(config[size]) == 'undefined'){
6629                     return;
6630                 }
6631                 
6632                 if (!config[size]) { // 0 = hidden
6633                     td.cls += ' hidden-' + size;
6634                     return;
6635                 }
6636                 
6637                 td.cls += ' col-' + size + '-' + config[size];
6638
6639             });
6640              
6641             row.cn.push(td);
6642            
6643         }
6644         
6645         row.cellObjects = cellObjects;
6646         
6647         return row;
6648           
6649     },
6650     
6651     
6652     
6653     onBeforeLoad : function()
6654     {
6655         //Roo.log('ds onBeforeLoad');
6656         
6657         //this.clear();
6658         
6659         //if(this.loadMask){
6660         //    this.maskEl.show();
6661         //}
6662     },
6663      /**
6664      * Remove all rows
6665      */
6666     clear : function()
6667     {
6668         this.el.select('tbody', true).first().dom.innerHTML = '';
6669     },
6670     /**
6671      * Show or hide a row.
6672      * @param {Number} rowIndex to show or hide
6673      * @param {Boolean} state hide
6674      */
6675     setRowVisibility : function(rowIndex, state)
6676     {
6677         var bt = this.mainBody.dom;
6678         
6679         var rows = this.el.select('tbody > tr', true).elements;
6680         
6681         if(typeof(rows[rowIndex]) == 'undefined'){
6682             return;
6683         }
6684         rows[rowIndex].dom.style.display = state ? '' : 'none';
6685     },
6686     
6687     
6688     getSelectionModel : function(){
6689         if(!this.selModel){
6690             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6691         }
6692         return this.selModel;
6693     },
6694     /*
6695      * Render the Roo.bootstrap object from renderder
6696      */
6697     renderCellObject : function(r)
6698     {
6699         var _this = this;
6700         
6701         var t = r.cfg.render(r.container);
6702         
6703         if(r.cfg.cn){
6704             Roo.each(r.cfg.cn, function(c){
6705                 var child = {
6706                     container: t.getChildContainer(),
6707                     cfg: c
6708                 };
6709                 _this.renderCellObject(child);
6710             })
6711         }
6712     },
6713     
6714     getRowIndex : function(row)
6715     {
6716         var rowIndex = -1;
6717         
6718         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6719             if(el != row){
6720                 return;
6721             }
6722             
6723             rowIndex = index;
6724         });
6725         
6726         return rowIndex;
6727     },
6728      /**
6729      * Returns the grid's underlying element = used by panel.Grid
6730      * @return {Element} The element
6731      */
6732     getGridEl : function(){
6733         return this.el;
6734     },
6735      /**
6736      * Forces a resize - used by panel.Grid
6737      * @return {Element} The element
6738      */
6739     autoSize : function()
6740     {
6741         //var ctr = Roo.get(this.container.dom.parentElement);
6742         var ctr = Roo.get(this.el.dom);
6743         
6744         var thd = this.getGridEl().select('thead',true).first();
6745         var tbd = this.getGridEl().select('tbody', true).first();
6746         var tfd = this.getGridEl().select('tfoot', true).first();
6747         
6748         var cw = ctr.getWidth();
6749         
6750         if (tbd) {
6751             
6752             tbd.setSize(ctr.getWidth(),
6753                         ctr.getHeight() - (thd.getHeight() + (tfd ? tfd.getHeight() : 0))
6754             );
6755             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6756             cw -= barsize;
6757         }
6758         cw = Math.max(cw, this.totalWidth);
6759         this.getGridEl().select('tr',true).setWidth(cw);
6760         // resize 'expandable coloumn?
6761         
6762         return; // we doe not have a view in this design..
6763         
6764     },
6765     onBodyScroll: function()
6766     {
6767         
6768         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6769         this.mainHead.setStyle({
6770                     'position' : 'relative',
6771                     'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6772         });
6773         
6774         
6775     }
6776 });
6777
6778  
6779
6780  /*
6781  * - LGPL
6782  *
6783  * table cell
6784  * 
6785  */
6786
6787 /**
6788  * @class Roo.bootstrap.TableCell
6789  * @extends Roo.bootstrap.Component
6790  * Bootstrap TableCell class
6791  * @cfg {String} html cell contain text
6792  * @cfg {String} cls cell class
6793  * @cfg {String} tag cell tag (td|th) default td
6794  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6795  * @cfg {String} align Aligns the content in a cell
6796  * @cfg {String} axis Categorizes cells
6797  * @cfg {String} bgcolor Specifies the background color of a cell
6798  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6799  * @cfg {Number} colspan Specifies the number of columns a cell should span
6800  * @cfg {String} headers Specifies one or more header cells a cell is related to
6801  * @cfg {Number} height Sets the height of a cell
6802  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6803  * @cfg {Number} rowspan Sets the number of rows a cell should span
6804  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6805  * @cfg {String} valign Vertical aligns the content in a cell
6806  * @cfg {Number} width Specifies the width of a cell
6807  * 
6808  * @constructor
6809  * Create a new TableCell
6810  * @param {Object} config The config object
6811  */
6812
6813 Roo.bootstrap.TableCell = function(config){
6814     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6815 };
6816
6817 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6818     
6819     html: false,
6820     cls: false,
6821     tag: false,
6822     abbr: false,
6823     align: false,
6824     axis: false,
6825     bgcolor: false,
6826     charoff: false,
6827     colspan: false,
6828     headers: false,
6829     height: false,
6830     nowrap: false,
6831     rowspan: false,
6832     scope: false,
6833     valign: false,
6834     width: false,
6835     
6836     
6837     getAutoCreate : function(){
6838         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6839         
6840         cfg = {
6841             tag: 'td'
6842         };
6843         
6844         if(this.tag){
6845             cfg.tag = this.tag;
6846         }
6847         
6848         if (this.html) {
6849             cfg.html=this.html
6850         }
6851         if (this.cls) {
6852             cfg.cls=this.cls
6853         }
6854         if (this.abbr) {
6855             cfg.abbr=this.abbr
6856         }
6857         if (this.align) {
6858             cfg.align=this.align
6859         }
6860         if (this.axis) {
6861             cfg.axis=this.axis
6862         }
6863         if (this.bgcolor) {
6864             cfg.bgcolor=this.bgcolor
6865         }
6866         if (this.charoff) {
6867             cfg.charoff=this.charoff
6868         }
6869         if (this.colspan) {
6870             cfg.colspan=this.colspan
6871         }
6872         if (this.headers) {
6873             cfg.headers=this.headers
6874         }
6875         if (this.height) {
6876             cfg.height=this.height
6877         }
6878         if (this.nowrap) {
6879             cfg.nowrap=this.nowrap
6880         }
6881         if (this.rowspan) {
6882             cfg.rowspan=this.rowspan
6883         }
6884         if (this.scope) {
6885             cfg.scope=this.scope
6886         }
6887         if (this.valign) {
6888             cfg.valign=this.valign
6889         }
6890         if (this.width) {
6891             cfg.width=this.width
6892         }
6893         
6894         
6895         return cfg;
6896     }
6897    
6898 });
6899
6900  
6901
6902  /*
6903  * - LGPL
6904  *
6905  * table row
6906  * 
6907  */
6908
6909 /**
6910  * @class Roo.bootstrap.TableRow
6911  * @extends Roo.bootstrap.Component
6912  * Bootstrap TableRow class
6913  * @cfg {String} cls row class
6914  * @cfg {String} align Aligns the content in a table row
6915  * @cfg {String} bgcolor Specifies a background color for a table row
6916  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6917  * @cfg {String} valign Vertical aligns the content in a table row
6918  * 
6919  * @constructor
6920  * Create a new TableRow
6921  * @param {Object} config The config object
6922  */
6923
6924 Roo.bootstrap.TableRow = function(config){
6925     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6926 };
6927
6928 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6929     
6930     cls: false,
6931     align: false,
6932     bgcolor: false,
6933     charoff: false,
6934     valign: false,
6935     
6936     getAutoCreate : function(){
6937         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6938         
6939         cfg = {
6940             tag: 'tr'
6941         };
6942             
6943         if(this.cls){
6944             cfg.cls = this.cls;
6945         }
6946         if(this.align){
6947             cfg.align = this.align;
6948         }
6949         if(this.bgcolor){
6950             cfg.bgcolor = this.bgcolor;
6951         }
6952         if(this.charoff){
6953             cfg.charoff = this.charoff;
6954         }
6955         if(this.valign){
6956             cfg.valign = this.valign;
6957         }
6958         
6959         return cfg;
6960     }
6961    
6962 });
6963
6964  
6965
6966  /*
6967  * - LGPL
6968  *
6969  * table body
6970  * 
6971  */
6972
6973 /**
6974  * @class Roo.bootstrap.TableBody
6975  * @extends Roo.bootstrap.Component
6976  * Bootstrap TableBody class
6977  * @cfg {String} cls element class
6978  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6979  * @cfg {String} align Aligns the content inside the element
6980  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6981  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6982  * 
6983  * @constructor
6984  * Create a new TableBody
6985  * @param {Object} config The config object
6986  */
6987
6988 Roo.bootstrap.TableBody = function(config){
6989     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6990 };
6991
6992 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6993     
6994     cls: false,
6995     tag: false,
6996     align: false,
6997     charoff: false,
6998     valign: false,
6999     
7000     getAutoCreate : function(){
7001         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7002         
7003         cfg = {
7004             tag: 'tbody'
7005         };
7006             
7007         if (this.cls) {
7008             cfg.cls=this.cls
7009         }
7010         if(this.tag){
7011             cfg.tag = this.tag;
7012         }
7013         
7014         if(this.align){
7015             cfg.align = this.align;
7016         }
7017         if(this.charoff){
7018             cfg.charoff = this.charoff;
7019         }
7020         if(this.valign){
7021             cfg.valign = this.valign;
7022         }
7023         
7024         return cfg;
7025     }
7026     
7027     
7028 //    initEvents : function()
7029 //    {
7030 //        
7031 //        if(!this.store){
7032 //            return;
7033 //        }
7034 //        
7035 //        this.store = Roo.factory(this.store, Roo.data);
7036 //        this.store.on('load', this.onLoad, this);
7037 //        
7038 //        this.store.load();
7039 //        
7040 //    },
7041 //    
7042 //    onLoad: function () 
7043 //    {   
7044 //        this.fireEvent('load', this);
7045 //    }
7046 //    
7047 //   
7048 });
7049
7050  
7051
7052  /*
7053  * Based on:
7054  * Ext JS Library 1.1.1
7055  * Copyright(c) 2006-2007, Ext JS, LLC.
7056  *
7057  * Originally Released Under LGPL - original licence link has changed is not relivant.
7058  *
7059  * Fork - LGPL
7060  * <script type="text/javascript">
7061  */
7062
7063 // as we use this in bootstrap.
7064 Roo.namespace('Roo.form');
7065  /**
7066  * @class Roo.form.Action
7067  * Internal Class used to handle form actions
7068  * @constructor
7069  * @param {Roo.form.BasicForm} el The form element or its id
7070  * @param {Object} config Configuration options
7071  */
7072
7073  
7074  
7075 // define the action interface
7076 Roo.form.Action = function(form, options){
7077     this.form = form;
7078     this.options = options || {};
7079 };
7080 /**
7081  * Client Validation Failed
7082  * @const 
7083  */
7084 Roo.form.Action.CLIENT_INVALID = 'client';
7085 /**
7086  * Server Validation Failed
7087  * @const 
7088  */
7089 Roo.form.Action.SERVER_INVALID = 'server';
7090  /**
7091  * Connect to Server Failed
7092  * @const 
7093  */
7094 Roo.form.Action.CONNECT_FAILURE = 'connect';
7095 /**
7096  * Reading Data from Server Failed
7097  * @const 
7098  */
7099 Roo.form.Action.LOAD_FAILURE = 'load';
7100
7101 Roo.form.Action.prototype = {
7102     type : 'default',
7103     failureType : undefined,
7104     response : undefined,
7105     result : undefined,
7106
7107     // interface method
7108     run : function(options){
7109
7110     },
7111
7112     // interface method
7113     success : function(response){
7114
7115     },
7116
7117     // interface method
7118     handleResponse : function(response){
7119
7120     },
7121
7122     // default connection failure
7123     failure : function(response){
7124         
7125         this.response = response;
7126         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7127         this.form.afterAction(this, false);
7128     },
7129
7130     processResponse : function(response){
7131         this.response = response;
7132         if(!response.responseText){
7133             return true;
7134         }
7135         this.result = this.handleResponse(response);
7136         return this.result;
7137     },
7138
7139     // utility functions used internally
7140     getUrl : function(appendParams){
7141         var url = this.options.url || this.form.url || this.form.el.dom.action;
7142         if(appendParams){
7143             var p = this.getParams();
7144             if(p){
7145                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7146             }
7147         }
7148         return url;
7149     },
7150
7151     getMethod : function(){
7152         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7153     },
7154
7155     getParams : function(){
7156         var bp = this.form.baseParams;
7157         var p = this.options.params;
7158         if(p){
7159             if(typeof p == "object"){
7160                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7161             }else if(typeof p == 'string' && bp){
7162                 p += '&' + Roo.urlEncode(bp);
7163             }
7164         }else if(bp){
7165             p = Roo.urlEncode(bp);
7166         }
7167         return p;
7168     },
7169
7170     createCallback : function(){
7171         return {
7172             success: this.success,
7173             failure: this.failure,
7174             scope: this,
7175             timeout: (this.form.timeout*1000),
7176             upload: this.form.fileUpload ? this.success : undefined
7177         };
7178     }
7179 };
7180
7181 Roo.form.Action.Submit = function(form, options){
7182     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7183 };
7184
7185 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7186     type : 'submit',
7187
7188     haveProgress : false,
7189     uploadComplete : false,
7190     
7191     // uploadProgress indicator.
7192     uploadProgress : function()
7193     {
7194         if (!this.form.progressUrl) {
7195             return;
7196         }
7197         
7198         if (!this.haveProgress) {
7199             Roo.MessageBox.progress("Uploading", "Uploading");
7200         }
7201         if (this.uploadComplete) {
7202            Roo.MessageBox.hide();
7203            return;
7204         }
7205         
7206         this.haveProgress = true;
7207    
7208         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7209         
7210         var c = new Roo.data.Connection();
7211         c.request({
7212             url : this.form.progressUrl,
7213             params: {
7214                 id : uid
7215             },
7216             method: 'GET',
7217             success : function(req){
7218                //console.log(data);
7219                 var rdata = false;
7220                 var edata;
7221                 try  {
7222                    rdata = Roo.decode(req.responseText)
7223                 } catch (e) {
7224                     Roo.log("Invalid data from server..");
7225                     Roo.log(edata);
7226                     return;
7227                 }
7228                 if (!rdata || !rdata.success) {
7229                     Roo.log(rdata);
7230                     Roo.MessageBox.alert(Roo.encode(rdata));
7231                     return;
7232                 }
7233                 var data = rdata.data;
7234                 
7235                 if (this.uploadComplete) {
7236                    Roo.MessageBox.hide();
7237                    return;
7238                 }
7239                    
7240                 if (data){
7241                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7242                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7243                     );
7244                 }
7245                 this.uploadProgress.defer(2000,this);
7246             },
7247        
7248             failure: function(data) {
7249                 Roo.log('progress url failed ');
7250                 Roo.log(data);
7251             },
7252             scope : this
7253         });
7254            
7255     },
7256     
7257     
7258     run : function()
7259     {
7260         // run get Values on the form, so it syncs any secondary forms.
7261         this.form.getValues();
7262         
7263         var o = this.options;
7264         var method = this.getMethod();
7265         var isPost = method == 'POST';
7266         if(o.clientValidation === false || this.form.isValid()){
7267             
7268             if (this.form.progressUrl) {
7269                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7270                     (new Date() * 1) + '' + Math.random());
7271                     
7272             } 
7273             
7274             
7275             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7276                 form:this.form.el.dom,
7277                 url:this.getUrl(!isPost),
7278                 method: method,
7279                 params:isPost ? this.getParams() : null,
7280                 isUpload: this.form.fileUpload
7281             }));
7282             
7283             this.uploadProgress();
7284
7285         }else if (o.clientValidation !== false){ // client validation failed
7286             this.failureType = Roo.form.Action.CLIENT_INVALID;
7287             this.form.afterAction(this, false);
7288         }
7289     },
7290
7291     success : function(response)
7292     {
7293         this.uploadComplete= true;
7294         if (this.haveProgress) {
7295             Roo.MessageBox.hide();
7296         }
7297         
7298         
7299         var result = this.processResponse(response);
7300         if(result === true || result.success){
7301             this.form.afterAction(this, true);
7302             return;
7303         }
7304         if(result.errors){
7305             this.form.markInvalid(result.errors);
7306             this.failureType = Roo.form.Action.SERVER_INVALID;
7307         }
7308         this.form.afterAction(this, false);
7309     },
7310     failure : function(response)
7311     {
7312         this.uploadComplete= true;
7313         if (this.haveProgress) {
7314             Roo.MessageBox.hide();
7315         }
7316         
7317         this.response = response;
7318         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7319         this.form.afterAction(this, false);
7320     },
7321     
7322     handleResponse : function(response){
7323         if(this.form.errorReader){
7324             var rs = this.form.errorReader.read(response);
7325             var errors = [];
7326             if(rs.records){
7327                 for(var i = 0, len = rs.records.length; i < len; i++) {
7328                     var r = rs.records[i];
7329                     errors[i] = r.data;
7330                 }
7331             }
7332             if(errors.length < 1){
7333                 errors = null;
7334             }
7335             return {
7336                 success : rs.success,
7337                 errors : errors
7338             };
7339         }
7340         var ret = false;
7341         try {
7342             ret = Roo.decode(response.responseText);
7343         } catch (e) {
7344             ret = {
7345                 success: false,
7346                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7347                 errors : []
7348             };
7349         }
7350         return ret;
7351         
7352     }
7353 });
7354
7355
7356 Roo.form.Action.Load = function(form, options){
7357     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7358     this.reader = this.form.reader;
7359 };
7360
7361 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7362     type : 'load',
7363
7364     run : function(){
7365         
7366         Roo.Ajax.request(Roo.apply(
7367                 this.createCallback(), {
7368                     method:this.getMethod(),
7369                     url:this.getUrl(false),
7370                     params:this.getParams()
7371         }));
7372     },
7373
7374     success : function(response){
7375         
7376         var result = this.processResponse(response);
7377         if(result === true || !result.success || !result.data){
7378             this.failureType = Roo.form.Action.LOAD_FAILURE;
7379             this.form.afterAction(this, false);
7380             return;
7381         }
7382         this.form.clearInvalid();
7383         this.form.setValues(result.data);
7384         this.form.afterAction(this, true);
7385     },
7386
7387     handleResponse : function(response){
7388         if(this.form.reader){
7389             var rs = this.form.reader.read(response);
7390             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7391             return {
7392                 success : rs.success,
7393                 data : data
7394             };
7395         }
7396         return Roo.decode(response.responseText);
7397     }
7398 });
7399
7400 Roo.form.Action.ACTION_TYPES = {
7401     'load' : Roo.form.Action.Load,
7402     'submit' : Roo.form.Action.Submit
7403 };/*
7404  * - LGPL
7405  *
7406  * form
7407  *
7408  */
7409
7410 /**
7411  * @class Roo.bootstrap.Form
7412  * @extends Roo.bootstrap.Component
7413  * Bootstrap Form class
7414  * @cfg {String} method  GET | POST (default POST)
7415  * @cfg {String} labelAlign top | left (default top)
7416  * @cfg {String} align left  | right - for navbars
7417  * @cfg {Boolean} loadMask load mask when submit (default true)
7418
7419  *
7420  * @constructor
7421  * Create a new Form
7422  * @param {Object} config The config object
7423  */
7424
7425
7426 Roo.bootstrap.Form = function(config){
7427     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7428     this.addEvents({
7429         /**
7430          * @event clientvalidation
7431          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7432          * @param {Form} this
7433          * @param {Boolean} valid true if the form has passed client-side validation
7434          */
7435         clientvalidation: true,
7436         /**
7437          * @event beforeaction
7438          * Fires before any action is performed. Return false to cancel the action.
7439          * @param {Form} this
7440          * @param {Action} action The action to be performed
7441          */
7442         beforeaction: true,
7443         /**
7444          * @event actionfailed
7445          * Fires when an action fails.
7446          * @param {Form} this
7447          * @param {Action} action The action that failed
7448          */
7449         actionfailed : true,
7450         /**
7451          * @event actioncomplete
7452          * Fires when an action is completed.
7453          * @param {Form} this
7454          * @param {Action} action The action that completed
7455          */
7456         actioncomplete : true
7457     });
7458
7459 };
7460
7461 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7462
7463      /**
7464      * @cfg {String} method
7465      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7466      */
7467     method : 'POST',
7468     /**
7469      * @cfg {String} url
7470      * The URL to use for form actions if one isn't supplied in the action options.
7471      */
7472     /**
7473      * @cfg {Boolean} fileUpload
7474      * Set to true if this form is a file upload.
7475      */
7476
7477     /**
7478      * @cfg {Object} baseParams
7479      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7480      */
7481
7482     /**
7483      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7484      */
7485     timeout: 30,
7486     /**
7487      * @cfg {Sting} align (left|right) for navbar forms
7488      */
7489     align : 'left',
7490
7491     // private
7492     activeAction : null,
7493
7494     /**
7495      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7496      * element by passing it or its id or mask the form itself by passing in true.
7497      * @type Mixed
7498      */
7499     waitMsgTarget : false,
7500
7501     loadMask : true,
7502
7503     getAutoCreate : function(){
7504
7505         var cfg = {
7506             tag: 'form',
7507             method : this.method || 'POST',
7508             id : this.id || Roo.id(),
7509             cls : ''
7510         };
7511         if (this.parent().xtype.match(/^Nav/)) {
7512             cfg.cls = 'navbar-form navbar-' + this.align;
7513
7514         }
7515
7516         if (this.labelAlign == 'left' ) {
7517             cfg.cls += ' form-horizontal';
7518         }
7519
7520
7521         return cfg;
7522     },
7523     initEvents : function()
7524     {
7525         this.el.on('submit', this.onSubmit, this);
7526         // this was added as random key presses on the form where triggering form submit.
7527         this.el.on('keypress', function(e) {
7528             if (e.getCharCode() != 13) {
7529                 return true;
7530             }
7531             // we might need to allow it for textareas.. and some other items.
7532             // check e.getTarget().
7533
7534             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7535                 return true;
7536             }
7537
7538             Roo.log("keypress blocked");
7539
7540             e.preventDefault();
7541             return false;
7542         });
7543
7544     },
7545     // private
7546     onSubmit : function(e){
7547         e.stopEvent();
7548     },
7549
7550      /**
7551      * Returns true if client-side validation on the form is successful.
7552      * @return Boolean
7553      */
7554     isValid : function(){
7555         var items = this.getItems();
7556         var valid = true;
7557         items.each(function(f){
7558            if(!f.validate()){
7559                valid = false;
7560
7561            }
7562         });
7563         return valid;
7564     },
7565     /**
7566      * Returns true if any fields in this form have changed since their original load.
7567      * @return Boolean
7568      */
7569     isDirty : function(){
7570         var dirty = false;
7571         var items = this.getItems();
7572         items.each(function(f){
7573            if(f.isDirty()){
7574                dirty = true;
7575                return false;
7576            }
7577            return true;
7578         });
7579         return dirty;
7580     },
7581      /**
7582      * Performs a predefined action (submit or load) or custom actions you define on this form.
7583      * @param {String} actionName The name of the action type
7584      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7585      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7586      * accept other config options):
7587      * <pre>
7588 Property          Type             Description
7589 ----------------  ---------------  ----------------------------------------------------------------------------------
7590 url               String           The url for the action (defaults to the form's url)
7591 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7592 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7593 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7594                                    validate the form on the client (defaults to false)
7595      * </pre>
7596      * @return {BasicForm} this
7597      */
7598     doAction : function(action, options){
7599         if(typeof action == 'string'){
7600             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7601         }
7602         if(this.fireEvent('beforeaction', this, action) !== false){
7603             this.beforeAction(action);
7604             action.run.defer(100, action);
7605         }
7606         return this;
7607     },
7608
7609     // private
7610     beforeAction : function(action){
7611         var o = action.options;
7612
7613         if(this.loadMask){
7614             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7615         }
7616         // not really supported yet.. ??
7617
7618         //if(this.waitMsgTarget === true){
7619         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7620         //}else if(this.waitMsgTarget){
7621         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7622         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7623         //}else {
7624         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7625        // }
7626
7627     },
7628
7629     // private
7630     afterAction : function(action, success){
7631         this.activeAction = null;
7632         var o = action.options;
7633
7634         //if(this.waitMsgTarget === true){
7635             this.el.unmask();
7636         //}else if(this.waitMsgTarget){
7637         //    this.waitMsgTarget.unmask();
7638         //}else{
7639         //    Roo.MessageBox.updateProgress(1);
7640         //    Roo.MessageBox.hide();
7641        // }
7642         //
7643         if(success){
7644             if(o.reset){
7645                 this.reset();
7646             }
7647             Roo.callback(o.success, o.scope, [this, action]);
7648             this.fireEvent('actioncomplete', this, action);
7649
7650         }else{
7651
7652             // failure condition..
7653             // we have a scenario where updates need confirming.
7654             // eg. if a locking scenario exists..
7655             // we look for { errors : { needs_confirm : true }} in the response.
7656             if (
7657                 (typeof(action.result) != 'undefined')  &&
7658                 (typeof(action.result.errors) != 'undefined')  &&
7659                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7660            ){
7661                 var _t = this;
7662                 Roo.log("not supported yet");
7663                  /*
7664
7665                 Roo.MessageBox.confirm(
7666                     "Change requires confirmation",
7667                     action.result.errorMsg,
7668                     function(r) {
7669                         if (r != 'yes') {
7670                             return;
7671                         }
7672                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7673                     }
7674
7675                 );
7676                 */
7677
7678
7679                 return;
7680             }
7681
7682             Roo.callback(o.failure, o.scope, [this, action]);
7683             // show an error message if no failed handler is set..
7684             if (!this.hasListener('actionfailed')) {
7685                 Roo.log("need to add dialog support");
7686                 /*
7687                 Roo.MessageBox.alert("Error",
7688                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7689                         action.result.errorMsg :
7690                         "Saving Failed, please check your entries or try again"
7691                 );
7692                 */
7693             }
7694
7695             this.fireEvent('actionfailed', this, action);
7696         }
7697
7698     },
7699     /**
7700      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7701      * @param {String} id The value to search for
7702      * @return Field
7703      */
7704     findField : function(id){
7705         var items = this.getItems();
7706         var field = items.get(id);
7707         if(!field){
7708              items.each(function(f){
7709                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7710                     field = f;
7711                     return false;
7712                 }
7713                 return true;
7714             });
7715         }
7716         return field || null;
7717     },
7718      /**
7719      * Mark fields in this form invalid in bulk.
7720      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7721      * @return {BasicForm} this
7722      */
7723     markInvalid : function(errors){
7724         if(errors instanceof Array){
7725             for(var i = 0, len = errors.length; i < len; i++){
7726                 var fieldError = errors[i];
7727                 var f = this.findField(fieldError.id);
7728                 if(f){
7729                     f.markInvalid(fieldError.msg);
7730                 }
7731             }
7732         }else{
7733             var field, id;
7734             for(id in errors){
7735                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7736                     field.markInvalid(errors[id]);
7737                 }
7738             }
7739         }
7740         //Roo.each(this.childForms || [], function (f) {
7741         //    f.markInvalid(errors);
7742         //});
7743
7744         return this;
7745     },
7746
7747     /**
7748      * Set values for fields in this form in bulk.
7749      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7750      * @return {BasicForm} this
7751      */
7752     setValues : function(values){
7753         if(values instanceof Array){ // array of objects
7754             for(var i = 0, len = values.length; i < len; i++){
7755                 var v = values[i];
7756                 var f = this.findField(v.id);
7757                 if(f){
7758                     f.setValue(v.value);
7759                     if(this.trackResetOnLoad){
7760                         f.originalValue = f.getValue();
7761                     }
7762                 }
7763             }
7764         }else{ // object hash
7765             var field, id;
7766             for(id in values){
7767                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7768
7769                     if (field.setFromData &&
7770                         field.valueField &&
7771                         field.displayField &&
7772                         // combos' with local stores can
7773                         // be queried via setValue()
7774                         // to set their value..
7775                         (field.store && !field.store.isLocal)
7776                         ) {
7777                         // it's a combo
7778                         var sd = { };
7779                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7780                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7781                         field.setFromData(sd);
7782
7783                     } else {
7784                         field.setValue(values[id]);
7785                     }
7786
7787
7788                     if(this.trackResetOnLoad){
7789                         field.originalValue = field.getValue();
7790                     }
7791                 }
7792             }
7793         }
7794
7795         //Roo.each(this.childForms || [], function (f) {
7796         //    f.setValues(values);
7797         //});
7798
7799         return this;
7800     },
7801
7802     /**
7803      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7804      * they are returned as an array.
7805      * @param {Boolean} asString
7806      * @return {Object}
7807      */
7808     getValues : function(asString){
7809         //if (this.childForms) {
7810             // copy values from the child forms
7811         //    Roo.each(this.childForms, function (f) {
7812         //        this.setValues(f.getValues());
7813         //    }, this);
7814         //}
7815
7816
7817
7818         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7819         if(asString === true){
7820             return fs;
7821         }
7822         return Roo.urlDecode(fs);
7823     },
7824
7825     /**
7826      * Returns the fields in this form as an object with key/value pairs.
7827      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7828      * @return {Object}
7829      */
7830     getFieldValues : function(with_hidden)
7831     {
7832         var items = this.getItems();
7833         var ret = {};
7834         items.each(function(f){
7835             if (!f.getName()) {
7836                 return;
7837             }
7838             var v = f.getValue();
7839             if (f.inputType =='radio') {
7840                 if (typeof(ret[f.getName()]) == 'undefined') {
7841                     ret[f.getName()] = ''; // empty..
7842                 }
7843
7844                 if (!f.el.dom.checked) {
7845                     return;
7846
7847                 }
7848                 v = f.el.dom.value;
7849
7850             }
7851
7852             // not sure if this supported any more..
7853             if ((typeof(v) == 'object') && f.getRawValue) {
7854                 v = f.getRawValue() ; // dates..
7855             }
7856             // combo boxes where name != hiddenName...
7857             if (f.name != f.getName()) {
7858                 ret[f.name] = f.getRawValue();
7859             }
7860             ret[f.getName()] = v;
7861         });
7862
7863         return ret;
7864     },
7865
7866     /**
7867      * Clears all invalid messages in this form.
7868      * @return {BasicForm} this
7869      */
7870     clearInvalid : function(){
7871         var items = this.getItems();
7872
7873         items.each(function(f){
7874            f.clearInvalid();
7875         });
7876
7877
7878
7879         return this;
7880     },
7881
7882     /**
7883      * Resets this form.
7884      * @return {BasicForm} this
7885      */
7886     reset : function(){
7887         var items = this.getItems();
7888         items.each(function(f){
7889             f.reset();
7890         });
7891
7892         Roo.each(this.childForms || [], function (f) {
7893             f.reset();
7894         });
7895
7896
7897         return this;
7898     },
7899     getItems : function()
7900     {
7901         var r=new Roo.util.MixedCollection(false, function(o){
7902             return o.id || (o.id = Roo.id());
7903         });
7904         var iter = function(el) {
7905             if (el.inputEl) {
7906                 r.add(el);
7907             }
7908             if (!el.items) {
7909                 return;
7910             }
7911             Roo.each(el.items,function(e) {
7912                 iter(e);
7913             });
7914
7915
7916         };
7917
7918         iter(this);
7919         return r;
7920
7921
7922
7923
7924     }
7925
7926 });
7927 /*
7928  * Based on:
7929  * Ext JS Library 1.1.1
7930  * Copyright(c) 2006-2007, Ext JS, LLC.
7931  *
7932  * Originally Released Under LGPL - original licence link has changed is not relivant.
7933  *
7934  * Fork - LGPL
7935  * <script type="text/javascript">
7936  */
7937 /**
7938  * @class Roo.form.VTypes
7939  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7940  * @singleton
7941  */
7942 Roo.form.VTypes = function(){
7943     // closure these in so they are only created once.
7944     var alpha = /^[a-zA-Z_]+$/;
7945     var alphanum = /^[a-zA-Z0-9_]+$/;
7946     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7947     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7948
7949     // All these messages and functions are configurable
7950     return {
7951         /**
7952          * The function used to validate email addresses
7953          * @param {String} value The email address
7954          */
7955         'email' : function(v){
7956             return email.test(v);
7957         },
7958         /**
7959          * The error text to display when the email validation function returns false
7960          * @type String
7961          */
7962         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7963         /**
7964          * The keystroke filter mask to be applied on email input
7965          * @type RegExp
7966          */
7967         'emailMask' : /[a-z0-9_\.\-@]/i,
7968
7969         /**
7970          * The function used to validate URLs
7971          * @param {String} value The URL
7972          */
7973         'url' : function(v){
7974             return url.test(v);
7975         },
7976         /**
7977          * The error text to display when the url validation function returns false
7978          * @type String
7979          */
7980         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7981         
7982         /**
7983          * The function used to validate alpha values
7984          * @param {String} value The value
7985          */
7986         'alpha' : function(v){
7987             return alpha.test(v);
7988         },
7989         /**
7990          * The error text to display when the alpha validation function returns false
7991          * @type String
7992          */
7993         'alphaText' : 'This field should only contain letters and _',
7994         /**
7995          * The keystroke filter mask to be applied on alpha input
7996          * @type RegExp
7997          */
7998         'alphaMask' : /[a-z_]/i,
7999
8000         /**
8001          * The function used to validate alphanumeric values
8002          * @param {String} value The value
8003          */
8004         'alphanum' : function(v){
8005             return alphanum.test(v);
8006         },
8007         /**
8008          * The error text to display when the alphanumeric validation function returns false
8009          * @type String
8010          */
8011         'alphanumText' : 'This field should only contain letters, numbers and _',
8012         /**
8013          * The keystroke filter mask to be applied on alphanumeric input
8014          * @type RegExp
8015          */
8016         'alphanumMask' : /[a-z0-9_]/i
8017     };
8018 }();/*
8019  * - LGPL
8020  *
8021  * Input
8022  * 
8023  */
8024
8025 /**
8026  * @class Roo.bootstrap.Input
8027  * @extends Roo.bootstrap.Component
8028  * Bootstrap Input class
8029  * @cfg {Boolean} disabled is it disabled
8030  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8031  * @cfg {String} name name of the input
8032  * @cfg {string} fieldLabel - the label associated
8033  * @cfg {string} placeholder - placeholder to put in text.
8034  * @cfg {string}  before - input group add on before
8035  * @cfg {string} after - input group add on after
8036  * @cfg {string} size - (lg|sm) or leave empty..
8037  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8038  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8039  * @cfg {Number} md colspan out of 12 for computer-sized screens
8040  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8041  * @cfg {string} value default value of the input
8042  * @cfg {Number} labelWidth set the width of label (0-12)
8043  * @cfg {String} labelAlign (top|left)
8044  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8045  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8046  * @cfg {String} indicatorpos (left|right) default left
8047
8048  * @cfg {String} align (left|center|right) Default left
8049  * @cfg {Boolean} forceFeedback (true|false) Default false
8050  * 
8051  * 
8052  * 
8053  * 
8054  * @constructor
8055  * Create a new Input
8056  * @param {Object} config The config object
8057  */
8058
8059 Roo.bootstrap.Input = function(config){
8060     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8061    
8062         this.addEvents({
8063             /**
8064              * @event focus
8065              * Fires when this field receives input focus.
8066              * @param {Roo.form.Field} this
8067              */
8068             focus : true,
8069             /**
8070              * @event blur
8071              * Fires when this field loses input focus.
8072              * @param {Roo.form.Field} this
8073              */
8074             blur : true,
8075             /**
8076              * @event specialkey
8077              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8078              * {@link Roo.EventObject#getKey} to determine which key was pressed.
8079              * @param {Roo.form.Field} this
8080              * @param {Roo.EventObject} e The event object
8081              */
8082             specialkey : true,
8083             /**
8084              * @event change
8085              * Fires just before the field blurs if the field value has changed.
8086              * @param {Roo.form.Field} this
8087              * @param {Mixed} newValue The new value
8088              * @param {Mixed} oldValue The original value
8089              */
8090             change : true,
8091             /**
8092              * @event invalid
8093              * Fires after the field has been marked as invalid.
8094              * @param {Roo.form.Field} this
8095              * @param {String} msg The validation message
8096              */
8097             invalid : true,
8098             /**
8099              * @event valid
8100              * Fires after the field has been validated with no errors.
8101              * @param {Roo.form.Field} this
8102              */
8103             valid : true,
8104              /**
8105              * @event keyup
8106              * Fires after the key up
8107              * @param {Roo.form.Field} this
8108              * @param {Roo.EventObject}  e The event Object
8109              */
8110             keyup : true
8111         });
8112 };
8113
8114 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8115      /**
8116      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8117       automatic validation (defaults to "keyup").
8118      */
8119     validationEvent : "keyup",
8120      /**
8121      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8122      */
8123     validateOnBlur : true,
8124     /**
8125      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8126      */
8127     validationDelay : 250,
8128      /**
8129      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8130      */
8131     focusClass : "x-form-focus",  // not needed???
8132     
8133        
8134     /**
8135      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8136      */
8137     invalidClass : "has-warning",
8138     
8139     /**
8140      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8141      */
8142     validClass : "has-success",
8143     
8144     /**
8145      * @cfg {Boolean} hasFeedback (true|false) default true
8146      */
8147     hasFeedback : true,
8148     
8149     /**
8150      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8151      */
8152     invalidFeedbackClass : "glyphicon-warning-sign",
8153     
8154     /**
8155      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8156      */
8157     validFeedbackClass : "glyphicon-ok",
8158     
8159     /**
8160      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8161      */
8162     selectOnFocus : false,
8163     
8164      /**
8165      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8166      */
8167     maskRe : null,
8168        /**
8169      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8170      */
8171     vtype : null,
8172     
8173       /**
8174      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8175      */
8176     disableKeyFilter : false,
8177     
8178        /**
8179      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8180      */
8181     disabled : false,
8182      /**
8183      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8184      */
8185     allowBlank : true,
8186     /**
8187      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8188      */
8189     blankText : "This field is required",
8190     
8191      /**
8192      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8193      */
8194     minLength : 0,
8195     /**
8196      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8197      */
8198     maxLength : Number.MAX_VALUE,
8199     /**
8200      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8201      */
8202     minLengthText : "The minimum length for this field is {0}",
8203     /**
8204      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8205      */
8206     maxLengthText : "The maximum length for this field is {0}",
8207   
8208     
8209     /**
8210      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8211      * If available, this function will be called only after the basic validators all return true, and will be passed the
8212      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8213      */
8214     validator : null,
8215     /**
8216      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8217      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8218      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8219      */
8220     regex : null,
8221     /**
8222      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8223      */
8224     regexText : "",
8225     
8226     autocomplete: false,
8227     
8228     
8229     fieldLabel : '',
8230     inputType : 'text',
8231     
8232     name : false,
8233     placeholder: false,
8234     before : false,
8235     after : false,
8236     size : false,
8237     hasFocus : false,
8238     preventMark: false,
8239     isFormField : true,
8240     value : '',
8241     labelWidth : 2,
8242     labelAlign : false,
8243     readOnly : false,
8244     align : false,
8245     formatedValue : false,
8246     forceFeedback : false,
8247     
8248     indicatorpos : 'left',
8249     
8250     parentLabelAlign : function()
8251     {
8252         var parent = this;
8253         while (parent.parent()) {
8254             parent = parent.parent();
8255             if (typeof(parent.labelAlign) !='undefined') {
8256                 return parent.labelAlign;
8257             }
8258         }
8259         return 'left';
8260         
8261     },
8262     
8263     getAutoCreate : function()
8264     {
8265         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8266         
8267         var id = Roo.id();
8268         
8269         var cfg = {};
8270         
8271         if(this.inputType != 'hidden'){
8272             cfg.cls = 'form-group' //input-group
8273         }
8274         
8275         var input =  {
8276             tag: 'input',
8277             id : id,
8278             type : this.inputType,
8279             value : this.value,
8280             cls : 'form-control',
8281             placeholder : this.placeholder || '',
8282             autocomplete : this.autocomplete || 'new-password'
8283         };
8284         
8285         if(this.align){
8286             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8287         }
8288         
8289         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8290             input.maxLength = this.maxLength;
8291         }
8292         
8293         if (this.disabled) {
8294             input.disabled=true;
8295         }
8296         
8297         if (this.readOnly) {
8298             input.readonly=true;
8299         }
8300         
8301         if (this.name) {
8302             input.name = this.name;
8303         }
8304         
8305         if (this.size) {
8306             input.cls += ' input-' + this.size;
8307         }
8308         
8309         var settings=this;
8310         ['xs','sm','md','lg'].map(function(size){
8311             if (settings[size]) {
8312                 cfg.cls += ' col-' + size + '-' + settings[size];
8313             }
8314         });
8315         
8316         var inputblock = input;
8317         
8318         var feedback = {
8319             tag: 'span',
8320             cls: 'glyphicon form-control-feedback'
8321         };
8322             
8323         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8324             
8325             inputblock = {
8326                 cls : 'has-feedback',
8327                 cn :  [
8328                     input,
8329                     feedback
8330                 ] 
8331             };  
8332         }
8333         
8334         if (this.before || this.after) {
8335             
8336             inputblock = {
8337                 cls : 'input-group',
8338                 cn :  [] 
8339             };
8340             
8341             if (this.before && typeof(this.before) == 'string') {
8342                 
8343                 inputblock.cn.push({
8344                     tag :'span',
8345                     cls : 'roo-input-before input-group-addon',
8346                     html : this.before
8347                 });
8348             }
8349             if (this.before && typeof(this.before) == 'object') {
8350                 this.before = Roo.factory(this.before);
8351                 
8352                 inputblock.cn.push({
8353                     tag :'span',
8354                     cls : 'roo-input-before input-group-' +
8355                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8356                 });
8357             }
8358             
8359             inputblock.cn.push(input);
8360             
8361             if (this.after && typeof(this.after) == 'string') {
8362                 inputblock.cn.push({
8363                     tag :'span',
8364                     cls : 'roo-input-after input-group-addon',
8365                     html : this.after
8366                 });
8367             }
8368             if (this.after && typeof(this.after) == 'object') {
8369                 this.after = Roo.factory(this.after);
8370                 
8371                 inputblock.cn.push({
8372                     tag :'span',
8373                     cls : 'roo-input-after input-group-' +
8374                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8375                 });
8376             }
8377             
8378             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8379                 inputblock.cls += ' has-feedback';
8380                 inputblock.cn.push(feedback);
8381             }
8382         };
8383         
8384         if (align ==='left' && this.fieldLabel.length) {
8385             
8386             cfg.cn = [
8387                 {
8388                     tag : 'i',
8389                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8390                     tooltip : 'This field is required'
8391                 },
8392                 {
8393                     tag: 'label',
8394                     'for' :  id,
8395                     cls : 'control-label col-sm-' + this.labelWidth,
8396                     html : this.fieldLabel
8397
8398                 },
8399                 {
8400                     cls : "col-sm-" + (12 - this.labelWidth), 
8401                     cn: [
8402                         inputblock
8403                     ]
8404                 }
8405
8406             ];
8407             
8408             if(this.indicatorpos == 'right'){
8409                 cfg.cn = [
8410                     {
8411                         tag: 'label',
8412                         'for' :  id,
8413                         cls : 'control-label col-sm-' + this.labelWidth,
8414                         html : this.fieldLabel
8415
8416                     },
8417                     {
8418                         tag : 'i',
8419                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8420                         tooltip : 'This field is required'
8421                     },
8422                     {
8423                         cls : "col-sm-" + (12 - this.labelWidth), 
8424                         cn: [
8425                             inputblock
8426                         ]
8427                     }
8428
8429                 ];
8430             }
8431             
8432         } else if ( this.fieldLabel.length) {
8433                 
8434             cfg.cn = [
8435                 {
8436                     tag : 'i',
8437                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8438                     tooltip : 'This field is required'
8439                 },
8440                 {
8441                     tag: 'label',
8442                    //cls : 'input-group-addon',
8443                     html : this.fieldLabel
8444
8445                 },
8446
8447                inputblock
8448
8449            ];
8450            
8451            if(this.indicatorpos == 'right'){
8452                 
8453                 cfg.cn = [
8454                     {
8455                         tag: 'label',
8456                        //cls : 'input-group-addon',
8457                         html : this.fieldLabel
8458
8459                     },
8460                     {
8461                         tag : 'i',
8462                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8463                         tooltip : 'This field is required'
8464                     },
8465
8466                    inputblock
8467
8468                ];
8469
8470             }
8471
8472         } else {
8473             
8474             cfg.cn = [
8475
8476                     inputblock
8477
8478             ];
8479                 
8480                 
8481         };
8482         
8483         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8484            cfg.cls += ' navbar-form';
8485         }
8486         
8487         if (this.parentType === 'NavGroup') {
8488            cfg.cls += ' navbar-form';
8489            cfg.tag = 'li';
8490         }
8491         
8492         return cfg;
8493         
8494     },
8495     /**
8496      * return the real input element.
8497      */
8498     inputEl: function ()
8499     {
8500         return this.el.select('input.form-control',true).first();
8501     },
8502     
8503     tooltipEl : function()
8504     {
8505         return this.inputEl();
8506     },
8507     
8508     indicatorEl : function()
8509     {
8510         var indicator = this.el.select('i.roo-required-indicator',true).first();
8511         
8512         if(!indicator){
8513             return false;
8514         }
8515         
8516         return indicator;
8517         
8518     },
8519     
8520     setDisabled : function(v)
8521     {
8522         var i  = this.inputEl().dom;
8523         if (!v) {
8524             i.removeAttribute('disabled');
8525             return;
8526             
8527         }
8528         i.setAttribute('disabled','true');
8529     },
8530     initEvents : function()
8531     {
8532           
8533         this.inputEl().on("keydown" , this.fireKey,  this);
8534         this.inputEl().on("focus", this.onFocus,  this);
8535         this.inputEl().on("blur", this.onBlur,  this);
8536         
8537         this.inputEl().relayEvent('keyup', this);
8538         
8539         this.indicator = this.indicatorEl();
8540         
8541         if(this.indicator){
8542             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8543             this.indicator.hide();
8544         }
8545  
8546         // reference to original value for reset
8547         this.originalValue = this.getValue();
8548         //Roo.form.TextField.superclass.initEvents.call(this);
8549         if(this.validationEvent == 'keyup'){
8550             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8551             this.inputEl().on('keyup', this.filterValidation, this);
8552         }
8553         else if(this.validationEvent !== false){
8554             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8555         }
8556         
8557         if(this.selectOnFocus){
8558             this.on("focus", this.preFocus, this);
8559             
8560         }
8561         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8562             this.inputEl().on("keypress", this.filterKeys, this);
8563         } else {
8564             this.inputEl().relayEvent('keypress', this);
8565         }
8566        /* if(this.grow){
8567             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8568             this.el.on("click", this.autoSize,  this);
8569         }
8570         */
8571         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8572             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8573         }
8574         
8575         if (typeof(this.before) == 'object') {
8576             this.before.render(this.el.select('.roo-input-before',true).first());
8577         }
8578         if (typeof(this.after) == 'object') {
8579             this.after.render(this.el.select('.roo-input-after',true).first());
8580         }
8581         
8582         
8583     },
8584     filterValidation : function(e){
8585         if(!e.isNavKeyPress()){
8586             this.validationTask.delay(this.validationDelay);
8587         }
8588     },
8589      /**
8590      * Validates the field value
8591      * @return {Boolean} True if the value is valid, else false
8592      */
8593     validate : function(){
8594         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8595         if(this.disabled || this.validateValue(this.getRawValue())){
8596             this.markValid();
8597             return true;
8598         }
8599         
8600         this.markInvalid();
8601         return false;
8602     },
8603     
8604     
8605     /**
8606      * Validates a value according to the field's validation rules and marks the field as invalid
8607      * if the validation fails
8608      * @param {Mixed} value The value to validate
8609      * @return {Boolean} True if the value is valid, else false
8610      */
8611     validateValue : function(value){
8612         if(value.length < 1)  { // if it's blank
8613             if(this.allowBlank){
8614                 return true;
8615             }
8616             return false;
8617         }
8618         
8619         if(value.length < this.minLength){
8620             return false;
8621         }
8622         if(value.length > this.maxLength){
8623             return false;
8624         }
8625         if(this.vtype){
8626             var vt = Roo.form.VTypes;
8627             if(!vt[this.vtype](value, this)){
8628                 return false;
8629             }
8630         }
8631         if(typeof this.validator == "function"){
8632             var msg = this.validator(value);
8633             if(msg !== true){
8634                 return false;
8635             }
8636         }
8637         
8638         if(this.regex && !this.regex.test(value)){
8639             return false;
8640         }
8641         
8642         return true;
8643     },
8644
8645     
8646     
8647      // private
8648     fireKey : function(e){
8649         //Roo.log('field ' + e.getKey());
8650         if(e.isNavKeyPress()){
8651             this.fireEvent("specialkey", this, e);
8652         }
8653     },
8654     focus : function (selectText){
8655         if(this.rendered){
8656             this.inputEl().focus();
8657             if(selectText === true){
8658                 this.inputEl().dom.select();
8659             }
8660         }
8661         return this;
8662     } ,
8663     
8664     onFocus : function(){
8665         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8666            // this.el.addClass(this.focusClass);
8667         }
8668         if(!this.hasFocus){
8669             this.hasFocus = true;
8670             this.startValue = this.getValue();
8671             this.fireEvent("focus", this);
8672         }
8673     },
8674     
8675     beforeBlur : Roo.emptyFn,
8676
8677     
8678     // private
8679     onBlur : function(){
8680         this.beforeBlur();
8681         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8682             //this.el.removeClass(this.focusClass);
8683         }
8684         this.hasFocus = false;
8685         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8686             this.validate();
8687         }
8688         var v = this.getValue();
8689         if(String(v) !== String(this.startValue)){
8690             this.fireEvent('change', this, v, this.startValue);
8691         }
8692         this.fireEvent("blur", this);
8693     },
8694     
8695     /**
8696      * Resets the current field value to the originally loaded value and clears any validation messages
8697      */
8698     reset : function(){
8699         this.setValue(this.originalValue);
8700         this.validate();
8701     },
8702      /**
8703      * Returns the name of the field
8704      * @return {Mixed} name The name field
8705      */
8706     getName: function(){
8707         return this.name;
8708     },
8709      /**
8710      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8711      * @return {Mixed} value The field value
8712      */
8713     getValue : function(){
8714         
8715         var v = this.inputEl().getValue();
8716         
8717         return v;
8718     },
8719     /**
8720      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8721      * @return {Mixed} value The field value
8722      */
8723     getRawValue : function(){
8724         var v = this.inputEl().getValue();
8725         
8726         return v;
8727     },
8728     
8729     /**
8730      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8731      * @param {Mixed} value The value to set
8732      */
8733     setRawValue : function(v){
8734         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8735     },
8736     
8737     selectText : function(start, end){
8738         var v = this.getRawValue();
8739         if(v.length > 0){
8740             start = start === undefined ? 0 : start;
8741             end = end === undefined ? v.length : end;
8742             var d = this.inputEl().dom;
8743             if(d.setSelectionRange){
8744                 d.setSelectionRange(start, end);
8745             }else if(d.createTextRange){
8746                 var range = d.createTextRange();
8747                 range.moveStart("character", start);
8748                 range.moveEnd("character", v.length-end);
8749                 range.select();
8750             }
8751         }
8752     },
8753     
8754     /**
8755      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8756      * @param {Mixed} value The value to set
8757      */
8758     setValue : function(v){
8759         this.value = v;
8760         if(this.rendered){
8761             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8762             this.validate();
8763         }
8764     },
8765     
8766     /*
8767     processValue : function(value){
8768         if(this.stripCharsRe){
8769             var newValue = value.replace(this.stripCharsRe, '');
8770             if(newValue !== value){
8771                 this.setRawValue(newValue);
8772                 return newValue;
8773             }
8774         }
8775         return value;
8776     },
8777   */
8778     preFocus : function(){
8779         
8780         if(this.selectOnFocus){
8781             this.inputEl().dom.select();
8782         }
8783     },
8784     filterKeys : function(e){
8785         var k = e.getKey();
8786         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8787             return;
8788         }
8789         var c = e.getCharCode(), cc = String.fromCharCode(c);
8790         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8791             return;
8792         }
8793         if(!this.maskRe.test(cc)){
8794             e.stopEvent();
8795         }
8796     },
8797      /**
8798      * Clear any invalid styles/messages for this field
8799      */
8800     clearInvalid : function(){
8801         
8802         if(!this.el || this.preventMark){ // not rendered
8803             return;
8804         }
8805         
8806         if(this.indicator){
8807             this.indicator.hide();
8808         }
8809         
8810         this.el.removeClass(this.invalidClass);
8811         
8812         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8813             
8814             var feedback = this.el.select('.form-control-feedback', true).first();
8815             
8816             if(feedback){
8817                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8818             }
8819             
8820         }
8821         
8822         this.fireEvent('valid', this);
8823     },
8824     
8825      /**
8826      * Mark this field as valid
8827      */
8828     markValid : function()
8829     {
8830         if(!this.el  || this.preventMark){ // not rendered
8831             return;
8832         }
8833         
8834         this.el.removeClass([this.invalidClass, this.validClass]);
8835         
8836         var feedback = this.el.select('.form-control-feedback', true).first();
8837             
8838         if(feedback){
8839             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8840         }
8841
8842         if(this.disabled || this.allowBlank){
8843             return;
8844         }
8845         
8846         if(this.indicator){
8847             this.indicator.hide();
8848         }
8849         
8850         this.el.addClass(this.validClass);
8851         
8852         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8853             
8854             var feedback = this.el.select('.form-control-feedback', true).first();
8855             
8856             if(feedback){
8857                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8858                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8859             }
8860             
8861         }
8862         
8863         this.fireEvent('valid', this);
8864     },
8865     
8866      /**
8867      * Mark this field as invalid
8868      * @param {String} msg The validation message
8869      */
8870     markInvalid : function(msg)
8871     {
8872         if(!this.el  || this.preventMark){ // not rendered
8873             return;
8874         }
8875         
8876         this.el.removeClass([this.invalidClass, this.validClass]);
8877         
8878         var feedback = this.el.select('.form-control-feedback', true).first();
8879             
8880         if(feedback){
8881             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8882         }
8883
8884         if(this.disabled || this.allowBlank){
8885             return;
8886         }
8887         
8888         if(this.indicator){
8889             this.indicator.show();
8890         }
8891         
8892         this.el.addClass(this.invalidClass);
8893         
8894         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8895             
8896             var feedback = this.el.select('.form-control-feedback', true).first();
8897             
8898             if(feedback){
8899                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8900                 
8901                 if(this.getValue().length || this.forceFeedback){
8902                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8903                 }
8904                 
8905             }
8906             
8907         }
8908         
8909         this.fireEvent('invalid', this, msg);
8910     },
8911     // private
8912     SafariOnKeyDown : function(event)
8913     {
8914         // this is a workaround for a password hang bug on chrome/ webkit.
8915         if (this.inputEl().dom.type != 'password') {
8916             return;
8917         }
8918         
8919         var isSelectAll = false;
8920         
8921         if(this.inputEl().dom.selectionEnd > 0){
8922             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8923         }
8924         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8925             event.preventDefault();
8926             this.setValue('');
8927             return;
8928         }
8929         
8930         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
8931             
8932             event.preventDefault();
8933             // this is very hacky as keydown always get's upper case.
8934             //
8935             var cc = String.fromCharCode(event.getCharCode());
8936             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8937             
8938         }
8939     },
8940     adjustWidth : function(tag, w){
8941         tag = tag.toLowerCase();
8942         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8943             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8944                 if(tag == 'input'){
8945                     return w + 2;
8946                 }
8947                 if(tag == 'textarea'){
8948                     return w-2;
8949                 }
8950             }else if(Roo.isOpera){
8951                 if(tag == 'input'){
8952                     return w + 2;
8953                 }
8954                 if(tag == 'textarea'){
8955                     return w-2;
8956                 }
8957             }
8958         }
8959         return w;
8960     }
8961     
8962 });
8963
8964  
8965 /*
8966  * - LGPL
8967  *
8968  * Input
8969  * 
8970  */
8971
8972 /**
8973  * @class Roo.bootstrap.TextArea
8974  * @extends Roo.bootstrap.Input
8975  * Bootstrap TextArea class
8976  * @cfg {Number} cols Specifies the visible width of a text area
8977  * @cfg {Number} rows Specifies the visible number of lines in a text area
8978  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8979  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8980  * @cfg {string} html text
8981  * 
8982  * @constructor
8983  * Create a new TextArea
8984  * @param {Object} config The config object
8985  */
8986
8987 Roo.bootstrap.TextArea = function(config){
8988     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8989    
8990 };
8991
8992 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8993      
8994     cols : false,
8995     rows : 5,
8996     readOnly : false,
8997     warp : 'soft',
8998     resize : false,
8999     value: false,
9000     html: false,
9001     
9002     getAutoCreate : function(){
9003         
9004         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9005         
9006         var id = Roo.id();
9007         
9008         var cfg = {};
9009         
9010         var input =  {
9011             tag: 'textarea',
9012             id : id,
9013             warp : this.warp,
9014             rows : this.rows,
9015             value : this.value || '',
9016             html: this.html || '',
9017             cls : 'form-control',
9018             placeholder : this.placeholder || '' 
9019             
9020         };
9021         
9022         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9023             input.maxLength = this.maxLength;
9024         }
9025         
9026         if(this.resize){
9027             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9028         }
9029         
9030         if(this.cols){
9031             input.cols = this.cols;
9032         }
9033         
9034         if (this.readOnly) {
9035             input.readonly = true;
9036         }
9037         
9038         if (this.name) {
9039             input.name = this.name;
9040         }
9041         
9042         if (this.size) {
9043             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9044         }
9045         
9046         var settings=this;
9047         ['xs','sm','md','lg'].map(function(size){
9048             if (settings[size]) {
9049                 cfg.cls += ' col-' + size + '-' + settings[size];
9050             }
9051         });
9052         
9053         var inputblock = input;
9054         
9055         if(this.hasFeedback && !this.allowBlank){
9056             
9057             var feedback = {
9058                 tag: 'span',
9059                 cls: 'glyphicon form-control-feedback'
9060             };
9061
9062             inputblock = {
9063                 cls : 'has-feedback',
9064                 cn :  [
9065                     input,
9066                     feedback
9067                 ] 
9068             };  
9069         }
9070         
9071         
9072         if (this.before || this.after) {
9073             
9074             inputblock = {
9075                 cls : 'input-group',
9076                 cn :  [] 
9077             };
9078             if (this.before) {
9079                 inputblock.cn.push({
9080                     tag :'span',
9081                     cls : 'input-group-addon',
9082                     html : this.before
9083                 });
9084             }
9085             
9086             inputblock.cn.push(input);
9087             
9088             if(this.hasFeedback && !this.allowBlank){
9089                 inputblock.cls += ' has-feedback';
9090                 inputblock.cn.push(feedback);
9091             }
9092             
9093             if (this.after) {
9094                 inputblock.cn.push({
9095                     tag :'span',
9096                     cls : 'input-group-addon',
9097                     html : this.after
9098                 });
9099             }
9100             
9101         }
9102         
9103         if (align ==='left' && this.fieldLabel.length) {
9104 //                Roo.log("left and has label");
9105                 cfg.cn = [
9106                     
9107                     {
9108                         tag: 'label',
9109                         'for' :  id,
9110                         cls : 'control-label col-sm-' + this.labelWidth,
9111                         html : this.fieldLabel
9112                         
9113                     },
9114                     {
9115                         cls : "col-sm-" + (12 - this.labelWidth), 
9116                         cn: [
9117                             inputblock
9118                         ]
9119                     }
9120                     
9121                 ];
9122         } else if ( this.fieldLabel.length) {
9123 //                Roo.log(" label");
9124                  cfg.cn = [
9125                    
9126                     {
9127                         tag: 'label',
9128                         //cls : 'input-group-addon',
9129                         html : this.fieldLabel
9130                         
9131                     },
9132                     
9133                     inputblock
9134                     
9135                 ];
9136
9137         } else {
9138             
9139 //                   Roo.log(" no label && no align");
9140                 cfg.cn = [
9141                     
9142                         inputblock
9143                     
9144                 ];
9145                 
9146                 
9147         }
9148         
9149         if (this.disabled) {
9150             input.disabled=true;
9151         }
9152         
9153         return cfg;
9154         
9155     },
9156     /**
9157      * return the real textarea element.
9158      */
9159     inputEl: function ()
9160     {
9161         return this.el.select('textarea.form-control',true).first();
9162     },
9163     
9164     /**
9165      * Clear any invalid styles/messages for this field
9166      */
9167     clearInvalid : function()
9168     {
9169         
9170         if(!this.el || this.preventMark){ // not rendered
9171             return;
9172         }
9173         
9174         var label = this.el.select('label', true).first();
9175         var icon = this.el.select('i.fa-star', true).first();
9176         
9177         if(label && icon){
9178             icon.remove();
9179         }
9180         
9181         this.el.removeClass(this.invalidClass);
9182         
9183         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9184             
9185             var feedback = this.el.select('.form-control-feedback', true).first();
9186             
9187             if(feedback){
9188                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9189             }
9190             
9191         }
9192         
9193         this.fireEvent('valid', this);
9194     },
9195     
9196      /**
9197      * Mark this field as valid
9198      */
9199     markValid : function()
9200     {
9201         if(!this.el  || this.preventMark){ // not rendered
9202             return;
9203         }
9204         
9205         this.el.removeClass([this.invalidClass, this.validClass]);
9206         
9207         var feedback = this.el.select('.form-control-feedback', true).first();
9208             
9209         if(feedback){
9210             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9211         }
9212
9213         if(this.disabled || this.allowBlank){
9214             return;
9215         }
9216         
9217         var label = this.el.select('label', true).first();
9218         var icon = this.el.select('i.fa-star', true).first();
9219         
9220         if(label && icon){
9221             icon.remove();
9222         }
9223         
9224         this.el.addClass(this.validClass);
9225         
9226         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9227             
9228             var feedback = this.el.select('.form-control-feedback', true).first();
9229             
9230             if(feedback){
9231                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9232                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9233             }
9234             
9235         }
9236         
9237         this.fireEvent('valid', this);
9238     },
9239     
9240      /**
9241      * Mark this field as invalid
9242      * @param {String} msg The validation message
9243      */
9244     markInvalid : function(msg)
9245     {
9246         if(!this.el  || this.preventMark){ // not rendered
9247             return;
9248         }
9249         
9250         this.el.removeClass([this.invalidClass, this.validClass]);
9251         
9252         var feedback = this.el.select('.form-control-feedback', true).first();
9253             
9254         if(feedback){
9255             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9256         }
9257
9258         if(this.disabled || this.allowBlank){
9259             return;
9260         }
9261         
9262         var label = this.el.select('label', true).first();
9263         var icon = this.el.select('i.fa-star', true).first();
9264         
9265         if(!this.getValue().length && label && !icon){
9266             this.el.createChild({
9267                 tag : 'i',
9268                 cls : 'text-danger fa fa-lg fa-star',
9269                 tooltip : 'This field is required',
9270                 style : 'margin-right:5px;'
9271             }, label, true);
9272         }
9273
9274         this.el.addClass(this.invalidClass);
9275         
9276         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9277             
9278             var feedback = this.el.select('.form-control-feedback', true).first();
9279             
9280             if(feedback){
9281                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9282                 
9283                 if(this.getValue().length || this.forceFeedback){
9284                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9285                 }
9286                 
9287             }
9288             
9289         }
9290         
9291         this.fireEvent('invalid', this, msg);
9292     }
9293 });
9294
9295  
9296 /*
9297  * - LGPL
9298  *
9299  * trigger field - base class for combo..
9300  * 
9301  */
9302  
9303 /**
9304  * @class Roo.bootstrap.TriggerField
9305  * @extends Roo.bootstrap.Input
9306  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9307  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9308  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9309  * for which you can provide a custom implementation.  For example:
9310  * <pre><code>
9311 var trigger = new Roo.bootstrap.TriggerField();
9312 trigger.onTriggerClick = myTriggerFn;
9313 trigger.applyTo('my-field');
9314 </code></pre>
9315  *
9316  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9317  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9318  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9319  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9320  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9321
9322  * @constructor
9323  * Create a new TriggerField.
9324  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9325  * to the base TextField)
9326  */
9327 Roo.bootstrap.TriggerField = function(config){
9328     this.mimicing = false;
9329     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9330 };
9331
9332 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9333     /**
9334      * @cfg {String} triggerClass A CSS class to apply to the trigger
9335      */
9336      /**
9337      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9338      */
9339     hideTrigger:false,
9340
9341     /**
9342      * @cfg {Boolean} removable (true|false) special filter default false
9343      */
9344     removable : false,
9345     
9346     /** @cfg {Boolean} grow @hide */
9347     /** @cfg {Number} growMin @hide */
9348     /** @cfg {Number} growMax @hide */
9349
9350     /**
9351      * @hide 
9352      * @method
9353      */
9354     autoSize: Roo.emptyFn,
9355     // private
9356     monitorTab : true,
9357     // private
9358     deferHeight : true,
9359
9360     
9361     actionMode : 'wrap',
9362     
9363     caret : false,
9364     
9365     
9366     getAutoCreate : function(){
9367        
9368         var align = this.labelAlign || this.parentLabelAlign();
9369         
9370         var id = Roo.id();
9371         
9372         var cfg = {
9373             cls: 'form-group' //input-group
9374         };
9375         
9376         
9377         var input =  {
9378             tag: 'input',
9379             id : id,
9380             type : this.inputType,
9381             cls : 'form-control',
9382             autocomplete: 'new-password',
9383             placeholder : this.placeholder || '' 
9384             
9385         };
9386         if (this.name) {
9387             input.name = this.name;
9388         }
9389         if (this.size) {
9390             input.cls += ' input-' + this.size;
9391         }
9392         
9393         if (this.disabled) {
9394             input.disabled=true;
9395         }
9396         
9397         var inputblock = input;
9398         
9399         if(this.hasFeedback && !this.allowBlank){
9400             
9401             var feedback = {
9402                 tag: 'span',
9403                 cls: 'glyphicon form-control-feedback'
9404             };
9405             
9406             if(this.removable && !this.editable && !this.tickable){
9407                 inputblock = {
9408                     cls : 'has-feedback',
9409                     cn :  [
9410                         inputblock,
9411                         {
9412                             tag: 'button',
9413                             html : 'x',
9414                             cls : 'roo-combo-removable-btn close'
9415                         },
9416                         feedback
9417                     ] 
9418                 };
9419             } else {
9420                 inputblock = {
9421                     cls : 'has-feedback',
9422                     cn :  [
9423                         inputblock,
9424                         feedback
9425                     ] 
9426                 };
9427             }
9428
9429         } else {
9430             if(this.removable && !this.editable && !this.tickable){
9431                 inputblock = {
9432                     cls : 'roo-removable',
9433                     cn :  [
9434                         inputblock,
9435                         {
9436                             tag: 'button',
9437                             html : 'x',
9438                             cls : 'roo-combo-removable-btn close'
9439                         }
9440                     ] 
9441                 };
9442             }
9443         }
9444         
9445         if (this.before || this.after) {
9446             
9447             inputblock = {
9448                 cls : 'input-group',
9449                 cn :  [] 
9450             };
9451             if (this.before) {
9452                 inputblock.cn.push({
9453                     tag :'span',
9454                     cls : 'input-group-addon',
9455                     html : this.before
9456                 });
9457             }
9458             
9459             inputblock.cn.push(input);
9460             
9461             if(this.hasFeedback && !this.allowBlank){
9462                 inputblock.cls += ' has-feedback';
9463                 inputblock.cn.push(feedback);
9464             }
9465             
9466             if (this.after) {
9467                 inputblock.cn.push({
9468                     tag :'span',
9469                     cls : 'input-group-addon',
9470                     html : this.after
9471                 });
9472             }
9473             
9474         };
9475         
9476         var box = {
9477             tag: 'div',
9478             cn: [
9479                 {
9480                     tag: 'input',
9481                     type : 'hidden',
9482                     cls: 'form-hidden-field'
9483                 },
9484                 inputblock
9485             ]
9486             
9487         };
9488         
9489         if(this.multiple){
9490             box = {
9491                 tag: 'div',
9492                 cn: [
9493                     {
9494                         tag: 'input',
9495                         type : 'hidden',
9496                         cls: 'form-hidden-field'
9497                     },
9498                     {
9499                         tag: 'ul',
9500                         cls: 'roo-select2-choices',
9501                         cn:[
9502                             {
9503                                 tag: 'li',
9504                                 cls: 'roo-select2-search-field',
9505                                 cn: [
9506
9507                                     inputblock
9508                                 ]
9509                             }
9510                         ]
9511                     }
9512                 ]
9513             }
9514         };
9515         
9516         var combobox = {
9517             cls: 'roo-select2-container input-group',
9518             cn: [
9519                 box
9520 //                {
9521 //                    tag: 'ul',
9522 //                    cls: 'typeahead typeahead-long dropdown-menu',
9523 //                    style: 'display:none'
9524 //                }
9525             ]
9526         };
9527         
9528         if(!this.multiple && this.showToggleBtn){
9529             
9530             var caret = {
9531                         tag: 'span',
9532                         cls: 'caret'
9533              };
9534             if (this.caret != false) {
9535                 caret = {
9536                      tag: 'i',
9537                      cls: 'fa fa-' + this.caret
9538                 };
9539                 
9540             }
9541             
9542             combobox.cn.push({
9543                 tag :'span',
9544                 cls : 'input-group-addon btn dropdown-toggle',
9545                 cn : [
9546                     caret,
9547                     {
9548                         tag: 'span',
9549                         cls: 'combobox-clear',
9550                         cn  : [
9551                             {
9552                                 tag : 'i',
9553                                 cls: 'icon-remove'
9554                             }
9555                         ]
9556                     }
9557                 ]
9558
9559             })
9560         }
9561         
9562         if(this.multiple){
9563             combobox.cls += ' roo-select2-container-multi';
9564         }
9565         
9566         if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
9567             
9568 //                Roo.log("left and has label");
9569             cfg.cn = [
9570                 {
9571                     tag : 'i',
9572                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9573                     tooltip : 'This field is required'
9574                 },
9575                 {
9576                     tag: 'label',
9577                     'for' :  id,
9578                     cls : 'control-label col-sm-' + this.labelWidth,
9579                     html : this.fieldLabel
9580
9581                 },
9582                 {
9583                     cls : "col-sm-" + (12 - this.labelWidth), 
9584                     cn: [
9585                         combobox
9586                     ]
9587                 }
9588
9589             ];
9590             
9591             if(this.indicatorpos == 'right'){
9592                 cfg.cn = [
9593                     {
9594                         tag: 'label',
9595                         'for' :  id,
9596                         cls : 'control-label col-sm-' + this.labelWidth,
9597                         html : this.fieldLabel
9598
9599                     },
9600                     {
9601                         tag : 'i',
9602                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9603                         tooltip : 'This field is required'
9604                     },
9605                     {
9606                         cls : "col-sm-" + (12 - this.labelWidth), 
9607                         cn: [
9608                             combobox
9609                         ]
9610                     }
9611
9612                 ];
9613             }
9614             
9615         } else if ( this.fieldLabel.length) {
9616 //                Roo.log(" label");
9617             cfg.cn = [
9618                 {
9619                    tag : 'i',
9620                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9621                    tooltip : 'This field is required'
9622                },
9623                {
9624                    tag: 'label',
9625                    //cls : 'input-group-addon',
9626                    html : this.fieldLabel
9627
9628                },
9629
9630                combobox
9631
9632             ];
9633             
9634             if(this.indicatorpos == 'right'){
9635                 
9636                 cfg.cn = [
9637                     {
9638                        tag: 'label',
9639                        //cls : 'input-group-addon',
9640                        html : this.fieldLabel
9641
9642                     },
9643                     {
9644                        tag : 'i',
9645                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9646                        tooltip : 'This field is required'
9647                     },
9648                     
9649                     combobox
9650
9651                 ];
9652
9653             }
9654
9655         } else {
9656             
9657 //                Roo.log(" no label && no align");
9658                 cfg = combobox
9659                      
9660                 
9661         }
9662          
9663         var settings=this;
9664         ['xs','sm','md','lg'].map(function(size){
9665             if (settings[size]) {
9666                 cfg.cls += ' col-' + size + '-' + settings[size];
9667             }
9668         });
9669         
9670         return cfg;
9671         
9672     },
9673     
9674     
9675     
9676     // private
9677     onResize : function(w, h){
9678 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9679 //        if(typeof w == 'number'){
9680 //            var x = w - this.trigger.getWidth();
9681 //            this.inputEl().setWidth(this.adjustWidth('input', x));
9682 //            this.trigger.setStyle('left', x+'px');
9683 //        }
9684     },
9685
9686     // private
9687     adjustSize : Roo.BoxComponent.prototype.adjustSize,
9688
9689     // private
9690     getResizeEl : function(){
9691         return this.inputEl();
9692     },
9693
9694     // private
9695     getPositionEl : function(){
9696         return this.inputEl();
9697     },
9698
9699     // private
9700     alignErrorIcon : function(){
9701         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9702     },
9703
9704     // private
9705     initEvents : function(){
9706         
9707         this.createList();
9708         
9709         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9710         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9711         if(!this.multiple && this.showToggleBtn){
9712             this.trigger = this.el.select('span.dropdown-toggle',true).first();
9713             if(this.hideTrigger){
9714                 this.trigger.setDisplayed(false);
9715             }
9716             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9717         }
9718         
9719         if(this.multiple){
9720             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9721         }
9722         
9723         if(this.removable && !this.editable && !this.tickable){
9724             var close = this.closeTriggerEl();
9725             
9726             if(close){
9727                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9728                 close.on('click', this.removeBtnClick, this, close);
9729             }
9730         }
9731         
9732         //this.trigger.addClassOnOver('x-form-trigger-over');
9733         //this.trigger.addClassOnClick('x-form-trigger-click');
9734         
9735         //if(!this.width){
9736         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9737         //}
9738     },
9739     
9740     closeTriggerEl : function()
9741     {
9742         var close = this.el.select('.roo-combo-removable-btn', true).first();
9743         return close ? close : false;
9744     },
9745     
9746     removeBtnClick : function(e, h, el)
9747     {
9748         e.preventDefault();
9749         
9750         if(this.fireEvent("remove", this) !== false){
9751             this.reset();
9752             this.fireEvent("afterremove", this)
9753         }
9754     },
9755     
9756     createList : function()
9757     {
9758         this.list = Roo.get(document.body).createChild({
9759             tag: 'ul',
9760             cls: 'typeahead typeahead-long dropdown-menu',
9761             style: 'display:none'
9762         });
9763         
9764         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9765         
9766     },
9767
9768     // private
9769     initTrigger : function(){
9770        
9771     },
9772
9773     // private
9774     onDestroy : function(){
9775         if(this.trigger){
9776             this.trigger.removeAllListeners();
9777           //  this.trigger.remove();
9778         }
9779         //if(this.wrap){
9780         //    this.wrap.remove();
9781         //}
9782         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9783     },
9784
9785     // private
9786     onFocus : function(){
9787         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9788         /*
9789         if(!this.mimicing){
9790             this.wrap.addClass('x-trigger-wrap-focus');
9791             this.mimicing = true;
9792             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9793             if(this.monitorTab){
9794                 this.el.on("keydown", this.checkTab, this);
9795             }
9796         }
9797         */
9798     },
9799
9800     // private
9801     checkTab : function(e){
9802         if(e.getKey() == e.TAB){
9803             this.triggerBlur();
9804         }
9805     },
9806
9807     // private
9808     onBlur : function(){
9809         // do nothing
9810     },
9811
9812     // private
9813     mimicBlur : function(e, t){
9814         /*
9815         if(!this.wrap.contains(t) && this.validateBlur()){
9816             this.triggerBlur();
9817         }
9818         */
9819     },
9820
9821     // private
9822     triggerBlur : function(){
9823         this.mimicing = false;
9824         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9825         if(this.monitorTab){
9826             this.el.un("keydown", this.checkTab, this);
9827         }
9828         //this.wrap.removeClass('x-trigger-wrap-focus');
9829         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9830     },
9831
9832     // private
9833     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9834     validateBlur : function(e, t){
9835         return true;
9836     },
9837
9838     // private
9839     onDisable : function(){
9840         this.inputEl().dom.disabled = true;
9841         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9842         //if(this.wrap){
9843         //    this.wrap.addClass('x-item-disabled');
9844         //}
9845     },
9846
9847     // private
9848     onEnable : function(){
9849         this.inputEl().dom.disabled = false;
9850         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9851         //if(this.wrap){
9852         //    this.el.removeClass('x-item-disabled');
9853         //}
9854     },
9855
9856     // private
9857     onShow : function(){
9858         var ae = this.getActionEl();
9859         
9860         if(ae){
9861             ae.dom.style.display = '';
9862             ae.dom.style.visibility = 'visible';
9863         }
9864     },
9865
9866     // private
9867     
9868     onHide : function(){
9869         var ae = this.getActionEl();
9870         ae.dom.style.display = 'none';
9871     },
9872
9873     /**
9874      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9875      * by an implementing function.
9876      * @method
9877      * @param {EventObject} e
9878      */
9879     onTriggerClick : Roo.emptyFn
9880 });
9881  /*
9882  * Based on:
9883  * Ext JS Library 1.1.1
9884  * Copyright(c) 2006-2007, Ext JS, LLC.
9885  *
9886  * Originally Released Under LGPL - original licence link has changed is not relivant.
9887  *
9888  * Fork - LGPL
9889  * <script type="text/javascript">
9890  */
9891
9892
9893 /**
9894  * @class Roo.data.SortTypes
9895  * @singleton
9896  * Defines the default sorting (casting?) comparison functions used when sorting data.
9897  */
9898 Roo.data.SortTypes = {
9899     /**
9900      * Default sort that does nothing
9901      * @param {Mixed} s The value being converted
9902      * @return {Mixed} The comparison value
9903      */
9904     none : function(s){
9905         return s;
9906     },
9907     
9908     /**
9909      * The regular expression used to strip tags
9910      * @type {RegExp}
9911      * @property
9912      */
9913     stripTagsRE : /<\/?[^>]+>/gi,
9914     
9915     /**
9916      * Strips all HTML tags to sort on text only
9917      * @param {Mixed} s The value being converted
9918      * @return {String} The comparison value
9919      */
9920     asText : function(s){
9921         return String(s).replace(this.stripTagsRE, "");
9922     },
9923     
9924     /**
9925      * Strips all HTML tags to sort on text only - Case insensitive
9926      * @param {Mixed} s The value being converted
9927      * @return {String} The comparison value
9928      */
9929     asUCText : function(s){
9930         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9931     },
9932     
9933     /**
9934      * Case insensitive string
9935      * @param {Mixed} s The value being converted
9936      * @return {String} The comparison value
9937      */
9938     asUCString : function(s) {
9939         return String(s).toUpperCase();
9940     },
9941     
9942     /**
9943      * Date sorting
9944      * @param {Mixed} s The value being converted
9945      * @return {Number} The comparison value
9946      */
9947     asDate : function(s) {
9948         if(!s){
9949             return 0;
9950         }
9951         if(s instanceof Date){
9952             return s.getTime();
9953         }
9954         return Date.parse(String(s));
9955     },
9956     
9957     /**
9958      * Float sorting
9959      * @param {Mixed} s The value being converted
9960      * @return {Float} The comparison value
9961      */
9962     asFloat : function(s) {
9963         var val = parseFloat(String(s).replace(/,/g, ""));
9964         if(isNaN(val)) {
9965             val = 0;
9966         }
9967         return val;
9968     },
9969     
9970     /**
9971      * Integer sorting
9972      * @param {Mixed} s The value being converted
9973      * @return {Number} The comparison value
9974      */
9975     asInt : function(s) {
9976         var val = parseInt(String(s).replace(/,/g, ""));
9977         if(isNaN(val)) {
9978             val = 0;
9979         }
9980         return val;
9981     }
9982 };/*
9983  * Based on:
9984  * Ext JS Library 1.1.1
9985  * Copyright(c) 2006-2007, Ext JS, LLC.
9986  *
9987  * Originally Released Under LGPL - original licence link has changed is not relivant.
9988  *
9989  * Fork - LGPL
9990  * <script type="text/javascript">
9991  */
9992
9993 /**
9994 * @class Roo.data.Record
9995  * Instances of this class encapsulate both record <em>definition</em> information, and record
9996  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9997  * to access Records cached in an {@link Roo.data.Store} object.<br>
9998  * <p>
9999  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10000  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10001  * objects.<br>
10002  * <p>
10003  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10004  * @constructor
10005  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10006  * {@link #create}. The parameters are the same.
10007  * @param {Array} data An associative Array of data values keyed by the field name.
10008  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10009  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10010  * not specified an integer id is generated.
10011  */
10012 Roo.data.Record = function(data, id){
10013     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10014     this.data = data;
10015 };
10016
10017 /**
10018  * Generate a constructor for a specific record layout.
10019  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10020  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10021  * Each field definition object may contain the following properties: <ul>
10022  * <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,
10023  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10024  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10025  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10026  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10027  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10028  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10029  * this may be omitted.</p></li>
10030  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10031  * <ul><li>auto (Default, implies no conversion)</li>
10032  * <li>string</li>
10033  * <li>int</li>
10034  * <li>float</li>
10035  * <li>boolean</li>
10036  * <li>date</li></ul></p></li>
10037  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10038  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10039  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10040  * by the Reader into an object that will be stored in the Record. It is passed the
10041  * following parameters:<ul>
10042  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10043  * </ul></p></li>
10044  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10045  * </ul>
10046  * <br>usage:<br><pre><code>
10047 var TopicRecord = Roo.data.Record.create(
10048     {name: 'title', mapping: 'topic_title'},
10049     {name: 'author', mapping: 'username'},
10050     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10051     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10052     {name: 'lastPoster', mapping: 'user2'},
10053     {name: 'excerpt', mapping: 'post_text'}
10054 );
10055
10056 var myNewRecord = new TopicRecord({
10057     title: 'Do my job please',
10058     author: 'noobie',
10059     totalPosts: 1,
10060     lastPost: new Date(),
10061     lastPoster: 'Animal',
10062     excerpt: 'No way dude!'
10063 });
10064 myStore.add(myNewRecord);
10065 </code></pre>
10066  * @method create
10067  * @static
10068  */
10069 Roo.data.Record.create = function(o){
10070     var f = function(){
10071         f.superclass.constructor.apply(this, arguments);
10072     };
10073     Roo.extend(f, Roo.data.Record);
10074     var p = f.prototype;
10075     p.fields = new Roo.util.MixedCollection(false, function(field){
10076         return field.name;
10077     });
10078     for(var i = 0, len = o.length; i < len; i++){
10079         p.fields.add(new Roo.data.Field(o[i]));
10080     }
10081     f.getField = function(name){
10082         return p.fields.get(name);  
10083     };
10084     return f;
10085 };
10086
10087 Roo.data.Record.AUTO_ID = 1000;
10088 Roo.data.Record.EDIT = 'edit';
10089 Roo.data.Record.REJECT = 'reject';
10090 Roo.data.Record.COMMIT = 'commit';
10091
10092 Roo.data.Record.prototype = {
10093     /**
10094      * Readonly flag - true if this record has been modified.
10095      * @type Boolean
10096      */
10097     dirty : false,
10098     editing : false,
10099     error: null,
10100     modified: null,
10101
10102     // private
10103     join : function(store){
10104         this.store = store;
10105     },
10106
10107     /**
10108      * Set the named field to the specified value.
10109      * @param {String} name The name of the field to set.
10110      * @param {Object} value The value to set the field to.
10111      */
10112     set : function(name, value){
10113         if(this.data[name] == value){
10114             return;
10115         }
10116         this.dirty = true;
10117         if(!this.modified){
10118             this.modified = {};
10119         }
10120         if(typeof this.modified[name] == 'undefined'){
10121             this.modified[name] = this.data[name];
10122         }
10123         this.data[name] = value;
10124         if(!this.editing && this.store){
10125             this.store.afterEdit(this);
10126         }       
10127     },
10128
10129     /**
10130      * Get the value of the named field.
10131      * @param {String} name The name of the field to get the value of.
10132      * @return {Object} The value of the field.
10133      */
10134     get : function(name){
10135         return this.data[name]; 
10136     },
10137
10138     // private
10139     beginEdit : function(){
10140         this.editing = true;
10141         this.modified = {}; 
10142     },
10143
10144     // private
10145     cancelEdit : function(){
10146         this.editing = false;
10147         delete this.modified;
10148     },
10149
10150     // private
10151     endEdit : function(){
10152         this.editing = false;
10153         if(this.dirty && this.store){
10154             this.store.afterEdit(this);
10155         }
10156     },
10157
10158     /**
10159      * Usually called by the {@link Roo.data.Store} which owns the Record.
10160      * Rejects all changes made to the Record since either creation, or the last commit operation.
10161      * Modified fields are reverted to their original values.
10162      * <p>
10163      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10164      * of reject operations.
10165      */
10166     reject : function(){
10167         var m = this.modified;
10168         for(var n in m){
10169             if(typeof m[n] != "function"){
10170                 this.data[n] = m[n];
10171             }
10172         }
10173         this.dirty = false;
10174         delete this.modified;
10175         this.editing = false;
10176         if(this.store){
10177             this.store.afterReject(this);
10178         }
10179     },
10180
10181     /**
10182      * Usually called by the {@link Roo.data.Store} which owns the Record.
10183      * Commits all changes made to the Record since either creation, or the last commit operation.
10184      * <p>
10185      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10186      * of commit operations.
10187      */
10188     commit : function(){
10189         this.dirty = false;
10190         delete this.modified;
10191         this.editing = false;
10192         if(this.store){
10193             this.store.afterCommit(this);
10194         }
10195     },
10196
10197     // private
10198     hasError : function(){
10199         return this.error != null;
10200     },
10201
10202     // private
10203     clearError : function(){
10204         this.error = null;
10205     },
10206
10207     /**
10208      * Creates a copy of this record.
10209      * @param {String} id (optional) A new record id if you don't want to use this record's id
10210      * @return {Record}
10211      */
10212     copy : function(newId) {
10213         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10214     }
10215 };/*
10216  * Based on:
10217  * Ext JS Library 1.1.1
10218  * Copyright(c) 2006-2007, Ext JS, LLC.
10219  *
10220  * Originally Released Under LGPL - original licence link has changed is not relivant.
10221  *
10222  * Fork - LGPL
10223  * <script type="text/javascript">
10224  */
10225
10226
10227
10228 /**
10229  * @class Roo.data.Store
10230  * @extends Roo.util.Observable
10231  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10232  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10233  * <p>
10234  * 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
10235  * has no knowledge of the format of the data returned by the Proxy.<br>
10236  * <p>
10237  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10238  * instances from the data object. These records are cached and made available through accessor functions.
10239  * @constructor
10240  * Creates a new Store.
10241  * @param {Object} config A config object containing the objects needed for the Store to access data,
10242  * and read the data into Records.
10243  */
10244 Roo.data.Store = function(config){
10245     this.data = new Roo.util.MixedCollection(false);
10246     this.data.getKey = function(o){
10247         return o.id;
10248     };
10249     this.baseParams = {};
10250     // private
10251     this.paramNames = {
10252         "start" : "start",
10253         "limit" : "limit",
10254         "sort" : "sort",
10255         "dir" : "dir",
10256         "multisort" : "_multisort"
10257     };
10258
10259     if(config && config.data){
10260         this.inlineData = config.data;
10261         delete config.data;
10262     }
10263
10264     Roo.apply(this, config);
10265     
10266     if(this.reader){ // reader passed
10267         this.reader = Roo.factory(this.reader, Roo.data);
10268         this.reader.xmodule = this.xmodule || false;
10269         if(!this.recordType){
10270             this.recordType = this.reader.recordType;
10271         }
10272         if(this.reader.onMetaChange){
10273             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10274         }
10275     }
10276
10277     if(this.recordType){
10278         this.fields = this.recordType.prototype.fields;
10279     }
10280     this.modified = [];
10281
10282     this.addEvents({
10283         /**
10284          * @event datachanged
10285          * Fires when the data cache has changed, and a widget which is using this Store
10286          * as a Record cache should refresh its view.
10287          * @param {Store} this
10288          */
10289         datachanged : true,
10290         /**
10291          * @event metachange
10292          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10293          * @param {Store} this
10294          * @param {Object} meta The JSON metadata
10295          */
10296         metachange : true,
10297         /**
10298          * @event add
10299          * Fires when Records have been added to the Store
10300          * @param {Store} this
10301          * @param {Roo.data.Record[]} records The array of Records added
10302          * @param {Number} index The index at which the record(s) were added
10303          */
10304         add : true,
10305         /**
10306          * @event remove
10307          * Fires when a Record has been removed from the Store
10308          * @param {Store} this
10309          * @param {Roo.data.Record} record The Record that was removed
10310          * @param {Number} index The index at which the record was removed
10311          */
10312         remove : true,
10313         /**
10314          * @event update
10315          * Fires when a Record has been updated
10316          * @param {Store} this
10317          * @param {Roo.data.Record} record The Record that was updated
10318          * @param {String} operation The update operation being performed.  Value may be one of:
10319          * <pre><code>
10320  Roo.data.Record.EDIT
10321  Roo.data.Record.REJECT
10322  Roo.data.Record.COMMIT
10323          * </code></pre>
10324          */
10325         update : true,
10326         /**
10327          * @event clear
10328          * Fires when the data cache has been cleared.
10329          * @param {Store} this
10330          */
10331         clear : true,
10332         /**
10333          * @event beforeload
10334          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10335          * the load action will be canceled.
10336          * @param {Store} this
10337          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10338          */
10339         beforeload : true,
10340         /**
10341          * @event beforeloadadd
10342          * Fires after a new set of Records has been loaded.
10343          * @param {Store} this
10344          * @param {Roo.data.Record[]} records The Records that were loaded
10345          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10346          */
10347         beforeloadadd : true,
10348         /**
10349          * @event load
10350          * Fires after a new set of Records has been loaded, before they are added to the store.
10351          * @param {Store} this
10352          * @param {Roo.data.Record[]} records The Records that were loaded
10353          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10354          * @params {Object} return from reader
10355          */
10356         load : true,
10357         /**
10358          * @event loadexception
10359          * Fires if an exception occurs in the Proxy during loading.
10360          * Called with the signature of the Proxy's "loadexception" event.
10361          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10362          * 
10363          * @param {Proxy} 
10364          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10365          * @param {Object} load options 
10366          * @param {Object} jsonData from your request (normally this contains the Exception)
10367          */
10368         loadexception : true
10369     });
10370     
10371     if(this.proxy){
10372         this.proxy = Roo.factory(this.proxy, Roo.data);
10373         this.proxy.xmodule = this.xmodule || false;
10374         this.relayEvents(this.proxy,  ["loadexception"]);
10375     }
10376     this.sortToggle = {};
10377     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10378
10379     Roo.data.Store.superclass.constructor.call(this);
10380
10381     if(this.inlineData){
10382         this.loadData(this.inlineData);
10383         delete this.inlineData;
10384     }
10385 };
10386
10387 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10388      /**
10389     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10390     * without a remote query - used by combo/forms at present.
10391     */
10392     
10393     /**
10394     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10395     */
10396     /**
10397     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10398     */
10399     /**
10400     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10401     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10402     */
10403     /**
10404     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10405     * on any HTTP request
10406     */
10407     /**
10408     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10409     */
10410     /**
10411     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10412     */
10413     multiSort: false,
10414     /**
10415     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10416     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10417     */
10418     remoteSort : false,
10419
10420     /**
10421     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10422      * loaded or when a record is removed. (defaults to false).
10423     */
10424     pruneModifiedRecords : false,
10425
10426     // private
10427     lastOptions : null,
10428
10429     /**
10430      * Add Records to the Store and fires the add event.
10431      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10432      */
10433     add : function(records){
10434         records = [].concat(records);
10435         for(var i = 0, len = records.length; i < len; i++){
10436             records[i].join(this);
10437         }
10438         var index = this.data.length;
10439         this.data.addAll(records);
10440         this.fireEvent("add", this, records, index);
10441     },
10442
10443     /**
10444      * Remove a Record from the Store and fires the remove event.
10445      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10446      */
10447     remove : function(record){
10448         var index = this.data.indexOf(record);
10449         this.data.removeAt(index);
10450         if(this.pruneModifiedRecords){
10451             this.modified.remove(record);
10452         }
10453         this.fireEvent("remove", this, record, index);
10454     },
10455
10456     /**
10457      * Remove all Records from the Store and fires the clear event.
10458      */
10459     removeAll : function(){
10460         this.data.clear();
10461         if(this.pruneModifiedRecords){
10462             this.modified = [];
10463         }
10464         this.fireEvent("clear", this);
10465     },
10466
10467     /**
10468      * Inserts Records to the Store at the given index and fires the add event.
10469      * @param {Number} index The start index at which to insert the passed Records.
10470      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10471      */
10472     insert : function(index, records){
10473         records = [].concat(records);
10474         for(var i = 0, len = records.length; i < len; i++){
10475             this.data.insert(index, records[i]);
10476             records[i].join(this);
10477         }
10478         this.fireEvent("add", this, records, index);
10479     },
10480
10481     /**
10482      * Get the index within the cache of the passed Record.
10483      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10484      * @return {Number} The index of the passed Record. Returns -1 if not found.
10485      */
10486     indexOf : function(record){
10487         return this.data.indexOf(record);
10488     },
10489
10490     /**
10491      * Get the index within the cache of the Record with the passed id.
10492      * @param {String} id The id of the Record to find.
10493      * @return {Number} The index of the Record. Returns -1 if not found.
10494      */
10495     indexOfId : function(id){
10496         return this.data.indexOfKey(id);
10497     },
10498
10499     /**
10500      * Get the Record with the specified id.
10501      * @param {String} id The id of the Record to find.
10502      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10503      */
10504     getById : function(id){
10505         return this.data.key(id);
10506     },
10507
10508     /**
10509      * Get the Record at the specified index.
10510      * @param {Number} index The index of the Record to find.
10511      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10512      */
10513     getAt : function(index){
10514         return this.data.itemAt(index);
10515     },
10516
10517     /**
10518      * Returns a range of Records between specified indices.
10519      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10520      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10521      * @return {Roo.data.Record[]} An array of Records
10522      */
10523     getRange : function(start, end){
10524         return this.data.getRange(start, end);
10525     },
10526
10527     // private
10528     storeOptions : function(o){
10529         o = Roo.apply({}, o);
10530         delete o.callback;
10531         delete o.scope;
10532         this.lastOptions = o;
10533     },
10534
10535     /**
10536      * Loads the Record cache from the configured Proxy using the configured Reader.
10537      * <p>
10538      * If using remote paging, then the first load call must specify the <em>start</em>
10539      * and <em>limit</em> properties in the options.params property to establish the initial
10540      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10541      * <p>
10542      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10543      * and this call will return before the new data has been loaded. Perform any post-processing
10544      * in a callback function, or in a "load" event handler.</strong>
10545      * <p>
10546      * @param {Object} options An object containing properties which control loading options:<ul>
10547      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10548      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10549      * passed the following arguments:<ul>
10550      * <li>r : Roo.data.Record[]</li>
10551      * <li>options: Options object from the load call</li>
10552      * <li>success: Boolean success indicator</li></ul></li>
10553      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10554      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10555      * </ul>
10556      */
10557     load : function(options){
10558         options = options || {};
10559         if(this.fireEvent("beforeload", this, options) !== false){
10560             this.storeOptions(options);
10561             var p = Roo.apply(options.params || {}, this.baseParams);
10562             // if meta was not loaded from remote source.. try requesting it.
10563             if (!this.reader.metaFromRemote) {
10564                 p._requestMeta = 1;
10565             }
10566             if(this.sortInfo && this.remoteSort){
10567                 var pn = this.paramNames;
10568                 p[pn["sort"]] = this.sortInfo.field;
10569                 p[pn["dir"]] = this.sortInfo.direction;
10570             }
10571             if (this.multiSort) {
10572                 var pn = this.paramNames;
10573                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10574             }
10575             
10576             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10577         }
10578     },
10579
10580     /**
10581      * Reloads the Record cache from the configured Proxy using the configured Reader and
10582      * the options from the last load operation performed.
10583      * @param {Object} options (optional) An object containing properties which may override the options
10584      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10585      * the most recently used options are reused).
10586      */
10587     reload : function(options){
10588         this.load(Roo.applyIf(options||{}, this.lastOptions));
10589     },
10590
10591     // private
10592     // Called as a callback by the Reader during a load operation.
10593     loadRecords : function(o, options, success){
10594         if(!o || success === false){
10595             if(success !== false){
10596                 this.fireEvent("load", this, [], options, o);
10597             }
10598             if(options.callback){
10599                 options.callback.call(options.scope || this, [], options, false);
10600             }
10601             return;
10602         }
10603         // if data returned failure - throw an exception.
10604         if (o.success === false) {
10605             // show a message if no listener is registered.
10606             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10607                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10608             }
10609             // loadmask wil be hooked into this..
10610             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10611             return;
10612         }
10613         var r = o.records, t = o.totalRecords || r.length;
10614         
10615         this.fireEvent("beforeloadadd", this, r, options, o);
10616         
10617         if(!options || options.add !== true){
10618             if(this.pruneModifiedRecords){
10619                 this.modified = [];
10620             }
10621             for(var i = 0, len = r.length; i < len; i++){
10622                 r[i].join(this);
10623             }
10624             if(this.snapshot){
10625                 this.data = this.snapshot;
10626                 delete this.snapshot;
10627             }
10628             this.data.clear();
10629             this.data.addAll(r);
10630             this.totalLength = t;
10631             this.applySort();
10632             this.fireEvent("datachanged", this);
10633         }else{
10634             this.totalLength = Math.max(t, this.data.length+r.length);
10635             this.add(r);
10636         }
10637         this.fireEvent("load", this, r, options, o);
10638         if(options.callback){
10639             options.callback.call(options.scope || this, r, options, true);
10640         }
10641     },
10642
10643
10644     /**
10645      * Loads data from a passed data block. A Reader which understands the format of the data
10646      * must have been configured in the constructor.
10647      * @param {Object} data The data block from which to read the Records.  The format of the data expected
10648      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10649      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10650      */
10651     loadData : function(o, append){
10652         var r = this.reader.readRecords(o);
10653         this.loadRecords(r, {add: append}, true);
10654     },
10655
10656     /**
10657      * Gets the number of cached records.
10658      * <p>
10659      * <em>If using paging, this may not be the total size of the dataset. If the data object
10660      * used by the Reader contains the dataset size, then the getTotalCount() function returns
10661      * the data set size</em>
10662      */
10663     getCount : function(){
10664         return this.data.length || 0;
10665     },
10666
10667     /**
10668      * Gets the total number of records in the dataset as returned by the server.
10669      * <p>
10670      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10671      * the dataset size</em>
10672      */
10673     getTotalCount : function(){
10674         return this.totalLength || 0;
10675     },
10676
10677     /**
10678      * Returns the sort state of the Store as an object with two properties:
10679      * <pre><code>
10680  field {String} The name of the field by which the Records are sorted
10681  direction {String} The sort order, "ASC" or "DESC"
10682      * </code></pre>
10683      */
10684     getSortState : function(){
10685         return this.sortInfo;
10686     },
10687
10688     // private
10689     applySort : function(){
10690         if(this.sortInfo && !this.remoteSort){
10691             var s = this.sortInfo, f = s.field;
10692             var st = this.fields.get(f).sortType;
10693             var fn = function(r1, r2){
10694                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10695                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10696             };
10697             this.data.sort(s.direction, fn);
10698             if(this.snapshot && this.snapshot != this.data){
10699                 this.snapshot.sort(s.direction, fn);
10700             }
10701         }
10702     },
10703
10704     /**
10705      * Sets the default sort column and order to be used by the next load operation.
10706      * @param {String} fieldName The name of the field to sort by.
10707      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10708      */
10709     setDefaultSort : function(field, dir){
10710         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10711     },
10712
10713     /**
10714      * Sort the Records.
10715      * If remote sorting is used, the sort is performed on the server, and the cache is
10716      * reloaded. If local sorting is used, the cache is sorted internally.
10717      * @param {String} fieldName The name of the field to sort by.
10718      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10719      */
10720     sort : function(fieldName, dir){
10721         var f = this.fields.get(fieldName);
10722         if(!dir){
10723             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10724             
10725             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10726                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10727             }else{
10728                 dir = f.sortDir;
10729             }
10730         }
10731         this.sortToggle[f.name] = dir;
10732         this.sortInfo = {field: f.name, direction: dir};
10733         if(!this.remoteSort){
10734             this.applySort();
10735             this.fireEvent("datachanged", this);
10736         }else{
10737             this.load(this.lastOptions);
10738         }
10739     },
10740
10741     /**
10742      * Calls the specified function for each of the Records in the cache.
10743      * @param {Function} fn The function to call. The Record is passed as the first parameter.
10744      * Returning <em>false</em> aborts and exits the iteration.
10745      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10746      */
10747     each : function(fn, scope){
10748         this.data.each(fn, scope);
10749     },
10750
10751     /**
10752      * Gets all records modified since the last commit.  Modified records are persisted across load operations
10753      * (e.g., during paging).
10754      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10755      */
10756     getModifiedRecords : function(){
10757         return this.modified;
10758     },
10759
10760     // private
10761     createFilterFn : function(property, value, anyMatch){
10762         if(!value.exec){ // not a regex
10763             value = String(value);
10764             if(value.length == 0){
10765                 return false;
10766             }
10767             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10768         }
10769         return function(r){
10770             return value.test(r.data[property]);
10771         };
10772     },
10773
10774     /**
10775      * Sums the value of <i>property</i> for each record between start and end and returns the result.
10776      * @param {String} property A field on your records
10777      * @param {Number} start The record index to start at (defaults to 0)
10778      * @param {Number} end The last record index to include (defaults to length - 1)
10779      * @return {Number} The sum
10780      */
10781     sum : function(property, start, end){
10782         var rs = this.data.items, v = 0;
10783         start = start || 0;
10784         end = (end || end === 0) ? end : rs.length-1;
10785
10786         for(var i = start; i <= end; i++){
10787             v += (rs[i].data[property] || 0);
10788         }
10789         return v;
10790     },
10791
10792     /**
10793      * Filter the records by a specified property.
10794      * @param {String} field A field on your records
10795      * @param {String/RegExp} value Either a string that the field
10796      * should start with or a RegExp to test against the field
10797      * @param {Boolean} anyMatch True to match any part not just the beginning
10798      */
10799     filter : function(property, value, anyMatch){
10800         var fn = this.createFilterFn(property, value, anyMatch);
10801         return fn ? this.filterBy(fn) : this.clearFilter();
10802     },
10803
10804     /**
10805      * Filter by a function. The specified function will be called with each
10806      * record in this data source. If the function returns true the record is included,
10807      * otherwise it is filtered.
10808      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10809      * @param {Object} scope (optional) The scope of the function (defaults to this)
10810      */
10811     filterBy : function(fn, scope){
10812         this.snapshot = this.snapshot || this.data;
10813         this.data = this.queryBy(fn, scope||this);
10814         this.fireEvent("datachanged", this);
10815     },
10816
10817     /**
10818      * Query the records by a specified property.
10819      * @param {String} field A field on your records
10820      * @param {String/RegExp} value Either a string that the field
10821      * should start with or a RegExp to test against the field
10822      * @param {Boolean} anyMatch True to match any part not just the beginning
10823      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10824      */
10825     query : function(property, value, anyMatch){
10826         var fn = this.createFilterFn(property, value, anyMatch);
10827         return fn ? this.queryBy(fn) : this.data.clone();
10828     },
10829
10830     /**
10831      * Query by a function. The specified function will be called with each
10832      * record in this data source. If the function returns true the record is included
10833      * in the results.
10834      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10835      * @param {Object} scope (optional) The scope of the function (defaults to this)
10836       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10837      **/
10838     queryBy : function(fn, scope){
10839         var data = this.snapshot || this.data;
10840         return data.filterBy(fn, scope||this);
10841     },
10842
10843     /**
10844      * Collects unique values for a particular dataIndex from this store.
10845      * @param {String} dataIndex The property to collect
10846      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10847      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10848      * @return {Array} An array of the unique values
10849      **/
10850     collect : function(dataIndex, allowNull, bypassFilter){
10851         var d = (bypassFilter === true && this.snapshot) ?
10852                 this.snapshot.items : this.data.items;
10853         var v, sv, r = [], l = {};
10854         for(var i = 0, len = d.length; i < len; i++){
10855             v = d[i].data[dataIndex];
10856             sv = String(v);
10857             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10858                 l[sv] = true;
10859                 r[r.length] = v;
10860             }
10861         }
10862         return r;
10863     },
10864
10865     /**
10866      * Revert to a view of the Record cache with no filtering applied.
10867      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10868      */
10869     clearFilter : function(suppressEvent){
10870         if(this.snapshot && this.snapshot != this.data){
10871             this.data = this.snapshot;
10872             delete this.snapshot;
10873             if(suppressEvent !== true){
10874                 this.fireEvent("datachanged", this);
10875             }
10876         }
10877     },
10878
10879     // private
10880     afterEdit : function(record){
10881         if(this.modified.indexOf(record) == -1){
10882             this.modified.push(record);
10883         }
10884         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10885     },
10886     
10887     // private
10888     afterReject : function(record){
10889         this.modified.remove(record);
10890         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10891     },
10892
10893     // private
10894     afterCommit : function(record){
10895         this.modified.remove(record);
10896         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10897     },
10898
10899     /**
10900      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10901      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10902      */
10903     commitChanges : function(){
10904         var m = this.modified.slice(0);
10905         this.modified = [];
10906         for(var i = 0, len = m.length; i < len; i++){
10907             m[i].commit();
10908         }
10909     },
10910
10911     /**
10912      * Cancel outstanding changes on all changed records.
10913      */
10914     rejectChanges : function(){
10915         var m = this.modified.slice(0);
10916         this.modified = [];
10917         for(var i = 0, len = m.length; i < len; i++){
10918             m[i].reject();
10919         }
10920     },
10921
10922     onMetaChange : function(meta, rtype, o){
10923         this.recordType = rtype;
10924         this.fields = rtype.prototype.fields;
10925         delete this.snapshot;
10926         this.sortInfo = meta.sortInfo || this.sortInfo;
10927         this.modified = [];
10928         this.fireEvent('metachange', this, this.reader.meta);
10929     },
10930     
10931     moveIndex : function(data, type)
10932     {
10933         var index = this.indexOf(data);
10934         
10935         var newIndex = index + type;
10936         
10937         this.remove(data);
10938         
10939         this.insert(newIndex, data);
10940         
10941     }
10942 });/*
10943  * Based on:
10944  * Ext JS Library 1.1.1
10945  * Copyright(c) 2006-2007, Ext JS, LLC.
10946  *
10947  * Originally Released Under LGPL - original licence link has changed is not relivant.
10948  *
10949  * Fork - LGPL
10950  * <script type="text/javascript">
10951  */
10952
10953 /**
10954  * @class Roo.data.SimpleStore
10955  * @extends Roo.data.Store
10956  * Small helper class to make creating Stores from Array data easier.
10957  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10958  * @cfg {Array} fields An array of field definition objects, or field name strings.
10959  * @cfg {Array} data The multi-dimensional array of data
10960  * @constructor
10961  * @param {Object} config
10962  */
10963 Roo.data.SimpleStore = function(config){
10964     Roo.data.SimpleStore.superclass.constructor.call(this, {
10965         isLocal : true,
10966         reader: new Roo.data.ArrayReader({
10967                 id: config.id
10968             },
10969             Roo.data.Record.create(config.fields)
10970         ),
10971         proxy : new Roo.data.MemoryProxy(config.data)
10972     });
10973     this.load();
10974 };
10975 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10976  * Based on:
10977  * Ext JS Library 1.1.1
10978  * Copyright(c) 2006-2007, Ext JS, LLC.
10979  *
10980  * Originally Released Under LGPL - original licence link has changed is not relivant.
10981  *
10982  * Fork - LGPL
10983  * <script type="text/javascript">
10984  */
10985
10986 /**
10987 /**
10988  * @extends Roo.data.Store
10989  * @class Roo.data.JsonStore
10990  * Small helper class to make creating Stores for JSON data easier. <br/>
10991 <pre><code>
10992 var store = new Roo.data.JsonStore({
10993     url: 'get-images.php',
10994     root: 'images',
10995     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10996 });
10997 </code></pre>
10998  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10999  * JsonReader and HttpProxy (unless inline data is provided).</b>
11000  * @cfg {Array} fields An array of field definition objects, or field name strings.
11001  * @constructor
11002  * @param {Object} config
11003  */
11004 Roo.data.JsonStore = function(c){
11005     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11006         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11007         reader: new Roo.data.JsonReader(c, c.fields)
11008     }));
11009 };
11010 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11011  * Based on:
11012  * Ext JS Library 1.1.1
11013  * Copyright(c) 2006-2007, Ext JS, LLC.
11014  *
11015  * Originally Released Under LGPL - original licence link has changed is not relivant.
11016  *
11017  * Fork - LGPL
11018  * <script type="text/javascript">
11019  */
11020
11021  
11022 Roo.data.Field = function(config){
11023     if(typeof config == "string"){
11024         config = {name: config};
11025     }
11026     Roo.apply(this, config);
11027     
11028     if(!this.type){
11029         this.type = "auto";
11030     }
11031     
11032     var st = Roo.data.SortTypes;
11033     // named sortTypes are supported, here we look them up
11034     if(typeof this.sortType == "string"){
11035         this.sortType = st[this.sortType];
11036     }
11037     
11038     // set default sortType for strings and dates
11039     if(!this.sortType){
11040         switch(this.type){
11041             case "string":
11042                 this.sortType = st.asUCString;
11043                 break;
11044             case "date":
11045                 this.sortType = st.asDate;
11046                 break;
11047             default:
11048                 this.sortType = st.none;
11049         }
11050     }
11051
11052     // define once
11053     var stripRe = /[\$,%]/g;
11054
11055     // prebuilt conversion function for this field, instead of
11056     // switching every time we're reading a value
11057     if(!this.convert){
11058         var cv, dateFormat = this.dateFormat;
11059         switch(this.type){
11060             case "":
11061             case "auto":
11062             case undefined:
11063                 cv = function(v){ return v; };
11064                 break;
11065             case "string":
11066                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11067                 break;
11068             case "int":
11069                 cv = function(v){
11070                     return v !== undefined && v !== null && v !== '' ?
11071                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11072                     };
11073                 break;
11074             case "float":
11075                 cv = function(v){
11076                     return v !== undefined && v !== null && v !== '' ?
11077                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11078                     };
11079                 break;
11080             case "bool":
11081             case "boolean":
11082                 cv = function(v){ return v === true || v === "true" || v == 1; };
11083                 break;
11084             case "date":
11085                 cv = function(v){
11086                     if(!v){
11087                         return '';
11088                     }
11089                     if(v instanceof Date){
11090                         return v;
11091                     }
11092                     if(dateFormat){
11093                         if(dateFormat == "timestamp"){
11094                             return new Date(v*1000);
11095                         }
11096                         return Date.parseDate(v, dateFormat);
11097                     }
11098                     var parsed = Date.parse(v);
11099                     return parsed ? new Date(parsed) : null;
11100                 };
11101              break;
11102             
11103         }
11104         this.convert = cv;
11105     }
11106 };
11107
11108 Roo.data.Field.prototype = {
11109     dateFormat: null,
11110     defaultValue: "",
11111     mapping: null,
11112     sortType : null,
11113     sortDir : "ASC"
11114 };/*
11115  * Based on:
11116  * Ext JS Library 1.1.1
11117  * Copyright(c) 2006-2007, Ext JS, LLC.
11118  *
11119  * Originally Released Under LGPL - original licence link has changed is not relivant.
11120  *
11121  * Fork - LGPL
11122  * <script type="text/javascript">
11123  */
11124  
11125 // Base class for reading structured data from a data source.  This class is intended to be
11126 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11127
11128 /**
11129  * @class Roo.data.DataReader
11130  * Base class for reading structured data from a data source.  This class is intended to be
11131  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11132  */
11133
11134 Roo.data.DataReader = function(meta, recordType){
11135     
11136     this.meta = meta;
11137     
11138     this.recordType = recordType instanceof Array ? 
11139         Roo.data.Record.create(recordType) : recordType;
11140 };
11141
11142 Roo.data.DataReader.prototype = {
11143      /**
11144      * Create an empty record
11145      * @param {Object} data (optional) - overlay some values
11146      * @return {Roo.data.Record} record created.
11147      */
11148     newRow :  function(d) {
11149         var da =  {};
11150         this.recordType.prototype.fields.each(function(c) {
11151             switch( c.type) {
11152                 case 'int' : da[c.name] = 0; break;
11153                 case 'date' : da[c.name] = new Date(); break;
11154                 case 'float' : da[c.name] = 0.0; break;
11155                 case 'boolean' : da[c.name] = false; break;
11156                 default : da[c.name] = ""; break;
11157             }
11158             
11159         });
11160         return new this.recordType(Roo.apply(da, d));
11161     }
11162     
11163 };/*
11164  * Based on:
11165  * Ext JS Library 1.1.1
11166  * Copyright(c) 2006-2007, Ext JS, LLC.
11167  *
11168  * Originally Released Under LGPL - original licence link has changed is not relivant.
11169  *
11170  * Fork - LGPL
11171  * <script type="text/javascript">
11172  */
11173
11174 /**
11175  * @class Roo.data.DataProxy
11176  * @extends Roo.data.Observable
11177  * This class is an abstract base class for implementations which provide retrieval of
11178  * unformatted data objects.<br>
11179  * <p>
11180  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11181  * (of the appropriate type which knows how to parse the data object) to provide a block of
11182  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11183  * <p>
11184  * Custom implementations must implement the load method as described in
11185  * {@link Roo.data.HttpProxy#load}.
11186  */
11187 Roo.data.DataProxy = function(){
11188     this.addEvents({
11189         /**
11190          * @event beforeload
11191          * Fires before a network request is made to retrieve a data object.
11192          * @param {Object} This DataProxy object.
11193          * @param {Object} params The params parameter to the load function.
11194          */
11195         beforeload : true,
11196         /**
11197          * @event load
11198          * Fires before the load method's callback is called.
11199          * @param {Object} This DataProxy object.
11200          * @param {Object} o The data object.
11201          * @param {Object} arg The callback argument object passed to the load function.
11202          */
11203         load : true,
11204         /**
11205          * @event loadexception
11206          * Fires if an Exception occurs during data retrieval.
11207          * @param {Object} This DataProxy object.
11208          * @param {Object} o The data object.
11209          * @param {Object} arg The callback argument object passed to the load function.
11210          * @param {Object} e The Exception.
11211          */
11212         loadexception : true
11213     });
11214     Roo.data.DataProxy.superclass.constructor.call(this);
11215 };
11216
11217 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11218
11219     /**
11220      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11221      */
11222 /*
11223  * Based on:
11224  * Ext JS Library 1.1.1
11225  * Copyright(c) 2006-2007, Ext JS, LLC.
11226  *
11227  * Originally Released Under LGPL - original licence link has changed is not relivant.
11228  *
11229  * Fork - LGPL
11230  * <script type="text/javascript">
11231  */
11232 /**
11233  * @class Roo.data.MemoryProxy
11234  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11235  * to the Reader when its load method is called.
11236  * @constructor
11237  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11238  */
11239 Roo.data.MemoryProxy = function(data){
11240     if (data.data) {
11241         data = data.data;
11242     }
11243     Roo.data.MemoryProxy.superclass.constructor.call(this);
11244     this.data = data;
11245 };
11246
11247 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11248     
11249     /**
11250      * Load data from the requested source (in this case an in-memory
11251      * data object passed to the constructor), read the data object into
11252      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11253      * process that block using the passed callback.
11254      * @param {Object} params This parameter is not used by the MemoryProxy class.
11255      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11256      * object into a block of Roo.data.Records.
11257      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11258      * The function must be passed <ul>
11259      * <li>The Record block object</li>
11260      * <li>The "arg" argument from the load function</li>
11261      * <li>A boolean success indicator</li>
11262      * </ul>
11263      * @param {Object} scope The scope in which to call the callback
11264      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11265      */
11266     load : function(params, reader, callback, scope, arg){
11267         params = params || {};
11268         var result;
11269         try {
11270             result = reader.readRecords(this.data);
11271         }catch(e){
11272             this.fireEvent("loadexception", this, arg, null, e);
11273             callback.call(scope, null, arg, false);
11274             return;
11275         }
11276         callback.call(scope, result, arg, true);
11277     },
11278     
11279     // private
11280     update : function(params, records){
11281         
11282     }
11283 });/*
11284  * Based on:
11285  * Ext JS Library 1.1.1
11286  * Copyright(c) 2006-2007, Ext JS, LLC.
11287  *
11288  * Originally Released Under LGPL - original licence link has changed is not relivant.
11289  *
11290  * Fork - LGPL
11291  * <script type="text/javascript">
11292  */
11293 /**
11294  * @class Roo.data.HttpProxy
11295  * @extends Roo.data.DataProxy
11296  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11297  * configured to reference a certain URL.<br><br>
11298  * <p>
11299  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11300  * from which the running page was served.<br><br>
11301  * <p>
11302  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11303  * <p>
11304  * Be aware that to enable the browser to parse an XML document, the server must set
11305  * the Content-Type header in the HTTP response to "text/xml".
11306  * @constructor
11307  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11308  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11309  * will be used to make the request.
11310  */
11311 Roo.data.HttpProxy = function(conn){
11312     Roo.data.HttpProxy.superclass.constructor.call(this);
11313     // is conn a conn config or a real conn?
11314     this.conn = conn;
11315     this.useAjax = !conn || !conn.events;
11316   
11317 };
11318
11319 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11320     // thse are take from connection...
11321     
11322     /**
11323      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11324      */
11325     /**
11326      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11327      * extra parameters to each request made by this object. (defaults to undefined)
11328      */
11329     /**
11330      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11331      *  to each request made by this object. (defaults to undefined)
11332      */
11333     /**
11334      * @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)
11335      */
11336     /**
11337      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11338      */
11339      /**
11340      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11341      * @type Boolean
11342      */
11343   
11344
11345     /**
11346      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11347      * @type Boolean
11348      */
11349     /**
11350      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11351      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11352      * a finer-grained basis than the DataProxy events.
11353      */
11354     getConnection : function(){
11355         return this.useAjax ? Roo.Ajax : this.conn;
11356     },
11357
11358     /**
11359      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11360      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11361      * process that block using the passed callback.
11362      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11363      * for the request to the remote server.
11364      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11365      * object into a block of Roo.data.Records.
11366      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11367      * The function must be passed <ul>
11368      * <li>The Record block object</li>
11369      * <li>The "arg" argument from the load function</li>
11370      * <li>A boolean success indicator</li>
11371      * </ul>
11372      * @param {Object} scope The scope in which to call the callback
11373      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11374      */
11375     load : function(params, reader, callback, scope, arg){
11376         if(this.fireEvent("beforeload", this, params) !== false){
11377             var  o = {
11378                 params : params || {},
11379                 request: {
11380                     callback : callback,
11381                     scope : scope,
11382                     arg : arg
11383                 },
11384                 reader: reader,
11385                 callback : this.loadResponse,
11386                 scope: this
11387             };
11388             if(this.useAjax){
11389                 Roo.applyIf(o, this.conn);
11390                 if(this.activeRequest){
11391                     Roo.Ajax.abort(this.activeRequest);
11392                 }
11393                 this.activeRequest = Roo.Ajax.request(o);
11394             }else{
11395                 this.conn.request(o);
11396             }
11397         }else{
11398             callback.call(scope||this, null, arg, false);
11399         }
11400     },
11401
11402     // private
11403     loadResponse : function(o, success, response){
11404         delete this.activeRequest;
11405         if(!success){
11406             this.fireEvent("loadexception", this, o, response);
11407             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11408             return;
11409         }
11410         var result;
11411         try {
11412             result = o.reader.read(response);
11413         }catch(e){
11414             this.fireEvent("loadexception", this, o, response, e);
11415             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11416             return;
11417         }
11418         
11419         this.fireEvent("load", this, o, o.request.arg);
11420         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11421     },
11422
11423     // private
11424     update : function(dataSet){
11425
11426     },
11427
11428     // private
11429     updateResponse : function(dataSet){
11430
11431     }
11432 });/*
11433  * Based on:
11434  * Ext JS Library 1.1.1
11435  * Copyright(c) 2006-2007, Ext JS, LLC.
11436  *
11437  * Originally Released Under LGPL - original licence link has changed is not relivant.
11438  *
11439  * Fork - LGPL
11440  * <script type="text/javascript">
11441  */
11442
11443 /**
11444  * @class Roo.data.ScriptTagProxy
11445  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11446  * other than the originating domain of the running page.<br><br>
11447  * <p>
11448  * <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
11449  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11450  * <p>
11451  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11452  * source code that is used as the source inside a &lt;script> tag.<br><br>
11453  * <p>
11454  * In order for the browser to process the returned data, the server must wrap the data object
11455  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11456  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11457  * depending on whether the callback name was passed:
11458  * <p>
11459  * <pre><code>
11460 boolean scriptTag = false;
11461 String cb = request.getParameter("callback");
11462 if (cb != null) {
11463     scriptTag = true;
11464     response.setContentType("text/javascript");
11465 } else {
11466     response.setContentType("application/x-json");
11467 }
11468 Writer out = response.getWriter();
11469 if (scriptTag) {
11470     out.write(cb + "(");
11471 }
11472 out.print(dataBlock.toJsonString());
11473 if (scriptTag) {
11474     out.write(");");
11475 }
11476 </pre></code>
11477  *
11478  * @constructor
11479  * @param {Object} config A configuration object.
11480  */
11481 Roo.data.ScriptTagProxy = function(config){
11482     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11483     Roo.apply(this, config);
11484     this.head = document.getElementsByTagName("head")[0];
11485 };
11486
11487 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11488
11489 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11490     /**
11491      * @cfg {String} url The URL from which to request the data object.
11492      */
11493     /**
11494      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11495      */
11496     timeout : 30000,
11497     /**
11498      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11499      * the server the name of the callback function set up by the load call to process the returned data object.
11500      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11501      * javascript output which calls this named function passing the data object as its only parameter.
11502      */
11503     callbackParam : "callback",
11504     /**
11505      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11506      * name to the request.
11507      */
11508     nocache : true,
11509
11510     /**
11511      * Load data from the configured URL, read the data object into
11512      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11513      * process that block using the passed callback.
11514      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11515      * for the request to the remote server.
11516      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11517      * object into a block of Roo.data.Records.
11518      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11519      * The function must be passed <ul>
11520      * <li>The Record block object</li>
11521      * <li>The "arg" argument from the load function</li>
11522      * <li>A boolean success indicator</li>
11523      * </ul>
11524      * @param {Object} scope The scope in which to call the callback
11525      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11526      */
11527     load : function(params, reader, callback, scope, arg){
11528         if(this.fireEvent("beforeload", this, params) !== false){
11529
11530             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11531
11532             var url = this.url;
11533             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11534             if(this.nocache){
11535                 url += "&_dc=" + (new Date().getTime());
11536             }
11537             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11538             var trans = {
11539                 id : transId,
11540                 cb : "stcCallback"+transId,
11541                 scriptId : "stcScript"+transId,
11542                 params : params,
11543                 arg : arg,
11544                 url : url,
11545                 callback : callback,
11546                 scope : scope,
11547                 reader : reader
11548             };
11549             var conn = this;
11550
11551             window[trans.cb] = function(o){
11552                 conn.handleResponse(o, trans);
11553             };
11554
11555             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11556
11557             if(this.autoAbort !== false){
11558                 this.abort();
11559             }
11560
11561             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11562
11563             var script = document.createElement("script");
11564             script.setAttribute("src", url);
11565             script.setAttribute("type", "text/javascript");
11566             script.setAttribute("id", trans.scriptId);
11567             this.head.appendChild(script);
11568
11569             this.trans = trans;
11570         }else{
11571             callback.call(scope||this, null, arg, false);
11572         }
11573     },
11574
11575     // private
11576     isLoading : function(){
11577         return this.trans ? true : false;
11578     },
11579
11580     /**
11581      * Abort the current server request.
11582      */
11583     abort : function(){
11584         if(this.isLoading()){
11585             this.destroyTrans(this.trans);
11586         }
11587     },
11588
11589     // private
11590     destroyTrans : function(trans, isLoaded){
11591         this.head.removeChild(document.getElementById(trans.scriptId));
11592         clearTimeout(trans.timeoutId);
11593         if(isLoaded){
11594             window[trans.cb] = undefined;
11595             try{
11596                 delete window[trans.cb];
11597             }catch(e){}
11598         }else{
11599             // if hasn't been loaded, wait for load to remove it to prevent script error
11600             window[trans.cb] = function(){
11601                 window[trans.cb] = undefined;
11602                 try{
11603                     delete window[trans.cb];
11604                 }catch(e){}
11605             };
11606         }
11607     },
11608
11609     // private
11610     handleResponse : function(o, trans){
11611         this.trans = false;
11612         this.destroyTrans(trans, true);
11613         var result;
11614         try {
11615             result = trans.reader.readRecords(o);
11616         }catch(e){
11617             this.fireEvent("loadexception", this, o, trans.arg, e);
11618             trans.callback.call(trans.scope||window, null, trans.arg, false);
11619             return;
11620         }
11621         this.fireEvent("load", this, o, trans.arg);
11622         trans.callback.call(trans.scope||window, result, trans.arg, true);
11623     },
11624
11625     // private
11626     handleFailure : function(trans){
11627         this.trans = false;
11628         this.destroyTrans(trans, false);
11629         this.fireEvent("loadexception", this, null, trans.arg);
11630         trans.callback.call(trans.scope||window, null, trans.arg, false);
11631     }
11632 });/*
11633  * Based on:
11634  * Ext JS Library 1.1.1
11635  * Copyright(c) 2006-2007, Ext JS, LLC.
11636  *
11637  * Originally Released Under LGPL - original licence link has changed is not relivant.
11638  *
11639  * Fork - LGPL
11640  * <script type="text/javascript">
11641  */
11642
11643 /**
11644  * @class Roo.data.JsonReader
11645  * @extends Roo.data.DataReader
11646  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11647  * based on mappings in a provided Roo.data.Record constructor.
11648  * 
11649  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11650  * in the reply previously. 
11651  * 
11652  * <p>
11653  * Example code:
11654  * <pre><code>
11655 var RecordDef = Roo.data.Record.create([
11656     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
11657     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
11658 ]);
11659 var myReader = new Roo.data.JsonReader({
11660     totalProperty: "results",    // The property which contains the total dataset size (optional)
11661     root: "rows",                // The property which contains an Array of row objects
11662     id: "id"                     // The property within each row object that provides an ID for the record (optional)
11663 }, RecordDef);
11664 </code></pre>
11665  * <p>
11666  * This would consume a JSON file like this:
11667  * <pre><code>
11668 { 'results': 2, 'rows': [
11669     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11670     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11671 }
11672 </code></pre>
11673  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11674  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11675  * paged from the remote server.
11676  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11677  * @cfg {String} root name of the property which contains the Array of row objects.
11678  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11679  * @cfg {Array} fields Array of field definition objects
11680  * @constructor
11681  * Create a new JsonReader
11682  * @param {Object} meta Metadata configuration options
11683  * @param {Object} recordType Either an Array of field definition objects,
11684  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11685  */
11686 Roo.data.JsonReader = function(meta, recordType){
11687     
11688     meta = meta || {};
11689     // set some defaults:
11690     Roo.applyIf(meta, {
11691         totalProperty: 'total',
11692         successProperty : 'success',
11693         root : 'data',
11694         id : 'id'
11695     });
11696     
11697     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11698 };
11699 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11700     
11701     /**
11702      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
11703      * Used by Store query builder to append _requestMeta to params.
11704      * 
11705      */
11706     metaFromRemote : false,
11707     /**
11708      * This method is only used by a DataProxy which has retrieved data from a remote server.
11709      * @param {Object} response The XHR object which contains the JSON data in its responseText.
11710      * @return {Object} data A data block which is used by an Roo.data.Store object as
11711      * a cache of Roo.data.Records.
11712      */
11713     read : function(response){
11714         var json = response.responseText;
11715        
11716         var o = /* eval:var:o */ eval("("+json+")");
11717         if(!o) {
11718             throw {message: "JsonReader.read: Json object not found"};
11719         }
11720         
11721         if(o.metaData){
11722             
11723             delete this.ef;
11724             this.metaFromRemote = true;
11725             this.meta = o.metaData;
11726             this.recordType = Roo.data.Record.create(o.metaData.fields);
11727             this.onMetaChange(this.meta, this.recordType, o);
11728         }
11729         return this.readRecords(o);
11730     },
11731
11732     // private function a store will implement
11733     onMetaChange : function(meta, recordType, o){
11734
11735     },
11736
11737     /**
11738          * @ignore
11739          */
11740     simpleAccess: function(obj, subsc) {
11741         return obj[subsc];
11742     },
11743
11744         /**
11745          * @ignore
11746          */
11747     getJsonAccessor: function(){
11748         var re = /[\[\.]/;
11749         return function(expr) {
11750             try {
11751                 return(re.test(expr))
11752                     ? new Function("obj", "return obj." + expr)
11753                     : function(obj){
11754                         return obj[expr];
11755                     };
11756             } catch(e){}
11757             return Roo.emptyFn;
11758         };
11759     }(),
11760
11761     /**
11762      * Create a data block containing Roo.data.Records from an XML document.
11763      * @param {Object} o An object which contains an Array of row objects in the property specified
11764      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11765      * which contains the total size of the dataset.
11766      * @return {Object} data A data block which is used by an Roo.data.Store object as
11767      * a cache of Roo.data.Records.
11768      */
11769     readRecords : function(o){
11770         /**
11771          * After any data loads, the raw JSON data is available for further custom processing.
11772          * @type Object
11773          */
11774         this.o = o;
11775         var s = this.meta, Record = this.recordType,
11776             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11777
11778 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
11779         if (!this.ef) {
11780             if(s.totalProperty) {
11781                     this.getTotal = this.getJsonAccessor(s.totalProperty);
11782                 }
11783                 if(s.successProperty) {
11784                     this.getSuccess = this.getJsonAccessor(s.successProperty);
11785                 }
11786                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11787                 if (s.id) {
11788                         var g = this.getJsonAccessor(s.id);
11789                         this.getId = function(rec) {
11790                                 var r = g(rec);  
11791                                 return (r === undefined || r === "") ? null : r;
11792                         };
11793                 } else {
11794                         this.getId = function(){return null;};
11795                 }
11796             this.ef = [];
11797             for(var jj = 0; jj < fl; jj++){
11798                 f = fi[jj];
11799                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11800                 this.ef[jj] = this.getJsonAccessor(map);
11801             }
11802         }
11803
11804         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11805         if(s.totalProperty){
11806             var vt = parseInt(this.getTotal(o), 10);
11807             if(!isNaN(vt)){
11808                 totalRecords = vt;
11809             }
11810         }
11811         if(s.successProperty){
11812             var vs = this.getSuccess(o);
11813             if(vs === false || vs === 'false'){
11814                 success = false;
11815             }
11816         }
11817         var records = [];
11818         for(var i = 0; i < c; i++){
11819                 var n = root[i];
11820             var values = {};
11821             var id = this.getId(n);
11822             for(var j = 0; j < fl; j++){
11823                 f = fi[j];
11824             var v = this.ef[j](n);
11825             if (!f.convert) {
11826                 Roo.log('missing convert for ' + f.name);
11827                 Roo.log(f);
11828                 continue;
11829             }
11830             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11831             }
11832             var record = new Record(values, id);
11833             record.json = n;
11834             records[i] = record;
11835         }
11836         return {
11837             raw : o,
11838             success : success,
11839             records : records,
11840             totalRecords : totalRecords
11841         };
11842     }
11843 });/*
11844  * Based on:
11845  * Ext JS Library 1.1.1
11846  * Copyright(c) 2006-2007, Ext JS, LLC.
11847  *
11848  * Originally Released Under LGPL - original licence link has changed is not relivant.
11849  *
11850  * Fork - LGPL
11851  * <script type="text/javascript">
11852  */
11853
11854 /**
11855  * @class Roo.data.ArrayReader
11856  * @extends Roo.data.DataReader
11857  * Data reader class to create an Array of Roo.data.Record objects from an Array.
11858  * Each element of that Array represents a row of data fields. The
11859  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11860  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11861  * <p>
11862  * Example code:.
11863  * <pre><code>
11864 var RecordDef = Roo.data.Record.create([
11865     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11866     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11867 ]);
11868 var myReader = new Roo.data.ArrayReader({
11869     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11870 }, RecordDef);
11871 </code></pre>
11872  * <p>
11873  * This would consume an Array like this:
11874  * <pre><code>
11875 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11876   </code></pre>
11877  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11878  * @constructor
11879  * Create a new JsonReader
11880  * @param {Object} meta Metadata configuration options.
11881  * @param {Object} recordType Either an Array of field definition objects
11882  * as specified to {@link Roo.data.Record#create},
11883  * or an {@link Roo.data.Record} object
11884  * created using {@link Roo.data.Record#create}.
11885  */
11886 Roo.data.ArrayReader = function(meta, recordType){
11887     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11888 };
11889
11890 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11891     /**
11892      * Create a data block containing Roo.data.Records from an XML document.
11893      * @param {Object} o An Array of row objects which represents the dataset.
11894      * @return {Object} data A data block which is used by an Roo.data.Store object as
11895      * a cache of Roo.data.Records.
11896      */
11897     readRecords : function(o){
11898         var sid = this.meta ? this.meta.id : null;
11899         var recordType = this.recordType, fields = recordType.prototype.fields;
11900         var records = [];
11901         var root = o;
11902             for(var i = 0; i < root.length; i++){
11903                     var n = root[i];
11904                 var values = {};
11905                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11906                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11907                 var f = fields.items[j];
11908                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11909                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11910                 v = f.convert(v);
11911                 values[f.name] = v;
11912             }
11913                 var record = new recordType(values, id);
11914                 record.json = n;
11915                 records[records.length] = record;
11916             }
11917             return {
11918                 records : records,
11919                 totalRecords : records.length
11920             };
11921     }
11922 });/*
11923  * - LGPL
11924  * * 
11925  */
11926
11927 /**
11928  * @class Roo.bootstrap.ComboBox
11929  * @extends Roo.bootstrap.TriggerField
11930  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11931  * @cfg {Boolean} append (true|false) default false
11932  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11933  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11934  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11935  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11936  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11937  * @cfg {Boolean} animate default true
11938  * @cfg {Boolean} emptyResultText only for touch device
11939  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11940  * @constructor
11941  * Create a new ComboBox.
11942  * @param {Object} config Configuration options
11943  */
11944 Roo.bootstrap.ComboBox = function(config){
11945     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11946     this.addEvents({
11947         /**
11948          * @event expand
11949          * Fires when the dropdown list is expanded
11950              * @param {Roo.bootstrap.ComboBox} combo This combo box
11951              */
11952         'expand' : true,
11953         /**
11954          * @event collapse
11955          * Fires when the dropdown list is collapsed
11956              * @param {Roo.bootstrap.ComboBox} combo This combo box
11957              */
11958         'collapse' : true,
11959         /**
11960          * @event beforeselect
11961          * Fires before a list item is selected. Return false to cancel the selection.
11962              * @param {Roo.bootstrap.ComboBox} combo This combo box
11963              * @param {Roo.data.Record} record The data record returned from the underlying store
11964              * @param {Number} index The index of the selected item in the dropdown list
11965              */
11966         'beforeselect' : true,
11967         /**
11968          * @event select
11969          * Fires when a list item is selected
11970              * @param {Roo.bootstrap.ComboBox} combo This combo box
11971              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11972              * @param {Number} index The index of the selected item in the dropdown list
11973              */
11974         'select' : true,
11975         /**
11976          * @event beforequery
11977          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11978          * The event object passed has these properties:
11979              * @param {Roo.bootstrap.ComboBox} combo This combo box
11980              * @param {String} query The query
11981              * @param {Boolean} forceAll true to force "all" query
11982              * @param {Boolean} cancel true to cancel the query
11983              * @param {Object} e The query event object
11984              */
11985         'beforequery': true,
11986          /**
11987          * @event add
11988          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11989              * @param {Roo.bootstrap.ComboBox} combo This combo box
11990              */
11991         'add' : true,
11992         /**
11993          * @event edit
11994          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11995              * @param {Roo.bootstrap.ComboBox} combo This combo box
11996              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11997              */
11998         'edit' : true,
11999         /**
12000          * @event remove
12001          * Fires when the remove value from the combobox array
12002              * @param {Roo.bootstrap.ComboBox} combo This combo box
12003              */
12004         'remove' : true,
12005         /**
12006          * @event afterremove
12007          * Fires when the remove value from the combobox array
12008              * @param {Roo.bootstrap.ComboBox} combo This combo box
12009              */
12010         'afterremove' : true,
12011         /**
12012          * @event specialfilter
12013          * Fires when specialfilter
12014             * @param {Roo.bootstrap.ComboBox} combo This combo box
12015             */
12016         'specialfilter' : true,
12017         /**
12018          * @event tick
12019          * Fires when tick the element
12020             * @param {Roo.bootstrap.ComboBox} combo This combo box
12021             */
12022         'tick' : true,
12023         /**
12024          * @event touchviewdisplay
12025          * Fires when touch view require special display (default is using displayField)
12026             * @param {Roo.bootstrap.ComboBox} combo This combo box
12027             * @param {Object} cfg set html .
12028             */
12029         'touchviewdisplay' : true
12030         
12031     });
12032     
12033     this.item = [];
12034     this.tickItems = [];
12035     
12036     this.selectedIndex = -1;
12037     if(this.mode == 'local'){
12038         if(config.queryDelay === undefined){
12039             this.queryDelay = 10;
12040         }
12041         if(config.minChars === undefined){
12042             this.minChars = 0;
12043         }
12044     }
12045 };
12046
12047 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12048      
12049     /**
12050      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12051      * rendering into an Roo.Editor, defaults to false)
12052      */
12053     /**
12054      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12055      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12056      */
12057     /**
12058      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12059      */
12060     /**
12061      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12062      * the dropdown list (defaults to undefined, with no header element)
12063      */
12064
12065      /**
12066      * @cfg {String/Roo.Template} tpl The template to use to render the output
12067      */
12068      
12069      /**
12070      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12071      */
12072     listWidth: undefined,
12073     /**
12074      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12075      * mode = 'remote' or 'text' if mode = 'local')
12076      */
12077     displayField: undefined,
12078     
12079     /**
12080      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12081      * mode = 'remote' or 'value' if mode = 'local'). 
12082      * Note: use of a valueField requires the user make a selection
12083      * in order for a value to be mapped.
12084      */
12085     valueField: undefined,
12086     /**
12087      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12088      */
12089     modalTitle : '',
12090     
12091     /**
12092      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12093      * field's data value (defaults to the underlying DOM element's name)
12094      */
12095     hiddenName: undefined,
12096     /**
12097      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12098      */
12099     listClass: '',
12100     /**
12101      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12102      */
12103     selectedClass: 'active',
12104     
12105     /**
12106      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12107      */
12108     shadow:'sides',
12109     /**
12110      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12111      * anchor positions (defaults to 'tl-bl')
12112      */
12113     listAlign: 'tl-bl?',
12114     /**
12115      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12116      */
12117     maxHeight: 300,
12118     /**
12119      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12120      * query specified by the allQuery config option (defaults to 'query')
12121      */
12122     triggerAction: 'query',
12123     /**
12124      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12125      * (defaults to 4, does not apply if editable = false)
12126      */
12127     minChars : 4,
12128     /**
12129      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12130      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12131      */
12132     typeAhead: false,
12133     /**
12134      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12135      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12136      */
12137     queryDelay: 500,
12138     /**
12139      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12140      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12141      */
12142     pageSize: 0,
12143     /**
12144      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12145      * when editable = true (defaults to false)
12146      */
12147     selectOnFocus:false,
12148     /**
12149      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12150      */
12151     queryParam: 'query',
12152     /**
12153      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12154      * when mode = 'remote' (defaults to 'Loading...')
12155      */
12156     loadingText: 'Loading...',
12157     /**
12158      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12159      */
12160     resizable: false,
12161     /**
12162      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12163      */
12164     handleHeight : 8,
12165     /**
12166      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12167      * traditional select (defaults to true)
12168      */
12169     editable: true,
12170     /**
12171      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12172      */
12173     allQuery: '',
12174     /**
12175      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12176      */
12177     mode: 'remote',
12178     /**
12179      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12180      * listWidth has a higher value)
12181      */
12182     minListWidth : 70,
12183     /**
12184      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12185      * allow the user to set arbitrary text into the field (defaults to false)
12186      */
12187     forceSelection:false,
12188     /**
12189      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12190      * if typeAhead = true (defaults to 250)
12191      */
12192     typeAheadDelay : 250,
12193     /**
12194      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12195      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12196      */
12197     valueNotFoundText : undefined,
12198     /**
12199      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12200      */
12201     blockFocus : false,
12202     
12203     /**
12204      * @cfg {Boolean} disableClear Disable showing of clear button.
12205      */
12206     disableClear : false,
12207     /**
12208      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12209      */
12210     alwaysQuery : false,
12211     
12212     /**
12213      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12214      */
12215     multiple : false,
12216     
12217     /**
12218      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12219      */
12220     invalidClass : "has-warning",
12221     
12222     /**
12223      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12224      */
12225     validClass : "has-success",
12226     
12227     /**
12228      * @cfg {Boolean} specialFilter (true|false) special filter default false
12229      */
12230     specialFilter : false,
12231     
12232     /**
12233      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12234      */
12235     mobileTouchView : true,
12236     
12237     /**
12238      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12239      */
12240     useNativeIOS : false,
12241     
12242     ios_options : false,
12243     
12244     //private
12245     addicon : false,
12246     editicon: false,
12247     
12248     page: 0,
12249     hasQuery: false,
12250     append: false,
12251     loadNext: false,
12252     autoFocus : true,
12253     tickable : false,
12254     btnPosition : 'right',
12255     triggerList : true,
12256     showToggleBtn : true,
12257     animate : true,
12258     emptyResultText: 'Empty',
12259     triggerText : 'Select',
12260     
12261     // element that contains real text value.. (when hidden is used..)
12262     
12263     getAutoCreate : function()
12264     {
12265         var cfg = false;
12266         
12267         /*
12268          * Render classic select for iso
12269          */
12270         
12271         if(Roo.isIOS && this.useNativeIOS){
12272             cfg = this.getAutoCreateNativeIOS();
12273             return cfg;
12274         }
12275         
12276         /*
12277          * Touch Devices
12278          */
12279         
12280         if(Roo.isTouch && this.mobileTouchView){
12281             cfg = this.getAutoCreateTouchView();
12282             return cfg;;
12283         }
12284         
12285         /*
12286          *  Normal ComboBox
12287          */
12288         if(!this.tickable){
12289             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12290             return cfg;
12291         }
12292         
12293         /*
12294          *  ComboBox with tickable selections
12295          */
12296              
12297         var align = this.labelAlign || this.parentLabelAlign();
12298         
12299         cfg = {
12300             cls : 'form-group roo-combobox-tickable' //input-group
12301         };
12302         
12303         var buttons = {
12304             tag : 'div',
12305             cls : 'tickable-buttons',
12306             cn : [
12307                 {
12308                     tag : 'button',
12309                     type : 'button',
12310                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12311                     html : this.triggerText
12312                 },
12313                 {
12314                     tag : 'button',
12315                     type : 'button',
12316                     name : 'ok',
12317                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12318                     html : 'Done'
12319                 },
12320                 {
12321                     tag : 'button',
12322                     type : 'button',
12323                     name : 'cancel',
12324                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12325                     html : 'Cancel'
12326                 }
12327             ]
12328         };
12329         
12330         if(this.editable){
12331             buttons.cn.unshift({
12332                 tag: 'input',
12333                 cls: 'roo-select2-search-field-input'
12334             });
12335         }
12336         
12337         var _this = this;
12338         
12339         Roo.each(buttons.cn, function(c){
12340             if (_this.size) {
12341                 c.cls += ' btn-' + _this.size;
12342             }
12343
12344             if (_this.disabled) {
12345                 c.disabled = true;
12346             }
12347         });
12348         
12349         var box = {
12350             tag: 'div',
12351             cn: [
12352                 {
12353                     tag: 'input',
12354                     type : 'hidden',
12355                     cls: 'form-hidden-field'
12356                 },
12357                 {
12358                     tag: 'ul',
12359                     cls: 'roo-select2-choices',
12360                     cn:[
12361                         {
12362                             tag: 'li',
12363                             cls: 'roo-select2-search-field',
12364                             cn: [
12365
12366                                 buttons
12367                             ]
12368                         }
12369                     ]
12370                 }
12371             ]
12372         };
12373         
12374         var combobox = {
12375             cls: 'roo-select2-container input-group roo-select2-container-multi',
12376             cn: [
12377                 box
12378 //                {
12379 //                    tag: 'ul',
12380 //                    cls: 'typeahead typeahead-long dropdown-menu',
12381 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12382 //                }
12383             ]
12384         };
12385         
12386         if(this.hasFeedback && !this.allowBlank){
12387             
12388             var feedback = {
12389                 tag: 'span',
12390                 cls: 'glyphicon form-control-feedback'
12391             };
12392
12393             combobox.cn.push(feedback);
12394         }
12395         
12396         if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
12397             
12398 //                Roo.log("left and has label");
12399             cfg.cn = [
12400                 {
12401                     tag : 'i',
12402                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12403                     tooltip : 'This field is required'
12404                 },
12405                 {
12406                     tag: 'label',
12407                     'for' :  id,
12408                     cls : 'control-label col-sm-' + this.labelWidth,
12409                     html : this.fieldLabel
12410
12411                 },
12412                 {
12413                     cls : "col-sm-" + (12 - this.labelWidth), 
12414                     cn: [
12415                         combobox
12416                     ]
12417                 }
12418
12419             ];
12420
12421             if(this.indicatorpos == 'right'){
12422                 
12423                 cfg.cn = [
12424                     {
12425                         tag: 'label',
12426                         'for' :  id,
12427                         cls : 'control-label col-sm-' + this.labelWidth,
12428                         html : this.fieldLabel
12429
12430                     },
12431                     {
12432                         tag : 'i',
12433                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12434                         tooltip : 'This field is required'
12435                     },
12436                     {
12437                         cls : "col-sm-" + (12 - this.labelWidth), 
12438                         cn: [
12439                             combobox
12440                         ]
12441                     }
12442
12443                 ];
12444             
12445             }
12446                 
12447                 
12448         } else if ( this.fieldLabel.length) {
12449 //                Roo.log(" label");
12450                  cfg.cn = [
12451                     {
12452                         tag : 'i',
12453                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12454                         tooltip : 'This field is required'
12455                     },
12456                     {
12457                         tag: 'label',
12458                         //cls : 'input-group-addon',
12459                         html : this.fieldLabel
12460                         
12461                     },
12462                     
12463                     combobox
12464                     
12465                 ];
12466                 
12467                 if(this.indicatorpos == 'right'){
12468                     
12469                     cfg.cn = [
12470                         {
12471                             tag: 'label',
12472                             //cls : 'input-group-addon',
12473                             html : this.fieldLabel
12474
12475                         },
12476                         
12477                         {
12478                             tag : 'i',
12479                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12480                             tooltip : 'This field is required'
12481                         },
12482                         
12483                         combobox
12484
12485                     ];
12486                 
12487                 }
12488
12489         } else {
12490             
12491 //                Roo.log(" no label && no align");
12492                 cfg = combobox
12493                      
12494                 
12495         }
12496          
12497         var settings=this;
12498         ['xs','sm','md','lg'].map(function(size){
12499             if (settings[size]) {
12500                 cfg.cls += ' col-' + size + '-' + settings[size];
12501             }
12502         });
12503         
12504         return cfg;
12505         
12506     },
12507     
12508     _initEventsCalled : false,
12509     
12510     // private
12511     initEvents: function()
12512     {   
12513         if (this._initEventsCalled) { // as we call render... prevent looping...
12514             return;
12515         }
12516         this._initEventsCalled = true;
12517         
12518         if (!this.store) {
12519             throw "can not find store for combo";
12520         }
12521         
12522         this.store = Roo.factory(this.store, Roo.data);
12523         
12524         // if we are building from html. then this element is so complex, that we can not really
12525         // use the rendered HTML.
12526         // so we have to trash and replace the previous code.
12527         if (Roo.XComponent.build_from_html) {
12528             
12529             // remove this element....
12530             var e = this.el.dom, k=0;
12531             while (e ) { e = e.previousSibling;  ++k;}
12532
12533             this.el.remove();
12534             
12535             this.el=false;
12536             this.rendered = false;
12537             
12538             this.render(this.parent().getChildContainer(true), k);
12539             
12540             
12541             
12542         }
12543         
12544         if(Roo.isIOS && this.useNativeIOS){
12545             this.initIOSView();
12546             return;
12547         }
12548         
12549         /*
12550          * Touch Devices
12551          */
12552         
12553         if(Roo.isTouch && this.mobileTouchView){
12554             this.initTouchView();
12555             return;
12556         }
12557         
12558         if(this.tickable){
12559             this.initTickableEvents();
12560             return;
12561         }
12562         
12563         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12564         
12565         if(this.hiddenName){
12566             
12567             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12568             
12569             this.hiddenField.dom.value =
12570                 this.hiddenValue !== undefined ? this.hiddenValue :
12571                 this.value !== undefined ? this.value : '';
12572
12573             // prevent input submission
12574             this.el.dom.removeAttribute('name');
12575             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12576              
12577              
12578         }
12579         //if(Roo.isGecko){
12580         //    this.el.dom.setAttribute('autocomplete', 'off');
12581         //}
12582         
12583         var cls = 'x-combo-list';
12584         
12585         //this.list = new Roo.Layer({
12586         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12587         //});
12588         
12589         var _this = this;
12590         
12591         (function(){
12592             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12593             _this.list.setWidth(lw);
12594         }).defer(100);
12595         
12596         this.list.on('mouseover', this.onViewOver, this);
12597         this.list.on('mousemove', this.onViewMove, this);
12598         
12599         this.list.on('scroll', this.onViewScroll, this);
12600         
12601         /*
12602         this.list.swallowEvent('mousewheel');
12603         this.assetHeight = 0;
12604
12605         if(this.title){
12606             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12607             this.assetHeight += this.header.getHeight();
12608         }
12609
12610         this.innerList = this.list.createChild({cls:cls+'-inner'});
12611         this.innerList.on('mouseover', this.onViewOver, this);
12612         this.innerList.on('mousemove', this.onViewMove, this);
12613         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12614         
12615         if(this.allowBlank && !this.pageSize && !this.disableClear){
12616             this.footer = this.list.createChild({cls:cls+'-ft'});
12617             this.pageTb = new Roo.Toolbar(this.footer);
12618            
12619         }
12620         if(this.pageSize){
12621             this.footer = this.list.createChild({cls:cls+'-ft'});
12622             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12623                     {pageSize: this.pageSize});
12624             
12625         }
12626         
12627         if (this.pageTb && this.allowBlank && !this.disableClear) {
12628             var _this = this;
12629             this.pageTb.add(new Roo.Toolbar.Fill(), {
12630                 cls: 'x-btn-icon x-btn-clear',
12631                 text: '&#160;',
12632                 handler: function()
12633                 {
12634                     _this.collapse();
12635                     _this.clearValue();
12636                     _this.onSelect(false, -1);
12637                 }
12638             });
12639         }
12640         if (this.footer) {
12641             this.assetHeight += this.footer.getHeight();
12642         }
12643         */
12644             
12645         if(!this.tpl){
12646             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12647         }
12648
12649         this.view = new Roo.View(this.list, this.tpl, {
12650             singleSelect:true, store: this.store, selectedClass: this.selectedClass
12651         });
12652         //this.view.wrapEl.setDisplayed(false);
12653         this.view.on('click', this.onViewClick, this);
12654         
12655         
12656         
12657         this.store.on('beforeload', this.onBeforeLoad, this);
12658         this.store.on('load', this.onLoad, this);
12659         this.store.on('loadexception', this.onLoadException, this);
12660         /*
12661         if(this.resizable){
12662             this.resizer = new Roo.Resizable(this.list,  {
12663                pinned:true, handles:'se'
12664             });
12665             this.resizer.on('resize', function(r, w, h){
12666                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12667                 this.listWidth = w;
12668                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12669                 this.restrictHeight();
12670             }, this);
12671             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12672         }
12673         */
12674         if(!this.editable){
12675             this.editable = true;
12676             this.setEditable(false);
12677         }
12678         
12679         /*
12680         
12681         if (typeof(this.events.add.listeners) != 'undefined') {
12682             
12683             this.addicon = this.wrap.createChild(
12684                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
12685        
12686             this.addicon.on('click', function(e) {
12687                 this.fireEvent('add', this);
12688             }, this);
12689         }
12690         if (typeof(this.events.edit.listeners) != 'undefined') {
12691             
12692             this.editicon = this.wrap.createChild(
12693                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
12694             if (this.addicon) {
12695                 this.editicon.setStyle('margin-left', '40px');
12696             }
12697             this.editicon.on('click', function(e) {
12698                 
12699                 // we fire even  if inothing is selected..
12700                 this.fireEvent('edit', this, this.lastData );
12701                 
12702             }, this);
12703         }
12704         */
12705         
12706         this.keyNav = new Roo.KeyNav(this.inputEl(), {
12707             "up" : function(e){
12708                 this.inKeyMode = true;
12709                 this.selectPrev();
12710             },
12711
12712             "down" : function(e){
12713                 if(!this.isExpanded()){
12714                     this.onTriggerClick();
12715                 }else{
12716                     this.inKeyMode = true;
12717                     this.selectNext();
12718                 }
12719             },
12720
12721             "enter" : function(e){
12722 //                this.onViewClick();
12723                 //return true;
12724                 this.collapse();
12725                 
12726                 if(this.fireEvent("specialkey", this, e)){
12727                     this.onViewClick(false);
12728                 }
12729                 
12730                 return true;
12731             },
12732
12733             "esc" : function(e){
12734                 this.collapse();
12735             },
12736
12737             "tab" : function(e){
12738                 this.collapse();
12739                 
12740                 if(this.fireEvent("specialkey", this, e)){
12741                     this.onViewClick(false);
12742                 }
12743                 
12744                 return true;
12745             },
12746
12747             scope : this,
12748
12749             doRelay : function(foo, bar, hname){
12750                 if(hname == 'down' || this.scope.isExpanded()){
12751                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12752                 }
12753                 return true;
12754             },
12755
12756             forceKeyDown: true
12757         });
12758         
12759         
12760         this.queryDelay = Math.max(this.queryDelay || 10,
12761                 this.mode == 'local' ? 10 : 250);
12762         
12763         
12764         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12765         
12766         if(this.typeAhead){
12767             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12768         }
12769         if(this.editable !== false){
12770             this.inputEl().on("keyup", this.onKeyUp, this);
12771         }
12772         if(this.forceSelection){
12773             this.inputEl().on('blur', this.doForce, this);
12774         }
12775         
12776         if(this.multiple){
12777             this.choices = this.el.select('ul.roo-select2-choices', true).first();
12778             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12779         }
12780     },
12781     
12782     initTickableEvents: function()
12783     {   
12784         this.createList();
12785         
12786         if(this.hiddenName){
12787             
12788             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12789             
12790             this.hiddenField.dom.value =
12791                 this.hiddenValue !== undefined ? this.hiddenValue :
12792                 this.value !== undefined ? this.value : '';
12793
12794             // prevent input submission
12795             this.el.dom.removeAttribute('name');
12796             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12797              
12798              
12799         }
12800         
12801 //        this.list = this.el.select('ul.dropdown-menu',true).first();
12802         
12803         this.choices = this.el.select('ul.roo-select2-choices', true).first();
12804         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12805         if(this.triggerList){
12806             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12807         }
12808          
12809         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12810         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12811         
12812         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12813         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12814         
12815         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12816         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12817         
12818         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12819         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12820         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12821         
12822         this.okBtn.hide();
12823         this.cancelBtn.hide();
12824         
12825         var _this = this;
12826         
12827         (function(){
12828             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12829             _this.list.setWidth(lw);
12830         }).defer(100);
12831         
12832         this.list.on('mouseover', this.onViewOver, this);
12833         this.list.on('mousemove', this.onViewMove, this);
12834         
12835         this.list.on('scroll', this.onViewScroll, this);
12836         
12837         if(!this.tpl){
12838             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}" type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></li>';
12839         }
12840
12841         this.view = new Roo.View(this.list, this.tpl, {
12842             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12843         });
12844         
12845         //this.view.wrapEl.setDisplayed(false);
12846         this.view.on('click', this.onViewClick, this);
12847         
12848         
12849         
12850         this.store.on('beforeload', this.onBeforeLoad, this);
12851         this.store.on('load', this.onLoad, this);
12852         this.store.on('loadexception', this.onLoadException, this);
12853         
12854         if(this.editable){
12855             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12856                 "up" : function(e){
12857                     this.inKeyMode = true;
12858                     this.selectPrev();
12859                 },
12860
12861                 "down" : function(e){
12862                     this.inKeyMode = true;
12863                     this.selectNext();
12864                 },
12865
12866                 "enter" : function(e){
12867                     if(this.fireEvent("specialkey", this, e)){
12868                         this.onViewClick(false);
12869                     }
12870                     
12871                     return true;
12872                 },
12873
12874                 "esc" : function(e){
12875                     this.onTickableFooterButtonClick(e, false, false);
12876                 },
12877
12878                 "tab" : function(e){
12879                     this.fireEvent("specialkey", this, e);
12880                     
12881                     this.onTickableFooterButtonClick(e, false, false);
12882                     
12883                     return true;
12884                 },
12885
12886                 scope : this,
12887
12888                 doRelay : function(e, fn, key){
12889                     if(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         
12909         if(this.editable !== false){
12910             this.tickableInputEl().on("keyup", this.onKeyUp, this);
12911         }
12912         
12913     },
12914
12915     onDestroy : function(){
12916         if(this.view){
12917             this.view.setStore(null);
12918             this.view.el.removeAllListeners();
12919             this.view.el.remove();
12920             this.view.purgeListeners();
12921         }
12922         if(this.list){
12923             this.list.dom.innerHTML  = '';
12924         }
12925         
12926         if(this.store){
12927             this.store.un('beforeload', this.onBeforeLoad, this);
12928             this.store.un('load', this.onLoad, this);
12929             this.store.un('loadexception', this.onLoadException, this);
12930         }
12931         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12932     },
12933
12934     // private
12935     fireKey : function(e){
12936         if(e.isNavKeyPress() && !this.list.isVisible()){
12937             this.fireEvent("specialkey", this, e);
12938         }
12939     },
12940
12941     // private
12942     onResize: function(w, h){
12943 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12944 //        
12945 //        if(typeof w != 'number'){
12946 //            // we do not handle it!?!?
12947 //            return;
12948 //        }
12949 //        var tw = this.trigger.getWidth();
12950 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
12951 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
12952 //        var x = w - tw;
12953 //        this.inputEl().setWidth( this.adjustWidth('input', x));
12954 //            
12955 //        //this.trigger.setStyle('left', x+'px');
12956 //        
12957 //        if(this.list && this.listWidth === undefined){
12958 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12959 //            this.list.setWidth(lw);
12960 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12961 //        }
12962         
12963     
12964         
12965     },
12966
12967     /**
12968      * Allow or prevent the user from directly editing the field text.  If false is passed,
12969      * the user will only be able to select from the items defined in the dropdown list.  This method
12970      * is the runtime equivalent of setting the 'editable' config option at config time.
12971      * @param {Boolean} value True to allow the user to directly edit the field text
12972      */
12973     setEditable : function(value){
12974         if(value == this.editable){
12975             return;
12976         }
12977         this.editable = value;
12978         if(!value){
12979             this.inputEl().dom.setAttribute('readOnly', true);
12980             this.inputEl().on('mousedown', this.onTriggerClick,  this);
12981             this.inputEl().addClass('x-combo-noedit');
12982         }else{
12983             this.inputEl().dom.setAttribute('readOnly', false);
12984             this.inputEl().un('mousedown', this.onTriggerClick,  this);
12985             this.inputEl().removeClass('x-combo-noedit');
12986         }
12987     },
12988
12989     // private
12990     
12991     onBeforeLoad : function(combo,opts){
12992         if(!this.hasFocus){
12993             return;
12994         }
12995          if (!opts.add) {
12996             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12997          }
12998         this.restrictHeight();
12999         this.selectedIndex = -1;
13000     },
13001
13002     // private
13003     onLoad : function(){
13004         
13005         this.hasQuery = false;
13006         
13007         if(!this.hasFocus){
13008             return;
13009         }
13010         
13011         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13012             this.loading.hide();
13013         }
13014              
13015         if(this.store.getCount() > 0){
13016             this.expand();
13017             this.restrictHeight();
13018             if(this.lastQuery == this.allQuery){
13019                 if(this.editable && !this.tickable){
13020                     this.inputEl().dom.select();
13021                 }
13022                 
13023                 if(
13024                     !this.selectByValue(this.value, true) &&
13025                     this.autoFocus && 
13026                     (
13027                         !this.store.lastOptions ||
13028                         typeof(this.store.lastOptions.add) == 'undefined' || 
13029                         this.store.lastOptions.add != true
13030                     )
13031                 ){
13032                     this.select(0, true);
13033                 }
13034             }else{
13035                 if(this.autoFocus){
13036                     this.selectNext();
13037                 }
13038                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13039                     this.taTask.delay(this.typeAheadDelay);
13040                 }
13041             }
13042         }else{
13043             this.onEmptyResults();
13044         }
13045         
13046         //this.el.focus();
13047     },
13048     // private
13049     onLoadException : function()
13050     {
13051         this.hasQuery = false;
13052         
13053         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13054             this.loading.hide();
13055         }
13056         
13057         if(this.tickable && this.editable){
13058             return;
13059         }
13060         
13061         this.collapse();
13062         // only causes errors at present
13063         //Roo.log(this.store.reader.jsonData);
13064         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13065             // fixme
13066             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13067         //}
13068         
13069         
13070     },
13071     // private
13072     onTypeAhead : function(){
13073         if(this.store.getCount() > 0){
13074             var r = this.store.getAt(0);
13075             var newValue = r.data[this.displayField];
13076             var len = newValue.length;
13077             var selStart = this.getRawValue().length;
13078             
13079             if(selStart != len){
13080                 this.setRawValue(newValue);
13081                 this.selectText(selStart, newValue.length);
13082             }
13083         }
13084     },
13085
13086     // private
13087     onSelect : function(record, index){
13088         
13089         if(this.fireEvent('beforeselect', this, record, index) !== false){
13090         
13091             this.setFromData(index > -1 ? record.data : false);
13092             
13093             this.collapse();
13094             this.fireEvent('select', this, record, index);
13095         }
13096     },
13097
13098     /**
13099      * Returns the currently selected field value or empty string if no value is set.
13100      * @return {String} value The selected value
13101      */
13102     getValue : function()
13103     {
13104         if(Roo.isIOS && this.useNativeIOS){
13105             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13106         }
13107         
13108         if(this.multiple){
13109             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13110         }
13111         
13112         if(this.valueField){
13113             return typeof this.value != 'undefined' ? this.value : '';
13114         }else{
13115             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13116         }
13117     },
13118     
13119     getRawValue : function()
13120     {
13121         if(Roo.isIOS && this.useNativeIOS){
13122             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13123         }
13124         
13125         var v = this.inputEl().getValue();
13126         
13127         return v;
13128     },
13129
13130     /**
13131      * Clears any text/value currently set in the field
13132      */
13133     clearValue : function(){
13134         
13135         if(this.hiddenField){
13136             this.hiddenField.dom.value = '';
13137         }
13138         this.value = '';
13139         this.setRawValue('');
13140         this.lastSelectionText = '';
13141         this.lastData = false;
13142         
13143         var close = this.closeTriggerEl();
13144         
13145         if(close){
13146             close.hide();
13147         }
13148         
13149         this.validate();
13150         
13151     },
13152
13153     /**
13154      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13155      * will be displayed in the field.  If the value does not match the data value of an existing item,
13156      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13157      * Otherwise the field will be blank (although the value will still be set).
13158      * @param {String} value The value to match
13159      */
13160     setValue : function(v)
13161     {
13162         if(Roo.isIOS && this.useNativeIOS){
13163             this.setIOSValue(v);
13164             return;
13165         }
13166         
13167         if(this.multiple){
13168             this.syncValue();
13169             return;
13170         }
13171         
13172         var text = v;
13173         if(this.valueField){
13174             var r = this.findRecord(this.valueField, v);
13175             if(r){
13176                 text = r.data[this.displayField];
13177             }else if(this.valueNotFoundText !== undefined){
13178                 text = this.valueNotFoundText;
13179             }
13180         }
13181         this.lastSelectionText = text;
13182         if(this.hiddenField){
13183             this.hiddenField.dom.value = v;
13184         }
13185         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13186         this.value = v;
13187         
13188         var close = this.closeTriggerEl();
13189         
13190         if(close){
13191             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13192         }
13193         
13194         this.validate();
13195     },
13196     /**
13197      * @property {Object} the last set data for the element
13198      */
13199     
13200     lastData : false,
13201     /**
13202      * Sets the value of the field based on a object which is related to the record format for the store.
13203      * @param {Object} value the value to set as. or false on reset?
13204      */
13205     setFromData : function(o){
13206         
13207         if(this.multiple){
13208             this.addItem(o);
13209             return;
13210         }
13211             
13212         var dv = ''; // display value
13213         var vv = ''; // value value..
13214         this.lastData = o;
13215         if (this.displayField) {
13216             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13217         } else {
13218             // this is an error condition!!!
13219             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13220         }
13221         
13222         if(this.valueField){
13223             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13224         }
13225         
13226         var close = this.closeTriggerEl();
13227         
13228         if(close){
13229             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13230         }
13231         
13232         if(this.hiddenField){
13233             this.hiddenField.dom.value = vv;
13234             
13235             this.lastSelectionText = dv;
13236             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13237             this.value = vv;
13238             return;
13239         }
13240         // no hidden field.. - we store the value in 'value', but still display
13241         // display field!!!!
13242         this.lastSelectionText = dv;
13243         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13244         this.value = vv;
13245         
13246         
13247         
13248     },
13249     // private
13250     reset : function(){
13251         // overridden so that last data is reset..
13252         
13253         if(this.multiple){
13254             this.clearItem();
13255             return;
13256         }
13257         
13258         this.setValue(this.originalValue);
13259         //this.clearInvalid();
13260         this.lastData = false;
13261         if (this.view) {
13262             this.view.clearSelections();
13263         }
13264         
13265         this.validate();
13266     },
13267     // private
13268     findRecord : function(prop, value){
13269         var record;
13270         if(this.store.getCount() > 0){
13271             this.store.each(function(r){
13272                 if(r.data[prop] == value){
13273                     record = r;
13274                     return false;
13275                 }
13276                 return true;
13277             });
13278         }
13279         return record;
13280     },
13281     
13282     getName: function()
13283     {
13284         // returns hidden if it's set..
13285         if (!this.rendered) {return ''};
13286         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13287         
13288     },
13289     // private
13290     onViewMove : function(e, t){
13291         this.inKeyMode = false;
13292     },
13293
13294     // private
13295     onViewOver : function(e, t){
13296         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13297             return;
13298         }
13299         var item = this.view.findItemFromChild(t);
13300         
13301         if(item){
13302             var index = this.view.indexOf(item);
13303             this.select(index, false);
13304         }
13305     },
13306
13307     // private
13308     onViewClick : function(view, doFocus, el, e)
13309     {
13310         var index = this.view.getSelectedIndexes()[0];
13311         
13312         var r = this.store.getAt(index);
13313         
13314         if(this.tickable){
13315             
13316             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13317                 return;
13318             }
13319             
13320             var rm = false;
13321             var _this = this;
13322             
13323             Roo.each(this.tickItems, function(v,k){
13324                 
13325                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13326                     Roo.log(v);
13327                     _this.tickItems.splice(k, 1);
13328                     
13329                     if(typeof(e) == 'undefined' && view == false){
13330                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13331                     }
13332                     
13333                     rm = true;
13334                     return;
13335                 }
13336             });
13337             
13338             if(rm){
13339                 return;
13340             }
13341             
13342             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13343                 this.tickItems.push(r.data);
13344             }
13345             
13346             if(typeof(e) == 'undefined' && view == false){
13347                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13348             }
13349                     
13350             return;
13351         }
13352         
13353         if(r){
13354             this.onSelect(r, index);
13355         }
13356         if(doFocus !== false && !this.blockFocus){
13357             this.inputEl().focus();
13358         }
13359     },
13360
13361     // private
13362     restrictHeight : function(){
13363         //this.innerList.dom.style.height = '';
13364         //var inner = this.innerList.dom;
13365         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13366         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13367         //this.list.beginUpdate();
13368         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13369         this.list.alignTo(this.inputEl(), this.listAlign);
13370         this.list.alignTo(this.inputEl(), this.listAlign);
13371         //this.list.endUpdate();
13372     },
13373
13374     // private
13375     onEmptyResults : function(){
13376         
13377         if(this.tickable && this.editable){
13378             this.restrictHeight();
13379             return;
13380         }
13381         
13382         this.collapse();
13383     },
13384
13385     /**
13386      * Returns true if the dropdown list is expanded, else false.
13387      */
13388     isExpanded : function(){
13389         return this.list.isVisible();
13390     },
13391
13392     /**
13393      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13394      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13395      * @param {String} value The data value of the item to select
13396      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13397      * selected item if it is not currently in view (defaults to true)
13398      * @return {Boolean} True if the value matched an item in the list, else false
13399      */
13400     selectByValue : function(v, scrollIntoView){
13401         if(v !== undefined && v !== null){
13402             var r = this.findRecord(this.valueField || this.displayField, v);
13403             if(r){
13404                 this.select(this.store.indexOf(r), scrollIntoView);
13405                 return true;
13406             }
13407         }
13408         return false;
13409     },
13410
13411     /**
13412      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13413      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13414      * @param {Number} index The zero-based index of the list item to select
13415      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13416      * selected item if it is not currently in view (defaults to true)
13417      */
13418     select : function(index, scrollIntoView){
13419         this.selectedIndex = index;
13420         this.view.select(index);
13421         if(scrollIntoView !== false){
13422             var el = this.view.getNode(index);
13423             /*
13424              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13425              */
13426             if(el){
13427                 this.list.scrollChildIntoView(el, false);
13428             }
13429         }
13430     },
13431
13432     // private
13433     selectNext : function(){
13434         var ct = this.store.getCount();
13435         if(ct > 0){
13436             if(this.selectedIndex == -1){
13437                 this.select(0);
13438             }else if(this.selectedIndex < ct-1){
13439                 this.select(this.selectedIndex+1);
13440             }
13441         }
13442     },
13443
13444     // private
13445     selectPrev : function(){
13446         var ct = this.store.getCount();
13447         if(ct > 0){
13448             if(this.selectedIndex == -1){
13449                 this.select(0);
13450             }else if(this.selectedIndex != 0){
13451                 this.select(this.selectedIndex-1);
13452             }
13453         }
13454     },
13455
13456     // private
13457     onKeyUp : function(e){
13458         if(this.editable !== false && !e.isSpecialKey()){
13459             this.lastKey = e.getKey();
13460             this.dqTask.delay(this.queryDelay);
13461         }
13462     },
13463
13464     // private
13465     validateBlur : function(){
13466         return !this.list || !this.list.isVisible();   
13467     },
13468
13469     // private
13470     initQuery : function(){
13471         
13472         var v = this.getRawValue();
13473         
13474         if(this.tickable && this.editable){
13475             v = this.tickableInputEl().getValue();
13476         }
13477         
13478         this.doQuery(v);
13479     },
13480
13481     // private
13482     doForce : function(){
13483         if(this.inputEl().dom.value.length > 0){
13484             this.inputEl().dom.value =
13485                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13486              
13487         }
13488     },
13489
13490     /**
13491      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
13492      * query allowing the query action to be canceled if needed.
13493      * @param {String} query The SQL query to execute
13494      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13495      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
13496      * saved in the current store (defaults to false)
13497      */
13498     doQuery : function(q, forceAll){
13499         
13500         if(q === undefined || q === null){
13501             q = '';
13502         }
13503         var qe = {
13504             query: q,
13505             forceAll: forceAll,
13506             combo: this,
13507             cancel:false
13508         };
13509         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13510             return false;
13511         }
13512         q = qe.query;
13513         
13514         forceAll = qe.forceAll;
13515         if(forceAll === true || (q.length >= this.minChars)){
13516             
13517             this.hasQuery = true;
13518             
13519             if(this.lastQuery != q || this.alwaysQuery){
13520                 this.lastQuery = q;
13521                 if(this.mode == 'local'){
13522                     this.selectedIndex = -1;
13523                     if(forceAll){
13524                         this.store.clearFilter();
13525                     }else{
13526                         
13527                         if(this.specialFilter){
13528                             this.fireEvent('specialfilter', this);
13529                             this.onLoad();
13530                             return;
13531                         }
13532                         
13533                         this.store.filter(this.displayField, q);
13534                     }
13535                     
13536                     this.store.fireEvent("datachanged", this.store);
13537                     
13538                     this.onLoad();
13539                     
13540                     
13541                 }else{
13542                     
13543                     this.store.baseParams[this.queryParam] = q;
13544                     
13545                     var options = {params : this.getParams(q)};
13546                     
13547                     if(this.loadNext){
13548                         options.add = true;
13549                         options.params.start = this.page * this.pageSize;
13550                     }
13551                     
13552                     this.store.load(options);
13553                     
13554                     /*
13555                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
13556                      *  we should expand the list on onLoad
13557                      *  so command out it
13558                      */
13559 //                    this.expand();
13560                 }
13561             }else{
13562                 this.selectedIndex = -1;
13563                 this.onLoad();   
13564             }
13565         }
13566         
13567         this.loadNext = false;
13568     },
13569     
13570     // private
13571     getParams : function(q){
13572         var p = {};
13573         //p[this.queryParam] = q;
13574         
13575         if(this.pageSize){
13576             p.start = 0;
13577             p.limit = this.pageSize;
13578         }
13579         return p;
13580     },
13581
13582     /**
13583      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13584      */
13585     collapse : function(){
13586         if(!this.isExpanded()){
13587             return;
13588         }
13589         
13590         this.list.hide();
13591         
13592         if(this.tickable){
13593             this.hasFocus = false;
13594             this.okBtn.hide();
13595             this.cancelBtn.hide();
13596             this.trigger.show();
13597             
13598             if(this.editable){
13599                 this.tickableInputEl().dom.value = '';
13600                 this.tickableInputEl().blur();
13601             }
13602             
13603         }
13604         
13605         Roo.get(document).un('mousedown', this.collapseIf, this);
13606         Roo.get(document).un('mousewheel', this.collapseIf, this);
13607         if (!this.editable) {
13608             Roo.get(document).un('keydown', this.listKeyPress, this);
13609         }
13610         this.fireEvent('collapse', this);
13611         
13612         this.validate();
13613     },
13614
13615     // private
13616     collapseIf : function(e){
13617         var in_combo  = e.within(this.el);
13618         var in_list =  e.within(this.list);
13619         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13620         
13621         if (in_combo || in_list || is_list) {
13622             //e.stopPropagation();
13623             return;
13624         }
13625         
13626         if(this.tickable){
13627             this.onTickableFooterButtonClick(e, false, false);
13628         }
13629
13630         this.collapse();
13631         
13632     },
13633
13634     /**
13635      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13636      */
13637     expand : function(){
13638        
13639         if(this.isExpanded() || !this.hasFocus){
13640             return;
13641         }
13642         
13643         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13644         this.list.setWidth(lw);
13645         
13646         
13647          Roo.log('expand');
13648         
13649         this.list.show();
13650         
13651         this.restrictHeight();
13652         
13653         if(this.tickable){
13654             
13655             this.tickItems = Roo.apply([], this.item);
13656             
13657             this.okBtn.show();
13658             this.cancelBtn.show();
13659             this.trigger.hide();
13660             
13661             if(this.editable){
13662                 this.tickableInputEl().focus();
13663             }
13664             
13665         }
13666         
13667         Roo.get(document).on('mousedown', this.collapseIf, this);
13668         Roo.get(document).on('mousewheel', this.collapseIf, this);
13669         if (!this.editable) {
13670             Roo.get(document).on('keydown', this.listKeyPress, this);
13671         }
13672         
13673         this.fireEvent('expand', this);
13674     },
13675
13676     // private
13677     // Implements the default empty TriggerField.onTriggerClick function
13678     onTriggerClick : function(e)
13679     {
13680         Roo.log('trigger click');
13681         
13682         if(this.disabled || !this.triggerList){
13683             return;
13684         }
13685         
13686         this.page = 0;
13687         this.loadNext = false;
13688         
13689         if(this.isExpanded()){
13690             this.collapse();
13691             if (!this.blockFocus) {
13692                 this.inputEl().focus();
13693             }
13694             
13695         }else {
13696             this.hasFocus = true;
13697             if(this.triggerAction == 'all') {
13698                 this.doQuery(this.allQuery, true);
13699             } else {
13700                 this.doQuery(this.getRawValue());
13701             }
13702             if (!this.blockFocus) {
13703                 this.inputEl().focus();
13704             }
13705         }
13706     },
13707     
13708     onTickableTriggerClick : function(e)
13709     {
13710         if(this.disabled){
13711             return;
13712         }
13713         
13714         this.page = 0;
13715         this.loadNext = false;
13716         this.hasFocus = true;
13717         
13718         if(this.triggerAction == 'all') {
13719             this.doQuery(this.allQuery, true);
13720         } else {
13721             this.doQuery(this.getRawValue());
13722         }
13723     },
13724     
13725     onSearchFieldClick : function(e)
13726     {
13727         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13728             this.onTickableFooterButtonClick(e, false, false);
13729             return;
13730         }
13731         
13732         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13733             return;
13734         }
13735         
13736         this.page = 0;
13737         this.loadNext = false;
13738         this.hasFocus = true;
13739         
13740         if(this.triggerAction == 'all') {
13741             this.doQuery(this.allQuery, true);
13742         } else {
13743             this.doQuery(this.getRawValue());
13744         }
13745     },
13746     
13747     listKeyPress : function(e)
13748     {
13749         //Roo.log('listkeypress');
13750         // scroll to first matching element based on key pres..
13751         if (e.isSpecialKey()) {
13752             return false;
13753         }
13754         var k = String.fromCharCode(e.getKey()).toUpperCase();
13755         //Roo.log(k);
13756         var match  = false;
13757         var csel = this.view.getSelectedNodes();
13758         var cselitem = false;
13759         if (csel.length) {
13760             var ix = this.view.indexOf(csel[0]);
13761             cselitem  = this.store.getAt(ix);
13762             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13763                 cselitem = false;
13764             }
13765             
13766         }
13767         
13768         this.store.each(function(v) { 
13769             if (cselitem) {
13770                 // start at existing selection.
13771                 if (cselitem.id == v.id) {
13772                     cselitem = false;
13773                 }
13774                 return true;
13775             }
13776                 
13777             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13778                 match = this.store.indexOf(v);
13779                 return false;
13780             }
13781             return true;
13782         }, this);
13783         
13784         if (match === false) {
13785             return true; // no more action?
13786         }
13787         // scroll to?
13788         this.view.select(match);
13789         var sn = Roo.get(this.view.getSelectedNodes()[0]);
13790         sn.scrollIntoView(sn.dom.parentNode, false);
13791     },
13792     
13793     onViewScroll : function(e, t){
13794         
13795         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){
13796             return;
13797         }
13798         
13799         this.hasQuery = true;
13800         
13801         this.loading = this.list.select('.loading', true).first();
13802         
13803         if(this.loading === null){
13804             this.list.createChild({
13805                 tag: 'div',
13806                 cls: 'loading roo-select2-more-results roo-select2-active',
13807                 html: 'Loading more results...'
13808             });
13809             
13810             this.loading = this.list.select('.loading', true).first();
13811             
13812             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13813             
13814             this.loading.hide();
13815         }
13816         
13817         this.loading.show();
13818         
13819         var _combo = this;
13820         
13821         this.page++;
13822         this.loadNext = true;
13823         
13824         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13825         
13826         return;
13827     },
13828     
13829     addItem : function(o)
13830     {   
13831         var dv = ''; // display value
13832         
13833         if (this.displayField) {
13834             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13835         } else {
13836             // this is an error condition!!!
13837             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13838         }
13839         
13840         if(!dv.length){
13841             return;
13842         }
13843         
13844         var choice = this.choices.createChild({
13845             tag: 'li',
13846             cls: 'roo-select2-search-choice',
13847             cn: [
13848                 {
13849                     tag: 'div',
13850                     html: dv
13851                 },
13852                 {
13853                     tag: 'a',
13854                     href: '#',
13855                     cls: 'roo-select2-search-choice-close',
13856                     tabindex: '-1'
13857                 }
13858             ]
13859             
13860         }, this.searchField);
13861         
13862         var close = choice.select('a.roo-select2-search-choice-close', true).first();
13863         
13864         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13865         
13866         this.item.push(o);
13867         
13868         this.lastData = o;
13869         
13870         this.syncValue();
13871         
13872         this.inputEl().dom.value = '';
13873         
13874         this.validate();
13875     },
13876     
13877     onRemoveItem : function(e, _self, o)
13878     {
13879         e.preventDefault();
13880         
13881         this.lastItem = Roo.apply([], this.item);
13882         
13883         var index = this.item.indexOf(o.data) * 1;
13884         
13885         if( index < 0){
13886             Roo.log('not this item?!');
13887             return;
13888         }
13889         
13890         this.item.splice(index, 1);
13891         o.item.remove();
13892         
13893         this.syncValue();
13894         
13895         this.fireEvent('remove', this, e);
13896         
13897         this.validate();
13898         
13899     },
13900     
13901     syncValue : function()
13902     {
13903         if(!this.item.length){
13904             this.clearValue();
13905             return;
13906         }
13907             
13908         var value = [];
13909         var _this = this;
13910         Roo.each(this.item, function(i){
13911             if(_this.valueField){
13912                 value.push(i[_this.valueField]);
13913                 return;
13914             }
13915
13916             value.push(i);
13917         });
13918
13919         this.value = value.join(',');
13920
13921         if(this.hiddenField){
13922             this.hiddenField.dom.value = this.value;
13923         }
13924         
13925         this.store.fireEvent("datachanged", this.store);
13926         
13927         this.validate();
13928     },
13929     
13930     clearItem : function()
13931     {
13932         if(!this.multiple){
13933             return;
13934         }
13935         
13936         this.item = [];
13937         
13938         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
13939            c.remove();
13940         });
13941         
13942         this.syncValue();
13943         
13944         this.validate();
13945         
13946         if(this.tickable && !Roo.isTouch){
13947             this.view.refresh();
13948         }
13949     },
13950     
13951     inputEl: function ()
13952     {
13953         if(Roo.isIOS && this.useNativeIOS){
13954             return this.el.select('select.roo-ios-select', true).first();
13955         }
13956         
13957         if(Roo.isTouch && this.mobileTouchView){
13958             return this.el.select('input.form-control',true).first();
13959         }
13960         
13961         if(this.tickable){
13962             return this.searchField;
13963         }
13964         
13965         return this.el.select('input.form-control',true).first();
13966     },
13967     
13968     onTickableFooterButtonClick : function(e, btn, el)
13969     {
13970         e.preventDefault();
13971         
13972         this.lastItem = Roo.apply([], this.item);
13973         
13974         if(btn && btn.name == 'cancel'){
13975             this.tickItems = Roo.apply([], this.item);
13976             this.collapse();
13977             return;
13978         }
13979         
13980         this.clearItem();
13981         
13982         var _this = this;
13983         
13984         Roo.each(this.tickItems, function(o){
13985             _this.addItem(o);
13986         });
13987         
13988         this.collapse();
13989         
13990     },
13991     
13992     validate : function()
13993     {
13994         var v = this.getRawValue();
13995         
13996         if(this.multiple){
13997             v = this.getValue();
13998         }
13999         
14000         if(this.disabled || this.allowBlank || v.length){
14001             this.markValid();
14002             return true;
14003         }
14004         
14005         this.markInvalid();
14006         return false;
14007     },
14008     
14009     tickableInputEl : function()
14010     {
14011         if(!this.tickable || !this.editable){
14012             return this.inputEl();
14013         }
14014         
14015         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14016     },
14017     
14018     
14019     getAutoCreateTouchView : function()
14020     {
14021         var id = Roo.id();
14022         
14023         var cfg = {
14024             cls: 'form-group' //input-group
14025         };
14026         
14027         var input =  {
14028             tag: 'input',
14029             id : id,
14030             type : this.inputType,
14031             cls : 'form-control x-combo-noedit',
14032             autocomplete: 'new-password',
14033             placeholder : this.placeholder || '',
14034             readonly : true
14035         };
14036         
14037         if (this.name) {
14038             input.name = this.name;
14039         }
14040         
14041         if (this.size) {
14042             input.cls += ' input-' + this.size;
14043         }
14044         
14045         if (this.disabled) {
14046             input.disabled = true;
14047         }
14048         
14049         var inputblock = {
14050             cls : '',
14051             cn : [
14052                 input
14053             ]
14054         };
14055         
14056         if(this.before){
14057             inputblock.cls += ' input-group';
14058             
14059             inputblock.cn.unshift({
14060                 tag :'span',
14061                 cls : 'input-group-addon',
14062                 html : this.before
14063             });
14064         }
14065         
14066         if(this.removable && !this.multiple){
14067             inputblock.cls += ' roo-removable';
14068             
14069             inputblock.cn.push({
14070                 tag: 'button',
14071                 html : 'x',
14072                 cls : 'roo-combo-removable-btn close'
14073             });
14074         }
14075
14076         if(this.hasFeedback && !this.allowBlank){
14077             
14078             inputblock.cls += ' has-feedback';
14079             
14080             inputblock.cn.push({
14081                 tag: 'span',
14082                 cls: 'glyphicon form-control-feedback'
14083             });
14084             
14085         }
14086         
14087         if (this.after) {
14088             
14089             inputblock.cls += (this.before) ? '' : ' input-group';
14090             
14091             inputblock.cn.push({
14092                 tag :'span',
14093                 cls : 'input-group-addon',
14094                 html : this.after
14095             });
14096         }
14097
14098         var box = {
14099             tag: 'div',
14100             cn: [
14101                 {
14102                     tag: 'input',
14103                     type : 'hidden',
14104                     cls: 'form-hidden-field'
14105                 },
14106                 inputblock
14107             ]
14108             
14109         };
14110         
14111         if(this.multiple){
14112             box = {
14113                 tag: 'div',
14114                 cn: [
14115                     {
14116                         tag: 'input',
14117                         type : 'hidden',
14118                         cls: 'form-hidden-field'
14119                     },
14120                     {
14121                         tag: 'ul',
14122                         cls: 'roo-select2-choices',
14123                         cn:[
14124                             {
14125                                 tag: 'li',
14126                                 cls: 'roo-select2-search-field',
14127                                 cn: [
14128
14129                                     inputblock
14130                                 ]
14131                             }
14132                         ]
14133                     }
14134                 ]
14135             }
14136         };
14137         
14138         var combobox = {
14139             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14140             cn: [
14141                 box
14142             ]
14143         };
14144         
14145         if(!this.multiple && this.showToggleBtn){
14146             
14147             var caret = {
14148                         tag: 'span',
14149                         cls: 'caret'
14150             };
14151             
14152             if (this.caret != false) {
14153                 caret = {
14154                      tag: 'i',
14155                      cls: 'fa fa-' + this.caret
14156                 };
14157                 
14158             }
14159             
14160             combobox.cn.push({
14161                 tag :'span',
14162                 cls : 'input-group-addon btn dropdown-toggle',
14163                 cn : [
14164                     caret,
14165                     {
14166                         tag: 'span',
14167                         cls: 'combobox-clear',
14168                         cn  : [
14169                             {
14170                                 tag : 'i',
14171                                 cls: 'icon-remove'
14172                             }
14173                         ]
14174                     }
14175                 ]
14176
14177             })
14178         }
14179         
14180         if(this.multiple){
14181             combobox.cls += ' roo-select2-container-multi';
14182         }
14183         
14184         var align = this.labelAlign || this.parentLabelAlign();
14185         
14186         cfg.cn = combobox;
14187         
14188         if(this.fieldLabel.length && this.labelWidth){
14189             
14190             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
14191             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
14192             
14193             cfg.cn = [
14194                 {
14195                    tag : 'i',
14196                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14197                    tooltip : 'This field is required'
14198                 },
14199                 {
14200                     tag: 'label',
14201                     cls : 'control-label ' + lw,
14202                     html : this.fieldLabel
14203
14204                 },
14205                 {
14206                     cls : cw, 
14207                     cn: [
14208                         combobox
14209                     ]
14210                 }
14211             ];
14212             
14213             if(this.indicatorpos == 'right'){
14214                 cfg.cn = [
14215                     {
14216                         tag: 'label',
14217                         cls : 'control-label ' + lw,
14218                         html : this.fieldLabel
14219
14220                     },
14221                     {
14222                        tag : 'i',
14223                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14224                        tooltip : 'This field is required'
14225                     },
14226                     {
14227                         cls : cw, 
14228                         cn: [
14229                             combobox
14230                         ]
14231                     }
14232                 ];
14233             }
14234         }
14235         
14236         var settings = this;
14237         
14238         ['xs','sm','md','lg'].map(function(size){
14239             if (settings[size]) {
14240                 cfg.cls += ' col-' + size + '-' + settings[size];
14241             }
14242         });
14243         
14244         return cfg;
14245     },
14246     
14247     initTouchView : function()
14248     {
14249         this.renderTouchView();
14250         
14251         this.touchViewEl.on('scroll', function(){
14252             this.el.dom.scrollTop = 0;
14253         }, this);
14254         
14255         this.originalValue = this.getValue();
14256         
14257         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14258         
14259         this.inputEl().on("click", this.showTouchView, this);
14260         this.triggerEl.on("click", this.showTouchView, this);
14261         
14262         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14263         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14264         
14265         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14266         
14267         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14268         this.store.on('load', this.onTouchViewLoad, this);
14269         this.store.on('loadexception', this.onTouchViewLoadException, this);
14270         
14271         if(this.hiddenName){
14272             
14273             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14274             
14275             this.hiddenField.dom.value =
14276                 this.hiddenValue !== undefined ? this.hiddenValue :
14277                 this.value !== undefined ? this.value : '';
14278         
14279             this.el.dom.removeAttribute('name');
14280             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14281         }
14282         
14283         if(this.multiple){
14284             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14285             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14286         }
14287         
14288         if(this.removable && !this.multiple){
14289             var close = this.closeTriggerEl();
14290             if(close){
14291                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14292                 close.on('click', this.removeBtnClick, this, close);
14293             }
14294         }
14295         /*
14296          * fix the bug in Safari iOS8
14297          */
14298         this.inputEl().on("focus", function(e){
14299             document.activeElement.blur();
14300         }, this);
14301         
14302         return;
14303         
14304         
14305     },
14306     
14307     renderTouchView : function()
14308     {
14309         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14310         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14311         
14312         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14313         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14314         
14315         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14316         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14317         this.touchViewBodyEl.setStyle('overflow', 'auto');
14318         
14319         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14320         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14321         
14322         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14323         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14324         
14325     },
14326     
14327     showTouchView : function()
14328     {
14329         if(this.disabled){
14330             return;
14331         }
14332         
14333         this.touchViewHeaderEl.hide();
14334
14335         if(this.modalTitle.length){
14336             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14337             this.touchViewHeaderEl.show();
14338         }
14339
14340         this.touchViewEl.show();
14341
14342         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14343         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14344                 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14345
14346         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14347
14348         if(this.modalTitle.length){
14349             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14350         }
14351         
14352         this.touchViewBodyEl.setHeight(bodyHeight);
14353
14354         if(this.animate){
14355             var _this = this;
14356             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14357         }else{
14358             this.touchViewEl.addClass('in');
14359         }
14360
14361         this.doTouchViewQuery();
14362         
14363     },
14364     
14365     hideTouchView : function()
14366     {
14367         this.touchViewEl.removeClass('in');
14368
14369         if(this.animate){
14370             var _this = this;
14371             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14372         }else{
14373             this.touchViewEl.setStyle('display', 'none');
14374         }
14375         
14376     },
14377     
14378     setTouchViewValue : function()
14379     {
14380         if(this.multiple){
14381             this.clearItem();
14382         
14383             var _this = this;
14384
14385             Roo.each(this.tickItems, function(o){
14386                 this.addItem(o);
14387             }, this);
14388         }
14389         
14390         this.hideTouchView();
14391     },
14392     
14393     doTouchViewQuery : function()
14394     {
14395         var qe = {
14396             query: '',
14397             forceAll: true,
14398             combo: this,
14399             cancel:false
14400         };
14401         
14402         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14403             return false;
14404         }
14405         
14406         if(!this.alwaysQuery || this.mode == 'local'){
14407             this.onTouchViewLoad();
14408             return;
14409         }
14410         
14411         this.store.load();
14412     },
14413     
14414     onTouchViewBeforeLoad : function(combo,opts)
14415     {
14416         return;
14417     },
14418
14419     // private
14420     onTouchViewLoad : function()
14421     {
14422         if(this.store.getCount() < 1){
14423             this.onTouchViewEmptyResults();
14424             return;
14425         }
14426         
14427         this.clearTouchView();
14428         
14429         var rawValue = this.getRawValue();
14430         
14431         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14432         
14433         this.tickItems = [];
14434         
14435         this.store.data.each(function(d, rowIndex){
14436             var row = this.touchViewListGroup.createChild(template);
14437             
14438             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14439                 row.addClass(d.data.cls);
14440             }
14441             
14442             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14443                 var cfg = {
14444                     data : d.data,
14445                     html : d.data[this.displayField]
14446                 };
14447                 
14448                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14449                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14450                 }
14451             }
14452             row.removeClass('selected');
14453             if(!this.multiple && this.valueField &&
14454                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14455             {
14456                 // radio buttons..
14457                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14458                 row.addClass('selected');
14459             }
14460             
14461             if(this.multiple && this.valueField &&
14462                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14463             {
14464                 
14465                 // checkboxes...
14466                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14467                 this.tickItems.push(d.data);
14468             }
14469             
14470             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14471             
14472         }, this);
14473         
14474         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14475         
14476         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14477
14478         if(this.modalTitle.length){
14479             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14480         }
14481
14482         var listHeight = this.touchViewListGroup.getHeight();
14483         
14484         var _this = this;
14485         
14486         if(firstChecked && listHeight > bodyHeight){
14487             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14488         }
14489         
14490     },
14491     
14492     onTouchViewLoadException : function()
14493     {
14494         this.hideTouchView();
14495     },
14496     
14497     onTouchViewEmptyResults : function()
14498     {
14499         this.clearTouchView();
14500         
14501         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14502         
14503         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14504         
14505     },
14506     
14507     clearTouchView : function()
14508     {
14509         this.touchViewListGroup.dom.innerHTML = '';
14510     },
14511     
14512     onTouchViewClick : function(e, el, o)
14513     {
14514         e.preventDefault();
14515         
14516         var row = o.row;
14517         var rowIndex = o.rowIndex;
14518         
14519         var r = this.store.getAt(rowIndex);
14520         
14521         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14522             
14523             if(!this.multiple){
14524                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14525                     c.dom.removeAttribute('checked');
14526                 }, this);
14527
14528                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14529
14530                 this.setFromData(r.data);
14531
14532                 var close = this.closeTriggerEl();
14533
14534                 if(close){
14535                     close.show();
14536                 }
14537
14538                 this.hideTouchView();
14539
14540                 this.fireEvent('select', this, r, rowIndex);
14541
14542                 return;
14543             }
14544
14545             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14546                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14547                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14548                 return;
14549             }
14550
14551             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14552             this.addItem(r.data);
14553             this.tickItems.push(r.data);
14554         }
14555     },
14556     
14557     getAutoCreateNativeIOS : function()
14558     {
14559         var cfg = {
14560             cls: 'form-group' //input-group,
14561         };
14562         
14563         var combobox =  {
14564             tag: 'select',
14565             cls : 'roo-ios-select'
14566         };
14567         
14568         if (this.name) {
14569             combobox.name = this.name;
14570         }
14571         
14572         if (this.disabled) {
14573             combobox.disabled = true;
14574         }
14575         
14576         var settings = this;
14577         
14578         ['xs','sm','md','lg'].map(function(size){
14579             if (settings[size]) {
14580                 cfg.cls += ' col-' + size + '-' + settings[size];
14581             }
14582         });
14583         
14584         cfg.cn = combobox;
14585         
14586         return cfg;
14587         
14588     },
14589     
14590     initIOSView : function()
14591     {
14592         this.store.on('load', this.onIOSViewLoad, this);
14593         
14594         return;
14595     },
14596     
14597     onIOSViewLoad : function()
14598     {
14599         if(this.store.getCount() < 1){
14600             return;
14601         }
14602         
14603         this.clearIOSView();
14604         
14605         if(this.allowBlank) {
14606             
14607             var default_text = '-- SELECT --';
14608             
14609             var opt = this.inputEl().createChild({
14610                 tag: 'option',
14611                 value : 0,
14612                 html : default_text
14613             });
14614             
14615             var o = {};
14616             o[this.valueField] = 0;
14617             o[this.displayField] = default_text;
14618             
14619             this.ios_options.push({
14620                 data : o,
14621                 el : opt
14622             });
14623             
14624         }
14625         
14626         this.store.data.each(function(d, rowIndex){
14627             
14628             var html = '';
14629             
14630             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14631                 html = d.data[this.displayField];
14632             }
14633             
14634             var value = '';
14635             
14636             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
14637                 value = d.data[this.valueField];
14638             }
14639             
14640             var option = {
14641                 tag: 'option',
14642                 value : value,
14643                 html : html
14644             };
14645             
14646             if(this.value == d.data[this.valueField]){
14647                 option['selected'] = true;
14648             }
14649             
14650             var opt = this.inputEl().createChild(option);
14651             
14652             this.ios_options.push({
14653                 data : d.data,
14654                 el : opt
14655             });
14656             
14657         }, this);
14658         
14659         this.inputEl().on('change', function(){
14660            this.fireEvent('select', this);
14661         }, this);
14662         
14663     },
14664     
14665     clearIOSView: function()
14666     {
14667         this.inputEl().dom.innerHTML = '';
14668         
14669         this.ios_options = [];
14670     },
14671     
14672     setIOSValue: function(v)
14673     {
14674         this.value = v;
14675         
14676         if(!this.ios_options){
14677             return;
14678         }
14679         
14680         Roo.each(this.ios_options, function(opts){
14681            
14682            opts.el.dom.removeAttribute('selected');
14683            
14684            if(opts.data[this.valueField] != v){
14685                return;
14686            }
14687            
14688            opts.el.dom.setAttribute('selected', true);
14689            
14690         }, this);
14691     }
14692
14693     /** 
14694     * @cfg {Boolean} grow 
14695     * @hide 
14696     */
14697     /** 
14698     * @cfg {Number} growMin 
14699     * @hide 
14700     */
14701     /** 
14702     * @cfg {Number} growMax 
14703     * @hide 
14704     */
14705     /**
14706      * @hide
14707      * @method autoSize
14708      */
14709 });
14710
14711 Roo.apply(Roo.bootstrap.ComboBox,  {
14712     
14713     header : {
14714         tag: 'div',
14715         cls: 'modal-header',
14716         cn: [
14717             {
14718                 tag: 'h4',
14719                 cls: 'modal-title'
14720             }
14721         ]
14722     },
14723     
14724     body : {
14725         tag: 'div',
14726         cls: 'modal-body',
14727         cn: [
14728             {
14729                 tag: 'ul',
14730                 cls: 'list-group'
14731             }
14732         ]
14733     },
14734     
14735     listItemRadio : {
14736         tag: 'li',
14737         cls: 'list-group-item',
14738         cn: [
14739             {
14740                 tag: 'span',
14741                 cls: 'roo-combobox-list-group-item-value'
14742             },
14743             {
14744                 tag: 'div',
14745                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14746                 cn: [
14747                     {
14748                         tag: 'input',
14749                         type: 'radio'
14750                     },
14751                     {
14752                         tag: 'label'
14753                     }
14754                 ]
14755             }
14756         ]
14757     },
14758     
14759     listItemCheckbox : {
14760         tag: 'li',
14761         cls: 'list-group-item',
14762         cn: [
14763             {
14764                 tag: 'span',
14765                 cls: 'roo-combobox-list-group-item-value'
14766             },
14767             {
14768                 tag: 'div',
14769                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14770                 cn: [
14771                     {
14772                         tag: 'input',
14773                         type: 'checkbox'
14774                     },
14775                     {
14776                         tag: 'label'
14777                     }
14778                 ]
14779             }
14780         ]
14781     },
14782     
14783     emptyResult : {
14784         tag: 'div',
14785         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14786     },
14787     
14788     footer : {
14789         tag: 'div',
14790         cls: 'modal-footer',
14791         cn: [
14792             {
14793                 tag: 'div',
14794                 cls: 'row',
14795                 cn: [
14796                     {
14797                         tag: 'div',
14798                         cls: 'col-xs-6 text-left',
14799                         cn: {
14800                             tag: 'button',
14801                             cls: 'btn btn-danger roo-touch-view-cancel',
14802                             html: 'Cancel'
14803                         }
14804                     },
14805                     {
14806                         tag: 'div',
14807                         cls: 'col-xs-6 text-right',
14808                         cn: {
14809                             tag: 'button',
14810                             cls: 'btn btn-success roo-touch-view-ok',
14811                             html: 'OK'
14812                         }
14813                     }
14814                 ]
14815             }
14816         ]
14817         
14818     }
14819 });
14820
14821 Roo.apply(Roo.bootstrap.ComboBox,  {
14822     
14823     touchViewTemplate : {
14824         tag: 'div',
14825         cls: 'modal fade roo-combobox-touch-view',
14826         cn: [
14827             {
14828                 tag: 'div',
14829                 cls: 'modal-dialog',
14830                 style : 'position:fixed', // we have to fix position....
14831                 cn: [
14832                     {
14833                         tag: 'div',
14834                         cls: 'modal-content',
14835                         cn: [
14836                             Roo.bootstrap.ComboBox.header,
14837                             Roo.bootstrap.ComboBox.body,
14838                             Roo.bootstrap.ComboBox.footer
14839                         ]
14840                     }
14841                 ]
14842             }
14843         ]
14844     }
14845 });/*
14846  * Based on:
14847  * Ext JS Library 1.1.1
14848  * Copyright(c) 2006-2007, Ext JS, LLC.
14849  *
14850  * Originally Released Under LGPL - original licence link has changed is not relivant.
14851  *
14852  * Fork - LGPL
14853  * <script type="text/javascript">
14854  */
14855
14856 /**
14857  * @class Roo.View
14858  * @extends Roo.util.Observable
14859  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
14860  * This class also supports single and multi selection modes. <br>
14861  * Create a data model bound view:
14862  <pre><code>
14863  var store = new Roo.data.Store(...);
14864
14865  var view = new Roo.View({
14866     el : "my-element",
14867     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
14868  
14869     singleSelect: true,
14870     selectedClass: "ydataview-selected",
14871     store: store
14872  });
14873
14874  // listen for node click?
14875  view.on("click", function(vw, index, node, e){
14876  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14877  });
14878
14879  // load XML data
14880  dataModel.load("foobar.xml");
14881  </code></pre>
14882  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14883  * <br><br>
14884  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14885  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14886  * 
14887  * Note: old style constructor is still suported (container, template, config)
14888  * 
14889  * @constructor
14890  * Create a new View
14891  * @param {Object} config The config object
14892  * 
14893  */
14894 Roo.View = function(config, depreciated_tpl, depreciated_config){
14895     
14896     this.parent = false;
14897     
14898     if (typeof(depreciated_tpl) == 'undefined') {
14899         // new way.. - universal constructor.
14900         Roo.apply(this, config);
14901         this.el  = Roo.get(this.el);
14902     } else {
14903         // old format..
14904         this.el  = Roo.get(config);
14905         this.tpl = depreciated_tpl;
14906         Roo.apply(this, depreciated_config);
14907     }
14908     this.wrapEl  = this.el.wrap().wrap();
14909     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14910     
14911     
14912     if(typeof(this.tpl) == "string"){
14913         this.tpl = new Roo.Template(this.tpl);
14914     } else {
14915         // support xtype ctors..
14916         this.tpl = new Roo.factory(this.tpl, Roo);
14917     }
14918     
14919     
14920     this.tpl.compile();
14921     
14922     /** @private */
14923     this.addEvents({
14924         /**
14925          * @event beforeclick
14926          * Fires before a click is processed. Returns false to cancel the default action.
14927          * @param {Roo.View} this
14928          * @param {Number} index The index of the target node
14929          * @param {HTMLElement} node The target node
14930          * @param {Roo.EventObject} e The raw event object
14931          */
14932             "beforeclick" : true,
14933         /**
14934          * @event click
14935          * Fires when a template node is clicked.
14936          * @param {Roo.View} this
14937          * @param {Number} index The index of the target node
14938          * @param {HTMLElement} node The target node
14939          * @param {Roo.EventObject} e The raw event object
14940          */
14941             "click" : true,
14942         /**
14943          * @event dblclick
14944          * Fires when a template node is double clicked.
14945          * @param {Roo.View} this
14946          * @param {Number} index The index of the target node
14947          * @param {HTMLElement} node The target node
14948          * @param {Roo.EventObject} e The raw event object
14949          */
14950             "dblclick" : true,
14951         /**
14952          * @event contextmenu
14953          * Fires when a template node is right clicked.
14954          * @param {Roo.View} this
14955          * @param {Number} index The index of the target node
14956          * @param {HTMLElement} node The target node
14957          * @param {Roo.EventObject} e The raw event object
14958          */
14959             "contextmenu" : true,
14960         /**
14961          * @event selectionchange
14962          * Fires when the selected nodes change.
14963          * @param {Roo.View} this
14964          * @param {Array} selections Array of the selected nodes
14965          */
14966             "selectionchange" : true,
14967     
14968         /**
14969          * @event beforeselect
14970          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14971          * @param {Roo.View} this
14972          * @param {HTMLElement} node The node to be selected
14973          * @param {Array} selections Array of currently selected nodes
14974          */
14975             "beforeselect" : true,
14976         /**
14977          * @event preparedata
14978          * Fires on every row to render, to allow you to change the data.
14979          * @param {Roo.View} this
14980          * @param {Object} data to be rendered (change this)
14981          */
14982           "preparedata" : true
14983           
14984           
14985         });
14986
14987
14988
14989     this.el.on({
14990         "click": this.onClick,
14991         "dblclick": this.onDblClick,
14992         "contextmenu": this.onContextMenu,
14993         scope:this
14994     });
14995
14996     this.selections = [];
14997     this.nodes = [];
14998     this.cmp = new Roo.CompositeElementLite([]);
14999     if(this.store){
15000         this.store = Roo.factory(this.store, Roo.data);
15001         this.setStore(this.store, true);
15002     }
15003     
15004     if ( this.footer && this.footer.xtype) {
15005            
15006          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15007         
15008         this.footer.dataSource = this.store;
15009         this.footer.container = fctr;
15010         this.footer = Roo.factory(this.footer, Roo);
15011         fctr.insertFirst(this.el);
15012         
15013         // this is a bit insane - as the paging toolbar seems to detach the el..
15014 //        dom.parentNode.parentNode.parentNode
15015          // they get detached?
15016     }
15017     
15018     
15019     Roo.View.superclass.constructor.call(this);
15020     
15021     
15022 };
15023
15024 Roo.extend(Roo.View, Roo.util.Observable, {
15025     
15026      /**
15027      * @cfg {Roo.data.Store} store Data store to load data from.
15028      */
15029     store : false,
15030     
15031     /**
15032      * @cfg {String|Roo.Element} el The container element.
15033      */
15034     el : '',
15035     
15036     /**
15037      * @cfg {String|Roo.Template} tpl The template used by this View 
15038      */
15039     tpl : false,
15040     /**
15041      * @cfg {String} dataName the named area of the template to use as the data area
15042      *                          Works with domtemplates roo-name="name"
15043      */
15044     dataName: false,
15045     /**
15046      * @cfg {String} selectedClass The css class to add to selected nodes
15047      */
15048     selectedClass : "x-view-selected",
15049      /**
15050      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15051      */
15052     emptyText : "",
15053     
15054     /**
15055      * @cfg {String} text to display on mask (default Loading)
15056      */
15057     mask : false,
15058     /**
15059      * @cfg {Boolean} multiSelect Allow multiple selection
15060      */
15061     multiSelect : false,
15062     /**
15063      * @cfg {Boolean} singleSelect Allow single selection
15064      */
15065     singleSelect:  false,
15066     
15067     /**
15068      * @cfg {Boolean} toggleSelect - selecting 
15069      */
15070     toggleSelect : false,
15071     
15072     /**
15073      * @cfg {Boolean} tickable - selecting 
15074      */
15075     tickable : false,
15076     
15077     /**
15078      * Returns the element this view is bound to.
15079      * @return {Roo.Element}
15080      */
15081     getEl : function(){
15082         return this.wrapEl;
15083     },
15084     
15085     
15086
15087     /**
15088      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15089      */
15090     refresh : function(){
15091         //Roo.log('refresh');
15092         var t = this.tpl;
15093         
15094         // if we are using something like 'domtemplate', then
15095         // the what gets used is:
15096         // t.applySubtemplate(NAME, data, wrapping data..)
15097         // the outer template then get' applied with
15098         //     the store 'extra data'
15099         // and the body get's added to the
15100         //      roo-name="data" node?
15101         //      <span class='roo-tpl-{name}'></span> ?????
15102         
15103         
15104         
15105         this.clearSelections();
15106         this.el.update("");
15107         var html = [];
15108         var records = this.store.getRange();
15109         if(records.length < 1) {
15110             
15111             // is this valid??  = should it render a template??
15112             
15113             this.el.update(this.emptyText);
15114             return;
15115         }
15116         var el = this.el;
15117         if (this.dataName) {
15118             this.el.update(t.apply(this.store.meta)); //????
15119             el = this.el.child('.roo-tpl-' + this.dataName);
15120         }
15121         
15122         for(var i = 0, len = records.length; i < len; i++){
15123             var data = this.prepareData(records[i].data, i, records[i]);
15124             this.fireEvent("preparedata", this, data, i, records[i]);
15125             
15126             var d = Roo.apply({}, data);
15127             
15128             if(this.tickable){
15129                 Roo.apply(d, {'roo-id' : Roo.id()});
15130                 
15131                 var _this = this;
15132             
15133                 Roo.each(this.parent.item, function(item){
15134                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15135                         return;
15136                     }
15137                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15138                 });
15139             }
15140             
15141             html[html.length] = Roo.util.Format.trim(
15142                 this.dataName ?
15143                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15144                     t.apply(d)
15145             );
15146         }
15147         
15148         
15149         
15150         el.update(html.join(""));
15151         this.nodes = el.dom.childNodes;
15152         this.updateIndexes(0);
15153     },
15154     
15155
15156     /**
15157      * Function to override to reformat the data that is sent to
15158      * the template for each node.
15159      * DEPRICATED - use the preparedata event handler.
15160      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15161      * a JSON object for an UpdateManager bound view).
15162      */
15163     prepareData : function(data, index, record)
15164     {
15165         this.fireEvent("preparedata", this, data, index, record);
15166         return data;
15167     },
15168
15169     onUpdate : function(ds, record){
15170         // Roo.log('on update');   
15171         this.clearSelections();
15172         var index = this.store.indexOf(record);
15173         var n = this.nodes[index];
15174         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15175         n.parentNode.removeChild(n);
15176         this.updateIndexes(index, index);
15177     },
15178
15179     
15180     
15181 // --------- FIXME     
15182     onAdd : function(ds, records, index)
15183     {
15184         //Roo.log(['on Add', ds, records, index] );        
15185         this.clearSelections();
15186         if(this.nodes.length == 0){
15187             this.refresh();
15188             return;
15189         }
15190         var n = this.nodes[index];
15191         for(var i = 0, len = records.length; i < len; i++){
15192             var d = this.prepareData(records[i].data, i, records[i]);
15193             if(n){
15194                 this.tpl.insertBefore(n, d);
15195             }else{
15196                 
15197                 this.tpl.append(this.el, d);
15198             }
15199         }
15200         this.updateIndexes(index);
15201     },
15202
15203     onRemove : function(ds, record, index){
15204        // Roo.log('onRemove');
15205         this.clearSelections();
15206         var el = this.dataName  ?
15207             this.el.child('.roo-tpl-' + this.dataName) :
15208             this.el; 
15209         
15210         el.dom.removeChild(this.nodes[index]);
15211         this.updateIndexes(index);
15212     },
15213
15214     /**
15215      * Refresh an individual node.
15216      * @param {Number} index
15217      */
15218     refreshNode : function(index){
15219         this.onUpdate(this.store, this.store.getAt(index));
15220     },
15221
15222     updateIndexes : function(startIndex, endIndex){
15223         var ns = this.nodes;
15224         startIndex = startIndex || 0;
15225         endIndex = endIndex || ns.length - 1;
15226         for(var i = startIndex; i <= endIndex; i++){
15227             ns[i].nodeIndex = i;
15228         }
15229     },
15230
15231     /**
15232      * Changes the data store this view uses and refresh the view.
15233      * @param {Store} store
15234      */
15235     setStore : function(store, initial){
15236         if(!initial && this.store){
15237             this.store.un("datachanged", this.refresh);
15238             this.store.un("add", this.onAdd);
15239             this.store.un("remove", this.onRemove);
15240             this.store.un("update", this.onUpdate);
15241             this.store.un("clear", this.refresh);
15242             this.store.un("beforeload", this.onBeforeLoad);
15243             this.store.un("load", this.onLoad);
15244             this.store.un("loadexception", this.onLoad);
15245         }
15246         if(store){
15247           
15248             store.on("datachanged", this.refresh, this);
15249             store.on("add", this.onAdd, this);
15250             store.on("remove", this.onRemove, this);
15251             store.on("update", this.onUpdate, this);
15252             store.on("clear", this.refresh, this);
15253             store.on("beforeload", this.onBeforeLoad, this);
15254             store.on("load", this.onLoad, this);
15255             store.on("loadexception", this.onLoad, this);
15256         }
15257         
15258         if(store){
15259             this.refresh();
15260         }
15261     },
15262     /**
15263      * onbeforeLoad - masks the loading area.
15264      *
15265      */
15266     onBeforeLoad : function(store,opts)
15267     {
15268          //Roo.log('onBeforeLoad');   
15269         if (!opts.add) {
15270             this.el.update("");
15271         }
15272         this.el.mask(this.mask ? this.mask : "Loading" ); 
15273     },
15274     onLoad : function ()
15275     {
15276         this.el.unmask();
15277     },
15278     
15279
15280     /**
15281      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15282      * @param {HTMLElement} node
15283      * @return {HTMLElement} The template node
15284      */
15285     findItemFromChild : function(node){
15286         var el = this.dataName  ?
15287             this.el.child('.roo-tpl-' + this.dataName,true) :
15288             this.el.dom; 
15289         
15290         if(!node || node.parentNode == el){
15291                     return node;
15292             }
15293             var p = node.parentNode;
15294             while(p && p != el){
15295             if(p.parentNode == el){
15296                 return p;
15297             }
15298             p = p.parentNode;
15299         }
15300             return null;
15301     },
15302
15303     /** @ignore */
15304     onClick : function(e){
15305         var item = this.findItemFromChild(e.getTarget());
15306         if(item){
15307             var index = this.indexOf(item);
15308             if(this.onItemClick(item, index, e) !== false){
15309                 this.fireEvent("click", this, index, item, e);
15310             }
15311         }else{
15312             this.clearSelections();
15313         }
15314     },
15315
15316     /** @ignore */
15317     onContextMenu : function(e){
15318         var item = this.findItemFromChild(e.getTarget());
15319         if(item){
15320             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15321         }
15322     },
15323
15324     /** @ignore */
15325     onDblClick : function(e){
15326         var item = this.findItemFromChild(e.getTarget());
15327         if(item){
15328             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15329         }
15330     },
15331
15332     onItemClick : function(item, index, e)
15333     {
15334         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15335             return false;
15336         }
15337         if (this.toggleSelect) {
15338             var m = this.isSelected(item) ? 'unselect' : 'select';
15339             //Roo.log(m);
15340             var _t = this;
15341             _t[m](item, true, false);
15342             return true;
15343         }
15344         if(this.multiSelect || this.singleSelect){
15345             if(this.multiSelect && e.shiftKey && this.lastSelection){
15346                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15347             }else{
15348                 this.select(item, this.multiSelect && e.ctrlKey);
15349                 this.lastSelection = item;
15350             }
15351             
15352             if(!this.tickable){
15353                 e.preventDefault();
15354             }
15355             
15356         }
15357         return true;
15358     },
15359
15360     /**
15361      * Get the number of selected nodes.
15362      * @return {Number}
15363      */
15364     getSelectionCount : function(){
15365         return this.selections.length;
15366     },
15367
15368     /**
15369      * Get the currently selected nodes.
15370      * @return {Array} An array of HTMLElements
15371      */
15372     getSelectedNodes : function(){
15373         return this.selections;
15374     },
15375
15376     /**
15377      * Get the indexes of the selected nodes.
15378      * @return {Array}
15379      */
15380     getSelectedIndexes : function(){
15381         var indexes = [], s = this.selections;
15382         for(var i = 0, len = s.length; i < len; i++){
15383             indexes.push(s[i].nodeIndex);
15384         }
15385         return indexes;
15386     },
15387
15388     /**
15389      * Clear all selections
15390      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15391      */
15392     clearSelections : function(suppressEvent){
15393         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15394             this.cmp.elements = this.selections;
15395             this.cmp.removeClass(this.selectedClass);
15396             this.selections = [];
15397             if(!suppressEvent){
15398                 this.fireEvent("selectionchange", this, this.selections);
15399             }
15400         }
15401     },
15402
15403     /**
15404      * Returns true if the passed node is selected
15405      * @param {HTMLElement/Number} node The node or node index
15406      * @return {Boolean}
15407      */
15408     isSelected : function(node){
15409         var s = this.selections;
15410         if(s.length < 1){
15411             return false;
15412         }
15413         node = this.getNode(node);
15414         return s.indexOf(node) !== -1;
15415     },
15416
15417     /**
15418      * Selects nodes.
15419      * @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
15420      * @param {Boolean} keepExisting (optional) true to keep existing selections
15421      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15422      */
15423     select : function(nodeInfo, keepExisting, suppressEvent){
15424         if(nodeInfo instanceof Array){
15425             if(!keepExisting){
15426                 this.clearSelections(true);
15427             }
15428             for(var i = 0, len = nodeInfo.length; i < len; i++){
15429                 this.select(nodeInfo[i], true, true);
15430             }
15431             return;
15432         } 
15433         var node = this.getNode(nodeInfo);
15434         if(!node || this.isSelected(node)){
15435             return; // already selected.
15436         }
15437         if(!keepExisting){
15438             this.clearSelections(true);
15439         }
15440         
15441         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15442             Roo.fly(node).addClass(this.selectedClass);
15443             this.selections.push(node);
15444             if(!suppressEvent){
15445                 this.fireEvent("selectionchange", this, this.selections);
15446             }
15447         }
15448         
15449         
15450     },
15451       /**
15452      * Unselects nodes.
15453      * @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
15454      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15455      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15456      */
15457     unselect : function(nodeInfo, keepExisting, suppressEvent)
15458     {
15459         if(nodeInfo instanceof Array){
15460             Roo.each(this.selections, function(s) {
15461                 this.unselect(s, nodeInfo);
15462             }, this);
15463             return;
15464         }
15465         var node = this.getNode(nodeInfo);
15466         if(!node || !this.isSelected(node)){
15467             //Roo.log("not selected");
15468             return; // not selected.
15469         }
15470         // fireevent???
15471         var ns = [];
15472         Roo.each(this.selections, function(s) {
15473             if (s == node ) {
15474                 Roo.fly(node).removeClass(this.selectedClass);
15475
15476                 return;
15477             }
15478             ns.push(s);
15479         },this);
15480         
15481         this.selections= ns;
15482         this.fireEvent("selectionchange", this, this.selections);
15483     },
15484
15485     /**
15486      * Gets a template node.
15487      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15488      * @return {HTMLElement} The node or null if it wasn't found
15489      */
15490     getNode : function(nodeInfo){
15491         if(typeof nodeInfo == "string"){
15492             return document.getElementById(nodeInfo);
15493         }else if(typeof nodeInfo == "number"){
15494             return this.nodes[nodeInfo];
15495         }
15496         return nodeInfo;
15497     },
15498
15499     /**
15500      * Gets a range template nodes.
15501      * @param {Number} startIndex
15502      * @param {Number} endIndex
15503      * @return {Array} An array of nodes
15504      */
15505     getNodes : function(start, end){
15506         var ns = this.nodes;
15507         start = start || 0;
15508         end = typeof end == "undefined" ? ns.length - 1 : end;
15509         var nodes = [];
15510         if(start <= end){
15511             for(var i = start; i <= end; i++){
15512                 nodes.push(ns[i]);
15513             }
15514         } else{
15515             for(var i = start; i >= end; i--){
15516                 nodes.push(ns[i]);
15517             }
15518         }
15519         return nodes;
15520     },
15521
15522     /**
15523      * Finds the index of the passed node
15524      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15525      * @return {Number} The index of the node or -1
15526      */
15527     indexOf : function(node){
15528         node = this.getNode(node);
15529         if(typeof node.nodeIndex == "number"){
15530             return node.nodeIndex;
15531         }
15532         var ns = this.nodes;
15533         for(var i = 0, len = ns.length; i < len; i++){
15534             if(ns[i] == node){
15535                 return i;
15536             }
15537         }
15538         return -1;
15539     }
15540 });
15541 /*
15542  * - LGPL
15543  *
15544  * based on jquery fullcalendar
15545  * 
15546  */
15547
15548 Roo.bootstrap = Roo.bootstrap || {};
15549 /**
15550  * @class Roo.bootstrap.Calendar
15551  * @extends Roo.bootstrap.Component
15552  * Bootstrap Calendar class
15553  * @cfg {Boolean} loadMask (true|false) default false
15554  * @cfg {Object} header generate the user specific header of the calendar, default false
15555
15556  * @constructor
15557  * Create a new Container
15558  * @param {Object} config The config object
15559  */
15560
15561
15562
15563 Roo.bootstrap.Calendar = function(config){
15564     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15565      this.addEvents({
15566         /**
15567              * @event select
15568              * Fires when a date is selected
15569              * @param {DatePicker} this
15570              * @param {Date} date The selected date
15571              */
15572         'select': true,
15573         /**
15574              * @event monthchange
15575              * Fires when the displayed month changes 
15576              * @param {DatePicker} this
15577              * @param {Date} date The selected month
15578              */
15579         'monthchange': true,
15580         /**
15581              * @event evententer
15582              * Fires when mouse over an event
15583              * @param {Calendar} this
15584              * @param {event} Event
15585              */
15586         'evententer': true,
15587         /**
15588              * @event eventleave
15589              * Fires when the mouse leaves an
15590              * @param {Calendar} this
15591              * @param {event}
15592              */
15593         'eventleave': true,
15594         /**
15595              * @event eventclick
15596              * Fires when the mouse click an
15597              * @param {Calendar} this
15598              * @param {event}
15599              */
15600         'eventclick': true
15601         
15602     });
15603
15604 };
15605
15606 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
15607     
15608      /**
15609      * @cfg {Number} startDay
15610      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15611      */
15612     startDay : 0,
15613     
15614     loadMask : false,
15615     
15616     header : false,
15617       
15618     getAutoCreate : function(){
15619         
15620         
15621         var fc_button = function(name, corner, style, content ) {
15622             return Roo.apply({},{
15623                 tag : 'span',
15624                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
15625                          (corner.length ?
15626                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15627                             ''
15628                         ),
15629                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15630                 unselectable: 'on'
15631             });
15632         };
15633         
15634         var header = {};
15635         
15636         if(!this.header){
15637             header = {
15638                 tag : 'table',
15639                 cls : 'fc-header',
15640                 style : 'width:100%',
15641                 cn : [
15642                     {
15643                         tag: 'tr',
15644                         cn : [
15645                             {
15646                                 tag : 'td',
15647                                 cls : 'fc-header-left',
15648                                 cn : [
15649                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
15650                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
15651                                     { tag: 'span', cls: 'fc-header-space' },
15652                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
15653
15654
15655                                 ]
15656                             },
15657
15658                             {
15659                                 tag : 'td',
15660                                 cls : 'fc-header-center',
15661                                 cn : [
15662                                     {
15663                                         tag: 'span',
15664                                         cls: 'fc-header-title',
15665                                         cn : {
15666                                             tag: 'H2',
15667                                             html : 'month / year'
15668                                         }
15669                                     }
15670
15671                                 ]
15672                             },
15673                             {
15674                                 tag : 'td',
15675                                 cls : 'fc-header-right',
15676                                 cn : [
15677                               /*      fc_button('month', 'left', '', 'month' ),
15678                                     fc_button('week', '', '', 'week' ),
15679                                     fc_button('day', 'right', '', 'day' )
15680                                 */    
15681
15682                                 ]
15683                             }
15684
15685                         ]
15686                     }
15687                 ]
15688             };
15689         }
15690         
15691         header = this.header;
15692         
15693        
15694         var cal_heads = function() {
15695             var ret = [];
15696             // fixme - handle this.
15697             
15698             for (var i =0; i < Date.dayNames.length; i++) {
15699                 var d = Date.dayNames[i];
15700                 ret.push({
15701                     tag: 'th',
15702                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15703                     html : d.substring(0,3)
15704                 });
15705                 
15706             }
15707             ret[0].cls += ' fc-first';
15708             ret[6].cls += ' fc-last';
15709             return ret;
15710         };
15711         var cal_cell = function(n) {
15712             return  {
15713                 tag: 'td',
15714                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15715                 cn : [
15716                     {
15717                         cn : [
15718                             {
15719                                 cls: 'fc-day-number',
15720                                 html: 'D'
15721                             },
15722                             {
15723                                 cls: 'fc-day-content',
15724                              
15725                                 cn : [
15726                                      {
15727                                         style: 'position: relative;' // height: 17px;
15728                                     }
15729                                 ]
15730                             }
15731                             
15732                             
15733                         ]
15734                     }
15735                 ]
15736                 
15737             }
15738         };
15739         var cal_rows = function() {
15740             
15741             var ret = [];
15742             for (var r = 0; r < 6; r++) {
15743                 var row= {
15744                     tag : 'tr',
15745                     cls : 'fc-week',
15746                     cn : []
15747                 };
15748                 
15749                 for (var i =0; i < Date.dayNames.length; i++) {
15750                     var d = Date.dayNames[i];
15751                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15752
15753                 }
15754                 row.cn[0].cls+=' fc-first';
15755                 row.cn[0].cn[0].style = 'min-height:90px';
15756                 row.cn[6].cls+=' fc-last';
15757                 ret.push(row);
15758                 
15759             }
15760             ret[0].cls += ' fc-first';
15761             ret[4].cls += ' fc-prev-last';
15762             ret[5].cls += ' fc-last';
15763             return ret;
15764             
15765         };
15766         
15767         var cal_table = {
15768             tag: 'table',
15769             cls: 'fc-border-separate',
15770             style : 'width:100%',
15771             cellspacing  : 0,
15772             cn : [
15773                 { 
15774                     tag: 'thead',
15775                     cn : [
15776                         { 
15777                             tag: 'tr',
15778                             cls : 'fc-first fc-last',
15779                             cn : cal_heads()
15780                         }
15781                     ]
15782                 },
15783                 { 
15784                     tag: 'tbody',
15785                     cn : cal_rows()
15786                 }
15787                   
15788             ]
15789         };
15790          
15791          var cfg = {
15792             cls : 'fc fc-ltr',
15793             cn : [
15794                 header,
15795                 {
15796                     cls : 'fc-content',
15797                     style : "position: relative;",
15798                     cn : [
15799                         {
15800                             cls : 'fc-view fc-view-month fc-grid',
15801                             style : 'position: relative',
15802                             unselectable : 'on',
15803                             cn : [
15804                                 {
15805                                     cls : 'fc-event-container',
15806                                     style : 'position:absolute;z-index:8;top:0;left:0;'
15807                                 },
15808                                 cal_table
15809                             ]
15810                         }
15811                     ]
15812     
15813                 }
15814            ] 
15815             
15816         };
15817         
15818          
15819         
15820         return cfg;
15821     },
15822     
15823     
15824     initEvents : function()
15825     {
15826         if(!this.store){
15827             throw "can not find store for calendar";
15828         }
15829         
15830         var mark = {
15831             tag: "div",
15832             cls:"x-dlg-mask",
15833             style: "text-align:center",
15834             cn: [
15835                 {
15836                     tag: "div",
15837                     style: "background-color:white;width:50%;margin:250 auto",
15838                     cn: [
15839                         {
15840                             tag: "img",
15841                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
15842                         },
15843                         {
15844                             tag: "span",
15845                             html: "Loading"
15846                         }
15847                         
15848                     ]
15849                 }
15850             ]
15851         };
15852         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15853         
15854         var size = this.el.select('.fc-content', true).first().getSize();
15855         this.maskEl.setSize(size.width, size.height);
15856         this.maskEl.enableDisplayMode("block");
15857         if(!this.loadMask){
15858             this.maskEl.hide();
15859         }
15860         
15861         this.store = Roo.factory(this.store, Roo.data);
15862         this.store.on('load', this.onLoad, this);
15863         this.store.on('beforeload', this.onBeforeLoad, this);
15864         
15865         this.resize();
15866         
15867         this.cells = this.el.select('.fc-day',true);
15868         //Roo.log(this.cells);
15869         this.textNodes = this.el.query('.fc-day-number');
15870         this.cells.addClassOnOver('fc-state-hover');
15871         
15872         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15873         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15874         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15875         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15876         
15877         this.on('monthchange', this.onMonthChange, this);
15878         
15879         this.update(new Date().clearTime());
15880     },
15881     
15882     resize : function() {
15883         var sz  = this.el.getSize();
15884         
15885         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15886         this.el.select('.fc-day-content div',true).setHeight(34);
15887     },
15888     
15889     
15890     // private
15891     showPrevMonth : function(e){
15892         this.update(this.activeDate.add("mo", -1));
15893     },
15894     showToday : function(e){
15895         this.update(new Date().clearTime());
15896     },
15897     // private
15898     showNextMonth : function(e){
15899         this.update(this.activeDate.add("mo", 1));
15900     },
15901
15902     // private
15903     showPrevYear : function(){
15904         this.update(this.activeDate.add("y", -1));
15905     },
15906
15907     // private
15908     showNextYear : function(){
15909         this.update(this.activeDate.add("y", 1));
15910     },
15911
15912     
15913    // private
15914     update : function(date)
15915     {
15916         var vd = this.activeDate;
15917         this.activeDate = date;
15918 //        if(vd && this.el){
15919 //            var t = date.getTime();
15920 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15921 //                Roo.log('using add remove');
15922 //                
15923 //                this.fireEvent('monthchange', this, date);
15924 //                
15925 //                this.cells.removeClass("fc-state-highlight");
15926 //                this.cells.each(function(c){
15927 //                   if(c.dateValue == t){
15928 //                       c.addClass("fc-state-highlight");
15929 //                       setTimeout(function(){
15930 //                            try{c.dom.firstChild.focus();}catch(e){}
15931 //                       }, 50);
15932 //                       return false;
15933 //                   }
15934 //                   return true;
15935 //                });
15936 //                return;
15937 //            }
15938 //        }
15939         
15940         var days = date.getDaysInMonth();
15941         
15942         var firstOfMonth = date.getFirstDateOfMonth();
15943         var startingPos = firstOfMonth.getDay()-this.startDay;
15944         
15945         if(startingPos < this.startDay){
15946             startingPos += 7;
15947         }
15948         
15949         var pm = date.add(Date.MONTH, -1);
15950         var prevStart = pm.getDaysInMonth()-startingPos;
15951 //        
15952         this.cells = this.el.select('.fc-day',true);
15953         this.textNodes = this.el.query('.fc-day-number');
15954         this.cells.addClassOnOver('fc-state-hover');
15955         
15956         var cells = this.cells.elements;
15957         var textEls = this.textNodes;
15958         
15959         Roo.each(cells, function(cell){
15960             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15961         });
15962         
15963         days += startingPos;
15964
15965         // convert everything to numbers so it's fast
15966         var day = 86400000;
15967         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15968         //Roo.log(d);
15969         //Roo.log(pm);
15970         //Roo.log(prevStart);
15971         
15972         var today = new Date().clearTime().getTime();
15973         var sel = date.clearTime().getTime();
15974         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15975         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15976         var ddMatch = this.disabledDatesRE;
15977         var ddText = this.disabledDatesText;
15978         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15979         var ddaysText = this.disabledDaysText;
15980         var format = this.format;
15981         
15982         var setCellClass = function(cal, cell){
15983             cell.row = 0;
15984             cell.events = [];
15985             cell.more = [];
15986             //Roo.log('set Cell Class');
15987             cell.title = "";
15988             var t = d.getTime();
15989             
15990             //Roo.log(d);
15991             
15992             cell.dateValue = t;
15993             if(t == today){
15994                 cell.className += " fc-today";
15995                 cell.className += " fc-state-highlight";
15996                 cell.title = cal.todayText;
15997             }
15998             if(t == sel){
15999                 // disable highlight in other month..
16000                 //cell.className += " fc-state-highlight";
16001                 
16002             }
16003             // disabling
16004             if(t < min) {
16005                 cell.className = " fc-state-disabled";
16006                 cell.title = cal.minText;
16007                 return;
16008             }
16009             if(t > max) {
16010                 cell.className = " fc-state-disabled";
16011                 cell.title = cal.maxText;
16012                 return;
16013             }
16014             if(ddays){
16015                 if(ddays.indexOf(d.getDay()) != -1){
16016                     cell.title = ddaysText;
16017                     cell.className = " fc-state-disabled";
16018                 }
16019             }
16020             if(ddMatch && format){
16021                 var fvalue = d.dateFormat(format);
16022                 if(ddMatch.test(fvalue)){
16023                     cell.title = ddText.replace("%0", fvalue);
16024                     cell.className = " fc-state-disabled";
16025                 }
16026             }
16027             
16028             if (!cell.initialClassName) {
16029                 cell.initialClassName = cell.dom.className;
16030             }
16031             
16032             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16033         };
16034
16035         var i = 0;
16036         
16037         for(; i < startingPos; i++) {
16038             textEls[i].innerHTML = (++prevStart);
16039             d.setDate(d.getDate()+1);
16040             
16041             cells[i].className = "fc-past fc-other-month";
16042             setCellClass(this, cells[i]);
16043         }
16044         
16045         var intDay = 0;
16046         
16047         for(; i < days; i++){
16048             intDay = i - startingPos + 1;
16049             textEls[i].innerHTML = (intDay);
16050             d.setDate(d.getDate()+1);
16051             
16052             cells[i].className = ''; // "x-date-active";
16053             setCellClass(this, cells[i]);
16054         }
16055         var extraDays = 0;
16056         
16057         for(; i < 42; i++) {
16058             textEls[i].innerHTML = (++extraDays);
16059             d.setDate(d.getDate()+1);
16060             
16061             cells[i].className = "fc-future fc-other-month";
16062             setCellClass(this, cells[i]);
16063         }
16064         
16065         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16066         
16067         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16068         
16069         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16070         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16071         
16072         if(totalRows != 6){
16073             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16074             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16075         }
16076         
16077         this.fireEvent('monthchange', this, date);
16078         
16079         
16080         /*
16081         if(!this.internalRender){
16082             var main = this.el.dom.firstChild;
16083             var w = main.offsetWidth;
16084             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16085             Roo.fly(main).setWidth(w);
16086             this.internalRender = true;
16087             // opera does not respect the auto grow header center column
16088             // then, after it gets a width opera refuses to recalculate
16089             // without a second pass
16090             if(Roo.isOpera && !this.secondPass){
16091                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16092                 this.secondPass = true;
16093                 this.update.defer(10, this, [date]);
16094             }
16095         }
16096         */
16097         
16098     },
16099     
16100     findCell : function(dt) {
16101         dt = dt.clearTime().getTime();
16102         var ret = false;
16103         this.cells.each(function(c){
16104             //Roo.log("check " +c.dateValue + '?=' + dt);
16105             if(c.dateValue == dt){
16106                 ret = c;
16107                 return false;
16108             }
16109             return true;
16110         });
16111         
16112         return ret;
16113     },
16114     
16115     findCells : function(ev) {
16116         var s = ev.start.clone().clearTime().getTime();
16117        // Roo.log(s);
16118         var e= ev.end.clone().clearTime().getTime();
16119        // Roo.log(e);
16120         var ret = [];
16121         this.cells.each(function(c){
16122              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16123             
16124             if(c.dateValue > e){
16125                 return ;
16126             }
16127             if(c.dateValue < s){
16128                 return ;
16129             }
16130             ret.push(c);
16131         });
16132         
16133         return ret;    
16134     },
16135     
16136 //    findBestRow: function(cells)
16137 //    {
16138 //        var ret = 0;
16139 //        
16140 //        for (var i =0 ; i < cells.length;i++) {
16141 //            ret  = Math.max(cells[i].rows || 0,ret);
16142 //        }
16143 //        return ret;
16144 //        
16145 //    },
16146     
16147     
16148     addItem : function(ev)
16149     {
16150         // look for vertical location slot in
16151         var cells = this.findCells(ev);
16152         
16153 //        ev.row = this.findBestRow(cells);
16154         
16155         // work out the location.
16156         
16157         var crow = false;
16158         var rows = [];
16159         for(var i =0; i < cells.length; i++) {
16160             
16161             cells[i].row = cells[0].row;
16162             
16163             if(i == 0){
16164                 cells[i].row = cells[i].row + 1;
16165             }
16166             
16167             if (!crow) {
16168                 crow = {
16169                     start : cells[i],
16170                     end :  cells[i]
16171                 };
16172                 continue;
16173             }
16174             if (crow.start.getY() == cells[i].getY()) {
16175                 // on same row.
16176                 crow.end = cells[i];
16177                 continue;
16178             }
16179             // different row.
16180             rows.push(crow);
16181             crow = {
16182                 start: cells[i],
16183                 end : cells[i]
16184             };
16185             
16186         }
16187         
16188         rows.push(crow);
16189         ev.els = [];
16190         ev.rows = rows;
16191         ev.cells = cells;
16192         
16193         cells[0].events.push(ev);
16194         
16195         this.calevents.push(ev);
16196     },
16197     
16198     clearEvents: function() {
16199         
16200         if(!this.calevents){
16201             return;
16202         }
16203         
16204         Roo.each(this.cells.elements, function(c){
16205             c.row = 0;
16206             c.events = [];
16207             c.more = [];
16208         });
16209         
16210         Roo.each(this.calevents, function(e) {
16211             Roo.each(e.els, function(el) {
16212                 el.un('mouseenter' ,this.onEventEnter, this);
16213                 el.un('mouseleave' ,this.onEventLeave, this);
16214                 el.remove();
16215             },this);
16216         },this);
16217         
16218         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16219             e.remove();
16220         });
16221         
16222     },
16223     
16224     renderEvents: function()
16225     {   
16226         var _this = this;
16227         
16228         this.cells.each(function(c) {
16229             
16230             if(c.row < 5){
16231                 return;
16232             }
16233             
16234             var ev = c.events;
16235             
16236             var r = 4;
16237             if(c.row != c.events.length){
16238                 r = 4 - (4 - (c.row - c.events.length));
16239             }
16240             
16241             c.events = ev.slice(0, r);
16242             c.more = ev.slice(r);
16243             
16244             if(c.more.length && c.more.length == 1){
16245                 c.events.push(c.more.pop());
16246             }
16247             
16248             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16249             
16250         });
16251             
16252         this.cells.each(function(c) {
16253             
16254             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16255             
16256             
16257             for (var e = 0; e < c.events.length; e++){
16258                 var ev = c.events[e];
16259                 var rows = ev.rows;
16260                 
16261                 for(var i = 0; i < rows.length; i++) {
16262                 
16263                     // how many rows should it span..
16264
16265                     var  cfg = {
16266                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16267                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16268
16269                         unselectable : "on",
16270                         cn : [
16271                             {
16272                                 cls: 'fc-event-inner',
16273                                 cn : [
16274     //                                {
16275     //                                  tag:'span',
16276     //                                  cls: 'fc-event-time',
16277     //                                  html : cells.length > 1 ? '' : ev.time
16278     //                                },
16279                                     {
16280                                       tag:'span',
16281                                       cls: 'fc-event-title',
16282                                       html : String.format('{0}', ev.title)
16283                                     }
16284
16285
16286                                 ]
16287                             },
16288                             {
16289                                 cls: 'ui-resizable-handle ui-resizable-e',
16290                                 html : '&nbsp;&nbsp;&nbsp'
16291                             }
16292
16293                         ]
16294                     };
16295
16296                     if (i == 0) {
16297                         cfg.cls += ' fc-event-start';
16298                     }
16299                     if ((i+1) == rows.length) {
16300                         cfg.cls += ' fc-event-end';
16301                     }
16302
16303                     var ctr = _this.el.select('.fc-event-container',true).first();
16304                     var cg = ctr.createChild(cfg);
16305
16306                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16307                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16308
16309                     var r = (c.more.length) ? 1 : 0;
16310                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16311                     cg.setWidth(ebox.right - sbox.x -2);
16312
16313                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16314                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16315                     cg.on('click', _this.onEventClick, _this, ev);
16316
16317                     ev.els.push(cg);
16318                     
16319                 }
16320                 
16321             }
16322             
16323             
16324             if(c.more.length){
16325                 var  cfg = {
16326                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16327                     style : 'position: absolute',
16328                     unselectable : "on",
16329                     cn : [
16330                         {
16331                             cls: 'fc-event-inner',
16332                             cn : [
16333                                 {
16334                                   tag:'span',
16335                                   cls: 'fc-event-title',
16336                                   html : 'More'
16337                                 }
16338
16339
16340                             ]
16341                         },
16342                         {
16343                             cls: 'ui-resizable-handle ui-resizable-e',
16344                             html : '&nbsp;&nbsp;&nbsp'
16345                         }
16346
16347                     ]
16348                 };
16349
16350                 var ctr = _this.el.select('.fc-event-container',true).first();
16351                 var cg = ctr.createChild(cfg);
16352
16353                 var sbox = c.select('.fc-day-content',true).first().getBox();
16354                 var ebox = c.select('.fc-day-content',true).first().getBox();
16355                 //Roo.log(cg);
16356                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
16357                 cg.setWidth(ebox.right - sbox.x -2);
16358
16359                 cg.on('click', _this.onMoreEventClick, _this, c.more);
16360                 
16361             }
16362             
16363         });
16364         
16365         
16366         
16367     },
16368     
16369     onEventEnter: function (e, el,event,d) {
16370         this.fireEvent('evententer', this, el, event);
16371     },
16372     
16373     onEventLeave: function (e, el,event,d) {
16374         this.fireEvent('eventleave', this, el, event);
16375     },
16376     
16377     onEventClick: function (e, el,event,d) {
16378         this.fireEvent('eventclick', this, el, event);
16379     },
16380     
16381     onMonthChange: function () {
16382         this.store.load();
16383     },
16384     
16385     onMoreEventClick: function(e, el, more)
16386     {
16387         var _this = this;
16388         
16389         this.calpopover.placement = 'right';
16390         this.calpopover.setTitle('More');
16391         
16392         this.calpopover.setContent('');
16393         
16394         var ctr = this.calpopover.el.select('.popover-content', true).first();
16395         
16396         Roo.each(more, function(m){
16397             var cfg = {
16398                 cls : 'fc-event-hori fc-event-draggable',
16399                 html : m.title
16400             };
16401             var cg = ctr.createChild(cfg);
16402             
16403             cg.on('click', _this.onEventClick, _this, m);
16404         });
16405         
16406         this.calpopover.show(el);
16407         
16408         
16409     },
16410     
16411     onLoad: function () 
16412     {   
16413         this.calevents = [];
16414         var cal = this;
16415         
16416         if(this.store.getCount() > 0){
16417             this.store.data.each(function(d){
16418                cal.addItem({
16419                     id : d.data.id,
16420                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16421                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16422                     time : d.data.start_time,
16423                     title : d.data.title,
16424                     description : d.data.description,
16425                     venue : d.data.venue
16426                 });
16427             });
16428         }
16429         
16430         this.renderEvents();
16431         
16432         if(this.calevents.length && this.loadMask){
16433             this.maskEl.hide();
16434         }
16435     },
16436     
16437     onBeforeLoad: function()
16438     {
16439         this.clearEvents();
16440         if(this.loadMask){
16441             this.maskEl.show();
16442         }
16443     }
16444 });
16445
16446  
16447  /*
16448  * - LGPL
16449  *
16450  * element
16451  * 
16452  */
16453
16454 /**
16455  * @class Roo.bootstrap.Popover
16456  * @extends Roo.bootstrap.Component
16457  * Bootstrap Popover class
16458  * @cfg {String} html contents of the popover   (or false to use children..)
16459  * @cfg {String} title of popover (or false to hide)
16460  * @cfg {String} placement how it is placed
16461  * @cfg {String} trigger click || hover (or false to trigger manually)
16462  * @cfg {String} over what (parent or false to trigger manually.)
16463  * @cfg {Number} delay - delay before showing
16464  
16465  * @constructor
16466  * Create a new Popover
16467  * @param {Object} config The config object
16468  */
16469
16470 Roo.bootstrap.Popover = function(config){
16471     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16472     
16473     this.addEvents({
16474         // raw events
16475          /**
16476          * @event show
16477          * After the popover show
16478          * 
16479          * @param {Roo.bootstrap.Popover} this
16480          */
16481         "show" : true,
16482         /**
16483          * @event hide
16484          * After the popover hide
16485          * 
16486          * @param {Roo.bootstrap.Popover} this
16487          */
16488         "hide" : true
16489     });
16490 };
16491
16492 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
16493     
16494     title: 'Fill in a title',
16495     html: false,
16496     
16497     placement : 'right',
16498     trigger : 'hover', // hover
16499     
16500     delay : 0,
16501     
16502     over: 'parent',
16503     
16504     can_build_overlaid : false,
16505     
16506     getChildContainer : function()
16507     {
16508         return this.el.select('.popover-content',true).first();
16509     },
16510     
16511     getAutoCreate : function(){
16512          
16513         var cfg = {
16514            cls : 'popover roo-dynamic',
16515            style: 'display:block',
16516            cn : [
16517                 {
16518                     cls : 'arrow'
16519                 },
16520                 {
16521                     cls : 'popover-inner',
16522                     cn : [
16523                         {
16524                             tag: 'h3',
16525                             cls: 'popover-title',
16526                             html : this.title
16527                         },
16528                         {
16529                             cls : 'popover-content',
16530                             html : this.html
16531                         }
16532                     ]
16533                     
16534                 }
16535            ]
16536         };
16537         
16538         return cfg;
16539     },
16540     setTitle: function(str)
16541     {
16542         this.title = str;
16543         this.el.select('.popover-title',true).first().dom.innerHTML = str;
16544     },
16545     setContent: function(str)
16546     {
16547         this.html = str;
16548         this.el.select('.popover-content',true).first().dom.innerHTML = str;
16549     },
16550     // as it get's added to the bottom of the page.
16551     onRender : function(ct, position)
16552     {
16553         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16554         if(!this.el){
16555             var cfg = Roo.apply({},  this.getAutoCreate());
16556             cfg.id = Roo.id();
16557             
16558             if (this.cls) {
16559                 cfg.cls += ' ' + this.cls;
16560             }
16561             if (this.style) {
16562                 cfg.style = this.style;
16563             }
16564             //Roo.log("adding to ");
16565             this.el = Roo.get(document.body).createChild(cfg, position);
16566 //            Roo.log(this.el);
16567         }
16568         this.initEvents();
16569     },
16570     
16571     initEvents : function()
16572     {
16573         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16574         this.el.enableDisplayMode('block');
16575         this.el.hide();
16576         if (this.over === false) {
16577             return; 
16578         }
16579         if (this.triggers === false) {
16580             return;
16581         }
16582         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16583         var triggers = this.trigger ? this.trigger.split(' ') : [];
16584         Roo.each(triggers, function(trigger) {
16585         
16586             if (trigger == 'click') {
16587                 on_el.on('click', this.toggle, this);
16588             } else if (trigger != 'manual') {
16589                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
16590                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16591       
16592                 on_el.on(eventIn  ,this.enter, this);
16593                 on_el.on(eventOut, this.leave, this);
16594             }
16595         }, this);
16596         
16597     },
16598     
16599     
16600     // private
16601     timeout : null,
16602     hoverState : null,
16603     
16604     toggle : function () {
16605         this.hoverState == 'in' ? this.leave() : this.enter();
16606     },
16607     
16608     enter : function () {
16609         
16610         clearTimeout(this.timeout);
16611     
16612         this.hoverState = 'in';
16613     
16614         if (!this.delay || !this.delay.show) {
16615             this.show();
16616             return;
16617         }
16618         var _t = this;
16619         this.timeout = setTimeout(function () {
16620             if (_t.hoverState == 'in') {
16621                 _t.show();
16622             }
16623         }, this.delay.show)
16624     },
16625     
16626     leave : function() {
16627         clearTimeout(this.timeout);
16628     
16629         this.hoverState = 'out';
16630     
16631         if (!this.delay || !this.delay.hide) {
16632             this.hide();
16633             return;
16634         }
16635         var _t = this;
16636         this.timeout = setTimeout(function () {
16637             if (_t.hoverState == 'out') {
16638                 _t.hide();
16639             }
16640         }, this.delay.hide)
16641     },
16642     
16643     show : function (on_el)
16644     {
16645         if (!on_el) {
16646             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16647         }
16648         
16649         // set content.
16650         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16651         if (this.html !== false) {
16652             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16653         }
16654         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16655         if (!this.title.length) {
16656             this.el.select('.popover-title',true).hide();
16657         }
16658         
16659         var placement = typeof this.placement == 'function' ?
16660             this.placement.call(this, this.el, on_el) :
16661             this.placement;
16662             
16663         var autoToken = /\s?auto?\s?/i;
16664         var autoPlace = autoToken.test(placement);
16665         if (autoPlace) {
16666             placement = placement.replace(autoToken, '') || 'top';
16667         }
16668         
16669         //this.el.detach()
16670         //this.el.setXY([0,0]);
16671         this.el.show();
16672         this.el.dom.style.display='block';
16673         this.el.addClass(placement);
16674         
16675         //this.el.appendTo(on_el);
16676         
16677         var p = this.getPosition();
16678         var box = this.el.getBox();
16679         
16680         if (autoPlace) {
16681             // fixme..
16682         }
16683         var align = Roo.bootstrap.Popover.alignment[placement];
16684         this.el.alignTo(on_el, align[0],align[1]);
16685         //var arrow = this.el.select('.arrow',true).first();
16686         //arrow.set(align[2], 
16687         
16688         this.el.addClass('in');
16689         
16690         
16691         if (this.el.hasClass('fade')) {
16692             // fade it?
16693         }
16694         
16695         this.hoverState = 'in';
16696         
16697         this.fireEvent('show', this);
16698         
16699     },
16700     hide : function()
16701     {
16702         this.el.setXY([0,0]);
16703         this.el.removeClass('in');
16704         this.el.hide();
16705         this.hoverState = null;
16706         
16707         this.fireEvent('hide', this);
16708     }
16709     
16710 });
16711
16712 Roo.bootstrap.Popover.alignment = {
16713     'left' : ['r-l', [-10,0], 'right'],
16714     'right' : ['l-r', [10,0], 'left'],
16715     'bottom' : ['t-b', [0,10], 'top'],
16716     'top' : [ 'b-t', [0,-10], 'bottom']
16717 };
16718
16719  /*
16720  * - LGPL
16721  *
16722  * Progress
16723  * 
16724  */
16725
16726 /**
16727  * @class Roo.bootstrap.Progress
16728  * @extends Roo.bootstrap.Component
16729  * Bootstrap Progress class
16730  * @cfg {Boolean} striped striped of the progress bar
16731  * @cfg {Boolean} active animated of the progress bar
16732  * 
16733  * 
16734  * @constructor
16735  * Create a new Progress
16736  * @param {Object} config The config object
16737  */
16738
16739 Roo.bootstrap.Progress = function(config){
16740     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16741 };
16742
16743 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
16744     
16745     striped : false,
16746     active: false,
16747     
16748     getAutoCreate : function(){
16749         var cfg = {
16750             tag: 'div',
16751             cls: 'progress'
16752         };
16753         
16754         
16755         if(this.striped){
16756             cfg.cls += ' progress-striped';
16757         }
16758       
16759         if(this.active){
16760             cfg.cls += ' active';
16761         }
16762         
16763         
16764         return cfg;
16765     }
16766    
16767 });
16768
16769  
16770
16771  /*
16772  * - LGPL
16773  *
16774  * ProgressBar
16775  * 
16776  */
16777
16778 /**
16779  * @class Roo.bootstrap.ProgressBar
16780  * @extends Roo.bootstrap.Component
16781  * Bootstrap ProgressBar class
16782  * @cfg {Number} aria_valuenow aria-value now
16783  * @cfg {Number} aria_valuemin aria-value min
16784  * @cfg {Number} aria_valuemax aria-value max
16785  * @cfg {String} label label for the progress bar
16786  * @cfg {String} panel (success | info | warning | danger )
16787  * @cfg {String} role role of the progress bar
16788  * @cfg {String} sr_only text
16789  * 
16790  * 
16791  * @constructor
16792  * Create a new ProgressBar
16793  * @param {Object} config The config object
16794  */
16795
16796 Roo.bootstrap.ProgressBar = function(config){
16797     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16798 };
16799
16800 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
16801     
16802     aria_valuenow : 0,
16803     aria_valuemin : 0,
16804     aria_valuemax : 100,
16805     label : false,
16806     panel : false,
16807     role : false,
16808     sr_only: false,
16809     
16810     getAutoCreate : function()
16811     {
16812         
16813         var cfg = {
16814             tag: 'div',
16815             cls: 'progress-bar',
16816             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16817         };
16818         
16819         if(this.sr_only){
16820             cfg.cn = {
16821                 tag: 'span',
16822                 cls: 'sr-only',
16823                 html: this.sr_only
16824             }
16825         }
16826         
16827         if(this.role){
16828             cfg.role = this.role;
16829         }
16830         
16831         if(this.aria_valuenow){
16832             cfg['aria-valuenow'] = this.aria_valuenow;
16833         }
16834         
16835         if(this.aria_valuemin){
16836             cfg['aria-valuemin'] = this.aria_valuemin;
16837         }
16838         
16839         if(this.aria_valuemax){
16840             cfg['aria-valuemax'] = this.aria_valuemax;
16841         }
16842         
16843         if(this.label && !this.sr_only){
16844             cfg.html = this.label;
16845         }
16846         
16847         if(this.panel){
16848             cfg.cls += ' progress-bar-' + this.panel;
16849         }
16850         
16851         return cfg;
16852     },
16853     
16854     update : function(aria_valuenow)
16855     {
16856         this.aria_valuenow = aria_valuenow;
16857         
16858         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16859     }
16860    
16861 });
16862
16863  
16864
16865  /*
16866  * - LGPL
16867  *
16868  * column
16869  * 
16870  */
16871
16872 /**
16873  * @class Roo.bootstrap.TabGroup
16874  * @extends Roo.bootstrap.Column
16875  * Bootstrap Column class
16876  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16877  * @cfg {Boolean} carousel true to make the group behave like a carousel
16878  * @cfg {Boolean} bullets show bullets for the panels
16879  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16880  * @cfg {Number} timer auto slide timer .. default 0 millisecond
16881  * @cfg {Boolean} showarrow (true|false) show arrow default true
16882  * 
16883  * @constructor
16884  * Create a new TabGroup
16885  * @param {Object} config The config object
16886  */
16887
16888 Roo.bootstrap.TabGroup = function(config){
16889     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16890     if (!this.navId) {
16891         this.navId = Roo.id();
16892     }
16893     this.tabs = [];
16894     Roo.bootstrap.TabGroup.register(this);
16895     
16896 };
16897
16898 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
16899     
16900     carousel : false,
16901     transition : false,
16902     bullets : 0,
16903     timer : 0,
16904     autoslide : false,
16905     slideFn : false,
16906     slideOnTouch : false,
16907     showarrow : true,
16908     
16909     getAutoCreate : function()
16910     {
16911         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16912         
16913         cfg.cls += ' tab-content';
16914         
16915         if (this.carousel) {
16916             cfg.cls += ' carousel slide';
16917             
16918             cfg.cn = [{
16919                cls : 'carousel-inner',
16920                cn : []
16921             }];
16922         
16923             if(this.bullets  && !Roo.isTouch){
16924                 
16925                 var bullets = {
16926                     cls : 'carousel-bullets',
16927                     cn : []
16928                 };
16929                
16930                 if(this.bullets_cls){
16931                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16932                 }
16933                 
16934                 bullets.cn.push({
16935                     cls : 'clear'
16936                 });
16937                 
16938                 cfg.cn[0].cn.push(bullets);
16939             }
16940             
16941             if(this.showarrow){
16942                 cfg.cn[0].cn.push({
16943                     tag : 'div',
16944                     class : 'carousel-arrow',
16945                     cn : [
16946                         {
16947                             tag : 'div',
16948                             class : 'carousel-prev',
16949                             cn : [
16950                                 {
16951                                     tag : 'i',
16952                                     class : 'fa fa-chevron-left'
16953                                 }
16954                             ]
16955                         },
16956                         {
16957                             tag : 'div',
16958                             class : 'carousel-next',
16959                             cn : [
16960                                 {
16961                                     tag : 'i',
16962                                     class : 'fa fa-chevron-right'
16963                                 }
16964                             ]
16965                         }
16966                     ]
16967                 });
16968             }
16969             
16970         }
16971         
16972         return cfg;
16973     },
16974     
16975     initEvents:  function()
16976     {
16977 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
16978 //            this.el.on("touchstart", this.onTouchStart, this);
16979 //        }
16980         
16981         if(this.autoslide){
16982             var _this = this;
16983             
16984             this.slideFn = window.setInterval(function() {
16985                 _this.showPanelNext();
16986             }, this.timer);
16987         }
16988         
16989         if(this.showarrow){
16990             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
16991             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
16992         }
16993         
16994         
16995     },
16996     
16997 //    onTouchStart : function(e, el, o)
16998 //    {
16999 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17000 //            return;
17001 //        }
17002 //        
17003 //        this.showPanelNext();
17004 //    },
17005     
17006     
17007     getChildContainer : function()
17008     {
17009         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17010     },
17011     
17012     /**
17013     * register a Navigation item
17014     * @param {Roo.bootstrap.NavItem} the navitem to add
17015     */
17016     register : function(item)
17017     {
17018         this.tabs.push( item);
17019         item.navId = this.navId; // not really needed..
17020         this.addBullet();
17021     
17022     },
17023     
17024     getActivePanel : function()
17025     {
17026         var r = false;
17027         Roo.each(this.tabs, function(t) {
17028             if (t.active) {
17029                 r = t;
17030                 return false;
17031             }
17032             return null;
17033         });
17034         return r;
17035         
17036     },
17037     getPanelByName : function(n)
17038     {
17039         var r = false;
17040         Roo.each(this.tabs, function(t) {
17041             if (t.tabId == n) {
17042                 r = t;
17043                 return false;
17044             }
17045             return null;
17046         });
17047         return r;
17048     },
17049     indexOfPanel : function(p)
17050     {
17051         var r = false;
17052         Roo.each(this.tabs, function(t,i) {
17053             if (t.tabId == p.tabId) {
17054                 r = i;
17055                 return false;
17056             }
17057             return null;
17058         });
17059         return r;
17060     },
17061     /**
17062      * show a specific panel
17063      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17064      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17065      */
17066     showPanel : function (pan)
17067     {
17068         if(this.transition || typeof(pan) == 'undefined'){
17069             Roo.log("waiting for the transitionend");
17070             return;
17071         }
17072         
17073         if (typeof(pan) == 'number') {
17074             pan = this.tabs[pan];
17075         }
17076         
17077         if (typeof(pan) == 'string') {
17078             pan = this.getPanelByName(pan);
17079         }
17080         
17081         var cur = this.getActivePanel();
17082         
17083         if(!pan || !cur){
17084             Roo.log('pan or acitve pan is undefined');
17085             return false;
17086         }
17087         
17088         if (pan.tabId == this.getActivePanel().tabId) {
17089             return true;
17090         }
17091         
17092         if (false === cur.fireEvent('beforedeactivate')) {
17093             return false;
17094         }
17095         
17096         if(this.bullets > 0 && !Roo.isTouch){
17097             this.setActiveBullet(this.indexOfPanel(pan));
17098         }
17099         
17100         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17101             
17102             this.transition = true;
17103             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17104             var lr = dir == 'next' ? 'left' : 'right';
17105             pan.el.addClass(dir); // or prev
17106             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17107             cur.el.addClass(lr); // or right
17108             pan.el.addClass(lr);
17109             
17110             var _this = this;
17111             cur.el.on('transitionend', function() {
17112                 Roo.log("trans end?");
17113                 
17114                 pan.el.removeClass([lr,dir]);
17115                 pan.setActive(true);
17116                 
17117                 cur.el.removeClass([lr]);
17118                 cur.setActive(false);
17119                 
17120                 _this.transition = false;
17121                 
17122             }, this, { single:  true } );
17123             
17124             return true;
17125         }
17126         
17127         cur.setActive(false);
17128         pan.setActive(true);
17129         
17130         return true;
17131         
17132     },
17133     showPanelNext : function()
17134     {
17135         var i = this.indexOfPanel(this.getActivePanel());
17136         
17137         if (i >= this.tabs.length - 1 && !this.autoslide) {
17138             return;
17139         }
17140         
17141         if (i >= this.tabs.length - 1 && this.autoslide) {
17142             i = -1;
17143         }
17144         
17145         this.showPanel(this.tabs[i+1]);
17146     },
17147     
17148     showPanelPrev : function()
17149     {
17150         var i = this.indexOfPanel(this.getActivePanel());
17151         
17152         if (i  < 1 && !this.autoslide) {
17153             return;
17154         }
17155         
17156         if (i < 1 && this.autoslide) {
17157             i = this.tabs.length;
17158         }
17159         
17160         this.showPanel(this.tabs[i-1]);
17161     },
17162     
17163     
17164     addBullet: function()
17165     {
17166         if(!this.bullets || Roo.isTouch){
17167             return;
17168         }
17169         var ctr = this.el.select('.carousel-bullets',true).first();
17170         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17171         var bullet = ctr.createChild({
17172             cls : 'bullet bullet-' + i
17173         },ctr.dom.lastChild);
17174         
17175         
17176         var _this = this;
17177         
17178         bullet.on('click', (function(e, el, o, ii, t){
17179
17180             e.preventDefault();
17181
17182             this.showPanel(ii);
17183
17184             if(this.autoslide && this.slideFn){
17185                 clearInterval(this.slideFn);
17186                 this.slideFn = window.setInterval(function() {
17187                     _this.showPanelNext();
17188                 }, this.timer);
17189             }
17190
17191         }).createDelegate(this, [i, bullet], true));
17192                 
17193         
17194     },
17195      
17196     setActiveBullet : function(i)
17197     {
17198         if(Roo.isTouch){
17199             return;
17200         }
17201         
17202         Roo.each(this.el.select('.bullet', true).elements, function(el){
17203             el.removeClass('selected');
17204         });
17205
17206         var bullet = this.el.select('.bullet-' + i, true).first();
17207         
17208         if(!bullet){
17209             return;
17210         }
17211         
17212         bullet.addClass('selected');
17213     }
17214     
17215     
17216   
17217 });
17218
17219  
17220
17221  
17222  
17223 Roo.apply(Roo.bootstrap.TabGroup, {
17224     
17225     groups: {},
17226      /**
17227     * register a Navigation Group
17228     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17229     */
17230     register : function(navgrp)
17231     {
17232         this.groups[navgrp.navId] = navgrp;
17233         
17234     },
17235     /**
17236     * fetch a Navigation Group based on the navigation ID
17237     * if one does not exist , it will get created.
17238     * @param {string} the navgroup to add
17239     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17240     */
17241     get: function(navId) {
17242         if (typeof(this.groups[navId]) == 'undefined') {
17243             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17244         }
17245         return this.groups[navId] ;
17246     }
17247     
17248     
17249     
17250 });
17251
17252  /*
17253  * - LGPL
17254  *
17255  * TabPanel
17256  * 
17257  */
17258
17259 /**
17260  * @class Roo.bootstrap.TabPanel
17261  * @extends Roo.bootstrap.Component
17262  * Bootstrap TabPanel class
17263  * @cfg {Boolean} active panel active
17264  * @cfg {String} html panel content
17265  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17266  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17267  * @cfg {String} href click to link..
17268  * 
17269  * 
17270  * @constructor
17271  * Create a new TabPanel
17272  * @param {Object} config The config object
17273  */
17274
17275 Roo.bootstrap.TabPanel = function(config){
17276     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17277     this.addEvents({
17278         /**
17279              * @event changed
17280              * Fires when the active status changes
17281              * @param {Roo.bootstrap.TabPanel} this
17282              * @param {Boolean} state the new state
17283             
17284          */
17285         'changed': true,
17286         /**
17287              * @event beforedeactivate
17288              * Fires before a tab is de-activated - can be used to do validation on a form.
17289              * @param {Roo.bootstrap.TabPanel} this
17290              * @return {Boolean} false if there is an error
17291             
17292          */
17293         'beforedeactivate': true
17294      });
17295     
17296     this.tabId = this.tabId || Roo.id();
17297   
17298 };
17299
17300 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17301     
17302     active: false,
17303     html: false,
17304     tabId: false,
17305     navId : false,
17306     href : '',
17307     
17308     getAutoCreate : function(){
17309         var cfg = {
17310             tag: 'div',
17311             // item is needed for carousel - not sure if it has any effect otherwise
17312             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17313             html: this.html || ''
17314         };
17315         
17316         if(this.active){
17317             cfg.cls += ' active';
17318         }
17319         
17320         if(this.tabId){
17321             cfg.tabId = this.tabId;
17322         }
17323         
17324         
17325         return cfg;
17326     },
17327     
17328     initEvents:  function()
17329     {
17330         var p = this.parent();
17331         
17332         this.navId = this.navId || p.navId;
17333         
17334         if (typeof(this.navId) != 'undefined') {
17335             // not really needed.. but just in case.. parent should be a NavGroup.
17336             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17337             
17338             tg.register(this);
17339             
17340             var i = tg.tabs.length - 1;
17341             
17342             if(this.active && tg.bullets > 0 && i < tg.bullets){
17343                 tg.setActiveBullet(i);
17344             }
17345         }
17346         
17347         this.el.on('click', this.onClick, this);
17348         
17349         if(Roo.isTouch){
17350             this.el.on("touchstart", this.onTouchStart, this);
17351             this.el.on("touchmove", this.onTouchMove, this);
17352             this.el.on("touchend", this.onTouchEnd, this);
17353         }
17354         
17355     },
17356     
17357     onRender : function(ct, position)
17358     {
17359         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17360     },
17361     
17362     setActive : function(state)
17363     {
17364         Roo.log("panel - set active " + this.tabId + "=" + state);
17365         
17366         this.active = state;
17367         if (!state) {
17368             this.el.removeClass('active');
17369             
17370         } else  if (!this.el.hasClass('active')) {
17371             this.el.addClass('active');
17372         }
17373         
17374         this.fireEvent('changed', this, state);
17375     },
17376     
17377     onClick : function(e)
17378     {
17379         e.preventDefault();
17380         
17381         if(!this.href.length){
17382             return;
17383         }
17384         
17385         window.location.href = this.href;
17386     },
17387     
17388     startX : 0,
17389     startY : 0,
17390     endX : 0,
17391     endY : 0,
17392     swiping : false,
17393     
17394     onTouchStart : function(e)
17395     {
17396         this.swiping = false;
17397         
17398         this.startX = e.browserEvent.touches[0].clientX;
17399         this.startY = e.browserEvent.touches[0].clientY;
17400     },
17401     
17402     onTouchMove : function(e)
17403     {
17404         this.swiping = true;
17405         
17406         this.endX = e.browserEvent.touches[0].clientX;
17407         this.endY = e.browserEvent.touches[0].clientY;
17408     },
17409     
17410     onTouchEnd : function(e)
17411     {
17412         if(!this.swiping){
17413             this.onClick(e);
17414             return;
17415         }
17416         
17417         var tabGroup = this.parent();
17418         
17419         if(this.endX > this.startX){ // swiping right
17420             tabGroup.showPanelPrev();
17421             return;
17422         }
17423         
17424         if(this.startX > this.endX){ // swiping left
17425             tabGroup.showPanelNext();
17426             return;
17427         }
17428     }
17429     
17430     
17431 });
17432  
17433
17434  
17435
17436  /*
17437  * - LGPL
17438  *
17439  * DateField
17440  * 
17441  */
17442
17443 /**
17444  * @class Roo.bootstrap.DateField
17445  * @extends Roo.bootstrap.Input
17446  * Bootstrap DateField class
17447  * @cfg {Number} weekStart default 0
17448  * @cfg {String} viewMode default empty, (months|years)
17449  * @cfg {String} minViewMode default empty, (months|years)
17450  * @cfg {Number} startDate default -Infinity
17451  * @cfg {Number} endDate default Infinity
17452  * @cfg {Boolean} todayHighlight default false
17453  * @cfg {Boolean} todayBtn default false
17454  * @cfg {Boolean} calendarWeeks default false
17455  * @cfg {Object} daysOfWeekDisabled default empty
17456  * @cfg {Boolean} singleMode default false (true | false)
17457  * 
17458  * @cfg {Boolean} keyboardNavigation default true
17459  * @cfg {String} language default en
17460  * 
17461  * @constructor
17462  * Create a new DateField
17463  * @param {Object} config The config object
17464  */
17465
17466 Roo.bootstrap.DateField = function(config){
17467     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
17468      this.addEvents({
17469             /**
17470              * @event show
17471              * Fires when this field show.
17472              * @param {Roo.bootstrap.DateField} this
17473              * @param {Mixed} date The date value
17474              */
17475             show : true,
17476             /**
17477              * @event show
17478              * Fires when this field hide.
17479              * @param {Roo.bootstrap.DateField} this
17480              * @param {Mixed} date The date value
17481              */
17482             hide : true,
17483             /**
17484              * @event select
17485              * Fires when select a date.
17486              * @param {Roo.bootstrap.DateField} this
17487              * @param {Mixed} date The date value
17488              */
17489             select : true,
17490             /**
17491              * @event beforeselect
17492              * Fires when before select a date.
17493              * @param {Roo.bootstrap.DateField} this
17494              * @param {Mixed} date The date value
17495              */
17496             beforeselect : true
17497         });
17498 };
17499
17500 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
17501     
17502     /**
17503      * @cfg {String} format
17504      * The default date format string which can be overriden for localization support.  The format must be
17505      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17506      */
17507     format : "m/d/y",
17508     /**
17509      * @cfg {String} altFormats
17510      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17511      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17512      */
17513     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17514     
17515     weekStart : 0,
17516     
17517     viewMode : '',
17518     
17519     minViewMode : '',
17520     
17521     todayHighlight : false,
17522     
17523     todayBtn: false,
17524     
17525     language: 'en',
17526     
17527     keyboardNavigation: true,
17528     
17529     calendarWeeks: false,
17530     
17531     startDate: -Infinity,
17532     
17533     endDate: Infinity,
17534     
17535     daysOfWeekDisabled: [],
17536     
17537     _events: [],
17538     
17539     singleMode : false,
17540     
17541     UTCDate: function()
17542     {
17543         return new Date(Date.UTC.apply(Date, arguments));
17544     },
17545     
17546     UTCToday: function()
17547     {
17548         var today = new Date();
17549         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
17550     },
17551     
17552     getDate: function() {
17553             var d = this.getUTCDate();
17554             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
17555     },
17556     
17557     getUTCDate: function() {
17558             return this.date;
17559     },
17560     
17561     setDate: function(d) {
17562             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
17563     },
17564     
17565     setUTCDate: function(d) {
17566             this.date = d;
17567             this.setValue(this.formatDate(this.date));
17568     },
17569         
17570     onRender: function(ct, position)
17571     {
17572         
17573         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
17574         
17575         this.language = this.language || 'en';
17576         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
17577         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
17578         
17579         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17580         this.format = this.format || 'm/d/y';
17581         this.isInline = false;
17582         this.isInput = true;
17583         this.component = this.el.select('.add-on', true).first() || false;
17584         this.component = (this.component && this.component.length === 0) ? false : this.component;
17585         this.hasInput = this.component && this.inputEl().length;
17586         
17587         if (typeof(this.minViewMode === 'string')) {
17588             switch (this.minViewMode) {
17589                 case 'months':
17590                     this.minViewMode = 1;
17591                     break;
17592                 case 'years':
17593                     this.minViewMode = 2;
17594                     break;
17595                 default:
17596                     this.minViewMode = 0;
17597                     break;
17598             }
17599         }
17600         
17601         if (typeof(this.viewMode === 'string')) {
17602             switch (this.viewMode) {
17603                 case 'months':
17604                     this.viewMode = 1;
17605                     break;
17606                 case 'years':
17607                     this.viewMode = 2;
17608                     break;
17609                 default:
17610                     this.viewMode = 0;
17611                     break;
17612             }
17613         }
17614                 
17615         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
17616         
17617 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
17618         
17619         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17620         
17621         this.picker().on('mousedown', this.onMousedown, this);
17622         this.picker().on('click', this.onClick, this);
17623         
17624         this.picker().addClass('datepicker-dropdown');
17625         
17626         this.startViewMode = this.viewMode;
17627         
17628         if(this.singleMode){
17629             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17630                 v.setVisibilityMode(Roo.Element.DISPLAY);
17631                 v.hide();
17632             });
17633             
17634             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17635                 v.setStyle('width', '189px');
17636             });
17637         }
17638         
17639         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17640             if(!this.calendarWeeks){
17641                 v.remove();
17642                 return;
17643             }
17644             
17645             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17646             v.attr('colspan', function(i, val){
17647                 return parseInt(val) + 1;
17648             });
17649         });
17650                         
17651         
17652         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17653         
17654         this.setStartDate(this.startDate);
17655         this.setEndDate(this.endDate);
17656         
17657         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17658         
17659         this.fillDow();
17660         this.fillMonths();
17661         this.update();
17662         this.showMode();
17663         
17664         if(this.isInline) {
17665             this.show();
17666         }
17667     },
17668     
17669     picker : function()
17670     {
17671         return this.pickerEl;
17672 //        return this.el.select('.datepicker', true).first();
17673     },
17674     
17675     fillDow: function()
17676     {
17677         var dowCnt = this.weekStart;
17678         
17679         var dow = {
17680             tag: 'tr',
17681             cn: [
17682                 
17683             ]
17684         };
17685         
17686         if(this.calendarWeeks){
17687             dow.cn.push({
17688                 tag: 'th',
17689                 cls: 'cw',
17690                 html: '&nbsp;'
17691             })
17692         }
17693         
17694         while (dowCnt < this.weekStart + 7) {
17695             dow.cn.push({
17696                 tag: 'th',
17697                 cls: 'dow',
17698                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17699             });
17700         }
17701         
17702         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17703     },
17704     
17705     fillMonths: function()
17706     {    
17707         var i = 0;
17708         var months = this.picker().select('>.datepicker-months td', true).first();
17709         
17710         months.dom.innerHTML = '';
17711         
17712         while (i < 12) {
17713             var month = {
17714                 tag: 'span',
17715                 cls: 'month',
17716                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17717             };
17718             
17719             months.createChild(month);
17720         }
17721         
17722     },
17723     
17724     update: function()
17725     {
17726         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;
17727         
17728         if (this.date < this.startDate) {
17729             this.viewDate = new Date(this.startDate);
17730         } else if (this.date > this.endDate) {
17731             this.viewDate = new Date(this.endDate);
17732         } else {
17733             this.viewDate = new Date(this.date);
17734         }
17735         
17736         this.fill();
17737     },
17738     
17739     fill: function() 
17740     {
17741         var d = new Date(this.viewDate),
17742                 year = d.getUTCFullYear(),
17743                 month = d.getUTCMonth(),
17744                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17745                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17746                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17747                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17748                 currentDate = this.date && this.date.valueOf(),
17749                 today = this.UTCToday();
17750         
17751         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17752         
17753 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17754         
17755 //        this.picker.select('>tfoot th.today').
17756 //                                              .text(dates[this.language].today)
17757 //                                              .toggle(this.todayBtn !== false);
17758     
17759         this.updateNavArrows();
17760         this.fillMonths();
17761                                                 
17762         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17763         
17764         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17765          
17766         prevMonth.setUTCDate(day);
17767         
17768         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17769         
17770         var nextMonth = new Date(prevMonth);
17771         
17772         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17773         
17774         nextMonth = nextMonth.valueOf();
17775         
17776         var fillMonths = false;
17777         
17778         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17779         
17780         while(prevMonth.valueOf() < nextMonth) {
17781             var clsName = '';
17782             
17783             if (prevMonth.getUTCDay() === this.weekStart) {
17784                 if(fillMonths){
17785                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17786                 }
17787                     
17788                 fillMonths = {
17789                     tag: 'tr',
17790                     cn: []
17791                 };
17792                 
17793                 if(this.calendarWeeks){
17794                     // ISO 8601: First week contains first thursday.
17795                     // ISO also states week starts on Monday, but we can be more abstract here.
17796                     var
17797                     // Start of current week: based on weekstart/current date
17798                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17799                     // Thursday of this week
17800                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17801                     // First Thursday of year, year from thursday
17802                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17803                     // Calendar week: ms between thursdays, div ms per day, div 7 days
17804                     calWeek =  (th - yth) / 864e5 / 7 + 1;
17805                     
17806                     fillMonths.cn.push({
17807                         tag: 'td',
17808                         cls: 'cw',
17809                         html: calWeek
17810                     });
17811                 }
17812             }
17813             
17814             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17815                 clsName += ' old';
17816             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17817                 clsName += ' new';
17818             }
17819             if (this.todayHighlight &&
17820                 prevMonth.getUTCFullYear() == today.getFullYear() &&
17821                 prevMonth.getUTCMonth() == today.getMonth() &&
17822                 prevMonth.getUTCDate() == today.getDate()) {
17823                 clsName += ' today';
17824             }
17825             
17826             if (currentDate && prevMonth.valueOf() === currentDate) {
17827                 clsName += ' active';
17828             }
17829             
17830             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17831                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17832                     clsName += ' disabled';
17833             }
17834             
17835             fillMonths.cn.push({
17836                 tag: 'td',
17837                 cls: 'day ' + clsName,
17838                 html: prevMonth.getDate()
17839             });
17840             
17841             prevMonth.setDate(prevMonth.getDate()+1);
17842         }
17843           
17844         var currentYear = this.date && this.date.getUTCFullYear();
17845         var currentMonth = this.date && this.date.getUTCMonth();
17846         
17847         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17848         
17849         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17850             v.removeClass('active');
17851             
17852             if(currentYear === year && k === currentMonth){
17853                 v.addClass('active');
17854             }
17855             
17856             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17857                 v.addClass('disabled');
17858             }
17859             
17860         });
17861         
17862         
17863         year = parseInt(year/10, 10) * 10;
17864         
17865         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17866         
17867         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17868         
17869         year -= 1;
17870         for (var i = -1; i < 11; i++) {
17871             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17872                 tag: 'span',
17873                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17874                 html: year
17875             });
17876             
17877             year += 1;
17878         }
17879     },
17880     
17881     showMode: function(dir) 
17882     {
17883         if (dir) {
17884             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17885         }
17886         
17887         Roo.each(this.picker().select('>div',true).elements, function(v){
17888             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17889             v.hide();
17890         });
17891         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17892     },
17893     
17894     place: function()
17895     {
17896         if(this.isInline) {
17897             return;
17898         }
17899         
17900         this.picker().removeClass(['bottom', 'top']);
17901         
17902         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17903             /*
17904              * place to the top of element!
17905              *
17906              */
17907             
17908             this.picker().addClass('top');
17909             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17910             
17911             return;
17912         }
17913         
17914         this.picker().addClass('bottom');
17915         
17916         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17917     },
17918     
17919     parseDate : function(value)
17920     {
17921         if(!value || value instanceof Date){
17922             return value;
17923         }
17924         var v = Date.parseDate(value, this.format);
17925         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17926             v = Date.parseDate(value, 'Y-m-d');
17927         }
17928         if(!v && this.altFormats){
17929             if(!this.altFormatsArray){
17930                 this.altFormatsArray = this.altFormats.split("|");
17931             }
17932             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17933                 v = Date.parseDate(value, this.altFormatsArray[i]);
17934             }
17935         }
17936         return v;
17937     },
17938     
17939     formatDate : function(date, fmt)
17940     {   
17941         return (!date || !(date instanceof Date)) ?
17942         date : date.dateFormat(fmt || this.format);
17943     },
17944     
17945     onFocus : function()
17946     {
17947         Roo.bootstrap.DateField.superclass.onFocus.call(this);
17948         this.show();
17949     },
17950     
17951     onBlur : function()
17952     {
17953         Roo.bootstrap.DateField.superclass.onBlur.call(this);
17954         
17955         var d = this.inputEl().getValue();
17956         
17957         this.setValue(d);
17958                 
17959         this.hide();
17960     },
17961     
17962     show : function()
17963     {
17964         this.picker().show();
17965         this.update();
17966         this.place();
17967         
17968         this.fireEvent('show', this, this.date);
17969     },
17970     
17971     hide : function()
17972     {
17973         if(this.isInline) {
17974             return;
17975         }
17976         this.picker().hide();
17977         this.viewMode = this.startViewMode;
17978         this.showMode();
17979         
17980         this.fireEvent('hide', this, this.date);
17981         
17982     },
17983     
17984     onMousedown: function(e)
17985     {
17986         e.stopPropagation();
17987         e.preventDefault();
17988     },
17989     
17990     keyup: function(e)
17991     {
17992         Roo.bootstrap.DateField.superclass.keyup.call(this);
17993         this.update();
17994     },
17995
17996     setValue: function(v)
17997     {
17998         if(this.fireEvent('beforeselect', this, v) !== false){
17999             var d = new Date(this.parseDate(v) ).clearTime();
18000         
18001             if(isNaN(d.getTime())){
18002                 this.date = this.viewDate = '';
18003                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18004                 return;
18005             }
18006
18007             v = this.formatDate(d);
18008
18009             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18010
18011             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18012
18013             this.update();
18014
18015             this.fireEvent('select', this, this.date);
18016         }
18017     },
18018     
18019     getValue: function()
18020     {
18021         return this.formatDate(this.date);
18022     },
18023     
18024     fireKey: function(e)
18025     {
18026         if (!this.picker().isVisible()){
18027             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18028                 this.show();
18029             }
18030             return;
18031         }
18032         
18033         var dateChanged = false,
18034         dir, day, month,
18035         newDate, newViewDate;
18036         
18037         switch(e.keyCode){
18038             case 27: // escape
18039                 this.hide();
18040                 e.preventDefault();
18041                 break;
18042             case 37: // left
18043             case 39: // right
18044                 if (!this.keyboardNavigation) {
18045                     break;
18046                 }
18047                 dir = e.keyCode == 37 ? -1 : 1;
18048                 
18049                 if (e.ctrlKey){
18050                     newDate = this.moveYear(this.date, dir);
18051                     newViewDate = this.moveYear(this.viewDate, dir);
18052                 } else if (e.shiftKey){
18053                     newDate = this.moveMonth(this.date, dir);
18054                     newViewDate = this.moveMonth(this.viewDate, dir);
18055                 } else {
18056                     newDate = new Date(this.date);
18057                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18058                     newViewDate = new Date(this.viewDate);
18059                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18060                 }
18061                 if (this.dateWithinRange(newDate)){
18062                     this.date = newDate;
18063                     this.viewDate = newViewDate;
18064                     this.setValue(this.formatDate(this.date));
18065 //                    this.update();
18066                     e.preventDefault();
18067                     dateChanged = true;
18068                 }
18069                 break;
18070             case 38: // up
18071             case 40: // down
18072                 if (!this.keyboardNavigation) {
18073                     break;
18074                 }
18075                 dir = e.keyCode == 38 ? -1 : 1;
18076                 if (e.ctrlKey){
18077                     newDate = this.moveYear(this.date, dir);
18078                     newViewDate = this.moveYear(this.viewDate, dir);
18079                 } else if (e.shiftKey){
18080                     newDate = this.moveMonth(this.date, dir);
18081                     newViewDate = this.moveMonth(this.viewDate, dir);
18082                 } else {
18083                     newDate = new Date(this.date);
18084                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18085                     newViewDate = new Date(this.viewDate);
18086                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18087                 }
18088                 if (this.dateWithinRange(newDate)){
18089                     this.date = newDate;
18090                     this.viewDate = newViewDate;
18091                     this.setValue(this.formatDate(this.date));
18092 //                    this.update();
18093                     e.preventDefault();
18094                     dateChanged = true;
18095                 }
18096                 break;
18097             case 13: // enter
18098                 this.setValue(this.formatDate(this.date));
18099                 this.hide();
18100                 e.preventDefault();
18101                 break;
18102             case 9: // tab
18103                 this.setValue(this.formatDate(this.date));
18104                 this.hide();
18105                 break;
18106             case 16: // shift
18107             case 17: // ctrl
18108             case 18: // alt
18109                 break;
18110             default :
18111                 this.hide();
18112                 
18113         }
18114     },
18115     
18116     
18117     onClick: function(e) 
18118     {
18119         e.stopPropagation();
18120         e.preventDefault();
18121         
18122         var target = e.getTarget();
18123         
18124         if(target.nodeName.toLowerCase() === 'i'){
18125             target = Roo.get(target).dom.parentNode;
18126         }
18127         
18128         var nodeName = target.nodeName;
18129         var className = target.className;
18130         var html = target.innerHTML;
18131         //Roo.log(nodeName);
18132         
18133         switch(nodeName.toLowerCase()) {
18134             case 'th':
18135                 switch(className) {
18136                     case 'switch':
18137                         this.showMode(1);
18138                         break;
18139                     case 'prev':
18140                     case 'next':
18141                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18142                         switch(this.viewMode){
18143                                 case 0:
18144                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18145                                         break;
18146                                 case 1:
18147                                 case 2:
18148                                         this.viewDate = this.moveYear(this.viewDate, dir);
18149                                         break;
18150                         }
18151                         this.fill();
18152                         break;
18153                     case 'today':
18154                         var date = new Date();
18155                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18156 //                        this.fill()
18157                         this.setValue(this.formatDate(this.date));
18158                         
18159                         this.hide();
18160                         break;
18161                 }
18162                 break;
18163             case 'span':
18164                 if (className.indexOf('disabled') < 0) {
18165                     this.viewDate.setUTCDate(1);
18166                     if (className.indexOf('month') > -1) {
18167                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18168                     } else {
18169                         var year = parseInt(html, 10) || 0;
18170                         this.viewDate.setUTCFullYear(year);
18171                         
18172                     }
18173                     
18174                     if(this.singleMode){
18175                         this.setValue(this.formatDate(this.viewDate));
18176                         this.hide();
18177                         return;
18178                     }
18179                     
18180                     this.showMode(-1);
18181                     this.fill();
18182                 }
18183                 break;
18184                 
18185             case 'td':
18186                 //Roo.log(className);
18187                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18188                     var day = parseInt(html, 10) || 1;
18189                     var year = this.viewDate.getUTCFullYear(),
18190                         month = this.viewDate.getUTCMonth();
18191
18192                     if (className.indexOf('old') > -1) {
18193                         if(month === 0 ){
18194                             month = 11;
18195                             year -= 1;
18196                         }else{
18197                             month -= 1;
18198                         }
18199                     } else if (className.indexOf('new') > -1) {
18200                         if (month == 11) {
18201                             month = 0;
18202                             year += 1;
18203                         } else {
18204                             month += 1;
18205                         }
18206                     }
18207                     //Roo.log([year,month,day]);
18208                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18209                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18210 //                    this.fill();
18211                     //Roo.log(this.formatDate(this.date));
18212                     this.setValue(this.formatDate(this.date));
18213                     this.hide();
18214                 }
18215                 break;
18216         }
18217     },
18218     
18219     setStartDate: function(startDate)
18220     {
18221         this.startDate = startDate || -Infinity;
18222         if (this.startDate !== -Infinity) {
18223             this.startDate = this.parseDate(this.startDate);
18224         }
18225         this.update();
18226         this.updateNavArrows();
18227     },
18228
18229     setEndDate: function(endDate)
18230     {
18231         this.endDate = endDate || Infinity;
18232         if (this.endDate !== Infinity) {
18233             this.endDate = this.parseDate(this.endDate);
18234         }
18235         this.update();
18236         this.updateNavArrows();
18237     },
18238     
18239     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18240     {
18241         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18242         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18243             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18244         }
18245         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18246             return parseInt(d, 10);
18247         });
18248         this.update();
18249         this.updateNavArrows();
18250     },
18251     
18252     updateNavArrows: function() 
18253     {
18254         if(this.singleMode){
18255             return;
18256         }
18257         
18258         var d = new Date(this.viewDate),
18259         year = d.getUTCFullYear(),
18260         month = d.getUTCMonth();
18261         
18262         Roo.each(this.picker().select('.prev', true).elements, function(v){
18263             v.show();
18264             switch (this.viewMode) {
18265                 case 0:
18266
18267                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18268                         v.hide();
18269                     }
18270                     break;
18271                 case 1:
18272                 case 2:
18273                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18274                         v.hide();
18275                     }
18276                     break;
18277             }
18278         });
18279         
18280         Roo.each(this.picker().select('.next', true).elements, function(v){
18281             v.show();
18282             switch (this.viewMode) {
18283                 case 0:
18284
18285                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18286                         v.hide();
18287                     }
18288                     break;
18289                 case 1:
18290                 case 2:
18291                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18292                         v.hide();
18293                     }
18294                     break;
18295             }
18296         })
18297     },
18298     
18299     moveMonth: function(date, dir)
18300     {
18301         if (!dir) {
18302             return date;
18303         }
18304         var new_date = new Date(date.valueOf()),
18305         day = new_date.getUTCDate(),
18306         month = new_date.getUTCMonth(),
18307         mag = Math.abs(dir),
18308         new_month, test;
18309         dir = dir > 0 ? 1 : -1;
18310         if (mag == 1){
18311             test = dir == -1
18312             // If going back one month, make sure month is not current month
18313             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18314             ? function(){
18315                 return new_date.getUTCMonth() == month;
18316             }
18317             // If going forward one month, make sure month is as expected
18318             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18319             : function(){
18320                 return new_date.getUTCMonth() != new_month;
18321             };
18322             new_month = month + dir;
18323             new_date.setUTCMonth(new_month);
18324             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18325             if (new_month < 0 || new_month > 11) {
18326                 new_month = (new_month + 12) % 12;
18327             }
18328         } else {
18329             // For magnitudes >1, move one month at a time...
18330             for (var i=0; i<mag; i++) {
18331                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18332                 new_date = this.moveMonth(new_date, dir);
18333             }
18334             // ...then reset the day, keeping it in the new month
18335             new_month = new_date.getUTCMonth();
18336             new_date.setUTCDate(day);
18337             test = function(){
18338                 return new_month != new_date.getUTCMonth();
18339             };
18340         }
18341         // Common date-resetting loop -- if date is beyond end of month, make it
18342         // end of month
18343         while (test()){
18344             new_date.setUTCDate(--day);
18345             new_date.setUTCMonth(new_month);
18346         }
18347         return new_date;
18348     },
18349
18350     moveYear: function(date, dir)
18351     {
18352         return this.moveMonth(date, dir*12);
18353     },
18354
18355     dateWithinRange: function(date)
18356     {
18357         return date >= this.startDate && date <= this.endDate;
18358     },
18359
18360     
18361     remove: function() 
18362     {
18363         this.picker().remove();
18364     },
18365     
18366     validateValue : function(value)
18367     {
18368         if(value.length < 1)  {
18369             if(this.allowBlank){
18370                 return true;
18371             }
18372             return false;
18373         }
18374         
18375         if(value.length < this.minLength){
18376             return false;
18377         }
18378         if(value.length > this.maxLength){
18379             return false;
18380         }
18381         if(this.vtype){
18382             var vt = Roo.form.VTypes;
18383             if(!vt[this.vtype](value, this)){
18384                 return false;
18385             }
18386         }
18387         if(typeof this.validator == "function"){
18388             var msg = this.validator(value);
18389             if(msg !== true){
18390                 return false;
18391             }
18392         }
18393         
18394         if(this.regex && !this.regex.test(value)){
18395             return false;
18396         }
18397         
18398         if(typeof(this.parseDate(value)) == 'undefined'){
18399             return false;
18400         }
18401         
18402         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
18403             return false;
18404         }      
18405         
18406         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
18407             return false;
18408         } 
18409         
18410         
18411         return true;
18412     }
18413    
18414 });
18415
18416 Roo.apply(Roo.bootstrap.DateField,  {
18417     
18418     head : {
18419         tag: 'thead',
18420         cn: [
18421         {
18422             tag: 'tr',
18423             cn: [
18424             {
18425                 tag: 'th',
18426                 cls: 'prev',
18427                 html: '<i class="fa fa-arrow-left"/>'
18428             },
18429             {
18430                 tag: 'th',
18431                 cls: 'switch',
18432                 colspan: '5'
18433             },
18434             {
18435                 tag: 'th',
18436                 cls: 'next',
18437                 html: '<i class="fa fa-arrow-right"/>'
18438             }
18439
18440             ]
18441         }
18442         ]
18443     },
18444     
18445     content : {
18446         tag: 'tbody',
18447         cn: [
18448         {
18449             tag: 'tr',
18450             cn: [
18451             {
18452                 tag: 'td',
18453                 colspan: '7'
18454             }
18455             ]
18456         }
18457         ]
18458     },
18459     
18460     footer : {
18461         tag: 'tfoot',
18462         cn: [
18463         {
18464             tag: 'tr',
18465             cn: [
18466             {
18467                 tag: 'th',
18468                 colspan: '7',
18469                 cls: 'today'
18470             }
18471                     
18472             ]
18473         }
18474         ]
18475     },
18476     
18477     dates:{
18478         en: {
18479             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
18480             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
18481             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
18482             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18483             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
18484             today: "Today"
18485         }
18486     },
18487     
18488     modes: [
18489     {
18490         clsName: 'days',
18491         navFnc: 'Month',
18492         navStep: 1
18493     },
18494     {
18495         clsName: 'months',
18496         navFnc: 'FullYear',
18497         navStep: 1
18498     },
18499     {
18500         clsName: 'years',
18501         navFnc: 'FullYear',
18502         navStep: 10
18503     }]
18504 });
18505
18506 Roo.apply(Roo.bootstrap.DateField,  {
18507   
18508     template : {
18509         tag: 'div',
18510         cls: 'datepicker dropdown-menu roo-dynamic',
18511         cn: [
18512         {
18513             tag: 'div',
18514             cls: 'datepicker-days',
18515             cn: [
18516             {
18517                 tag: 'table',
18518                 cls: 'table-condensed',
18519                 cn:[
18520                 Roo.bootstrap.DateField.head,
18521                 {
18522                     tag: 'tbody'
18523                 },
18524                 Roo.bootstrap.DateField.footer
18525                 ]
18526             }
18527             ]
18528         },
18529         {
18530             tag: 'div',
18531             cls: 'datepicker-months',
18532             cn: [
18533             {
18534                 tag: 'table',
18535                 cls: 'table-condensed',
18536                 cn:[
18537                 Roo.bootstrap.DateField.head,
18538                 Roo.bootstrap.DateField.content,
18539                 Roo.bootstrap.DateField.footer
18540                 ]
18541             }
18542             ]
18543         },
18544         {
18545             tag: 'div',
18546             cls: 'datepicker-years',
18547             cn: [
18548             {
18549                 tag: 'table',
18550                 cls: 'table-condensed',
18551                 cn:[
18552                 Roo.bootstrap.DateField.head,
18553                 Roo.bootstrap.DateField.content,
18554                 Roo.bootstrap.DateField.footer
18555                 ]
18556             }
18557             ]
18558         }
18559         ]
18560     }
18561 });
18562
18563  
18564
18565  /*
18566  * - LGPL
18567  *
18568  * TimeField
18569  * 
18570  */
18571
18572 /**
18573  * @class Roo.bootstrap.TimeField
18574  * @extends Roo.bootstrap.Input
18575  * Bootstrap DateField class
18576  * 
18577  * 
18578  * @constructor
18579  * Create a new TimeField
18580  * @param {Object} config The config object
18581  */
18582
18583 Roo.bootstrap.TimeField = function(config){
18584     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
18585     this.addEvents({
18586             /**
18587              * @event show
18588              * Fires when this field show.
18589              * @param {Roo.bootstrap.DateField} thisthis
18590              * @param {Mixed} date The date value
18591              */
18592             show : true,
18593             /**
18594              * @event show
18595              * Fires when this field hide.
18596              * @param {Roo.bootstrap.DateField} this
18597              * @param {Mixed} date The date value
18598              */
18599             hide : true,
18600             /**
18601              * @event select
18602              * Fires when select a date.
18603              * @param {Roo.bootstrap.DateField} this
18604              * @param {Mixed} date The date value
18605              */
18606             select : true
18607         });
18608 };
18609
18610 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
18611     
18612     /**
18613      * @cfg {String} format
18614      * The default time format string which can be overriden for localization support.  The format must be
18615      * valid according to {@link Date#parseDate} (defaults to 'H:i').
18616      */
18617     format : "H:i",
18618        
18619     onRender: function(ct, position)
18620     {
18621         
18622         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
18623                 
18624         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
18625         
18626         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18627         
18628         this.pop = this.picker().select('>.datepicker-time',true).first();
18629         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18630         
18631         this.picker().on('mousedown', this.onMousedown, this);
18632         this.picker().on('click', this.onClick, this);
18633         
18634         this.picker().addClass('datepicker-dropdown');
18635     
18636         this.fillTime();
18637         this.update();
18638             
18639         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
18640         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
18641         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
18642         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
18643         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
18644         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
18645
18646     },
18647     
18648     fireKey: function(e){
18649         if (!this.picker().isVisible()){
18650             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18651                 this.show();
18652             }
18653             return;
18654         }
18655
18656         e.preventDefault();
18657         
18658         switch(e.keyCode){
18659             case 27: // escape
18660                 this.hide();
18661                 break;
18662             case 37: // left
18663             case 39: // right
18664                 this.onTogglePeriod();
18665                 break;
18666             case 38: // up
18667                 this.onIncrementMinutes();
18668                 break;
18669             case 40: // down
18670                 this.onDecrementMinutes();
18671                 break;
18672             case 13: // enter
18673             case 9: // tab
18674                 this.setTime();
18675                 break;
18676         }
18677     },
18678     
18679     onClick: function(e) {
18680         e.stopPropagation();
18681         e.preventDefault();
18682     },
18683     
18684     picker : function()
18685     {
18686         return this.el.select('.datepicker', true).first();
18687     },
18688     
18689     fillTime: function()
18690     {    
18691         var time = this.pop.select('tbody', true).first();
18692         
18693         time.dom.innerHTML = '';
18694         
18695         time.createChild({
18696             tag: 'tr',
18697             cn: [
18698                 {
18699                     tag: 'td',
18700                     cn: [
18701                         {
18702                             tag: 'a',
18703                             href: '#',
18704                             cls: 'btn',
18705                             cn: [
18706                                 {
18707                                     tag: 'span',
18708                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
18709                                 }
18710                             ]
18711                         } 
18712                     ]
18713                 },
18714                 {
18715                     tag: 'td',
18716                     cls: 'separator'
18717                 },
18718                 {
18719                     tag: 'td',
18720                     cn: [
18721                         {
18722                             tag: 'a',
18723                             href: '#',
18724                             cls: 'btn',
18725                             cn: [
18726                                 {
18727                                     tag: 'span',
18728                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
18729                                 }
18730                             ]
18731                         }
18732                     ]
18733                 },
18734                 {
18735                     tag: 'td',
18736                     cls: 'separator'
18737                 }
18738             ]
18739         });
18740         
18741         time.createChild({
18742             tag: 'tr',
18743             cn: [
18744                 {
18745                     tag: 'td',
18746                     cn: [
18747                         {
18748                             tag: 'span',
18749                             cls: 'timepicker-hour',
18750                             html: '00'
18751                         }  
18752                     ]
18753                 },
18754                 {
18755                     tag: 'td',
18756                     cls: 'separator',
18757                     html: ':'
18758                 },
18759                 {
18760                     tag: 'td',
18761                     cn: [
18762                         {
18763                             tag: 'span',
18764                             cls: 'timepicker-minute',
18765                             html: '00'
18766                         }  
18767                     ]
18768                 },
18769                 {
18770                     tag: 'td',
18771                     cls: 'separator'
18772                 },
18773                 {
18774                     tag: 'td',
18775                     cn: [
18776                         {
18777                             tag: 'button',
18778                             type: 'button',
18779                             cls: 'btn btn-primary period',
18780                             html: 'AM'
18781                             
18782                         }
18783                     ]
18784                 }
18785             ]
18786         });
18787         
18788         time.createChild({
18789             tag: 'tr',
18790             cn: [
18791                 {
18792                     tag: 'td',
18793                     cn: [
18794                         {
18795                             tag: 'a',
18796                             href: '#',
18797                             cls: 'btn',
18798                             cn: [
18799                                 {
18800                                     tag: 'span',
18801                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
18802                                 }
18803                             ]
18804                         }
18805                     ]
18806                 },
18807                 {
18808                     tag: 'td',
18809                     cls: 'separator'
18810                 },
18811                 {
18812                     tag: 'td',
18813                     cn: [
18814                         {
18815                             tag: 'a',
18816                             href: '#',
18817                             cls: 'btn',
18818                             cn: [
18819                                 {
18820                                     tag: 'span',
18821                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
18822                                 }
18823                             ]
18824                         }
18825                     ]
18826                 },
18827                 {
18828                     tag: 'td',
18829                     cls: 'separator'
18830                 }
18831             ]
18832         });
18833         
18834     },
18835     
18836     update: function()
18837     {
18838         
18839         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18840         
18841         this.fill();
18842     },
18843     
18844     fill: function() 
18845     {
18846         var hours = this.time.getHours();
18847         var minutes = this.time.getMinutes();
18848         var period = 'AM';
18849         
18850         if(hours > 11){
18851             period = 'PM';
18852         }
18853         
18854         if(hours == 0){
18855             hours = 12;
18856         }
18857         
18858         
18859         if(hours > 12){
18860             hours = hours - 12;
18861         }
18862         
18863         if(hours < 10){
18864             hours = '0' + hours;
18865         }
18866         
18867         if(minutes < 10){
18868             minutes = '0' + minutes;
18869         }
18870         
18871         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18872         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18873         this.pop.select('button', true).first().dom.innerHTML = period;
18874         
18875     },
18876     
18877     place: function()
18878     {   
18879         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18880         
18881         var cls = ['bottom'];
18882         
18883         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18884             cls.pop();
18885             cls.push('top');
18886         }
18887         
18888         cls.push('right');
18889         
18890         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18891             cls.pop();
18892             cls.push('left');
18893         }
18894         
18895         this.picker().addClass(cls.join('-'));
18896         
18897         var _this = this;
18898         
18899         Roo.each(cls, function(c){
18900             if(c == 'bottom'){
18901                 _this.picker().setTop(_this.inputEl().getHeight());
18902                 return;
18903             }
18904             if(c == 'top'){
18905                 _this.picker().setTop(0 - _this.picker().getHeight());
18906                 return;
18907             }
18908             
18909             if(c == 'left'){
18910                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18911                 return;
18912             }
18913             if(c == 'right'){
18914                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18915                 return;
18916             }
18917         });
18918         
18919     },
18920   
18921     onFocus : function()
18922     {
18923         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
18924         this.show();
18925     },
18926     
18927     onBlur : function()
18928     {
18929         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
18930         this.hide();
18931     },
18932     
18933     show : function()
18934     {
18935         this.picker().show();
18936         this.pop.show();
18937         this.update();
18938         this.place();
18939         
18940         this.fireEvent('show', this, this.date);
18941     },
18942     
18943     hide : function()
18944     {
18945         this.picker().hide();
18946         this.pop.hide();
18947         
18948         this.fireEvent('hide', this, this.date);
18949     },
18950     
18951     setTime : function()
18952     {
18953         this.hide();
18954         this.setValue(this.time.format(this.format));
18955         
18956         this.fireEvent('select', this, this.date);
18957         
18958         
18959     },
18960     
18961     onMousedown: function(e){
18962         e.stopPropagation();
18963         e.preventDefault();
18964     },
18965     
18966     onIncrementHours: function()
18967     {
18968         Roo.log('onIncrementHours');
18969         this.time = this.time.add(Date.HOUR, 1);
18970         this.update();
18971         
18972     },
18973     
18974     onDecrementHours: function()
18975     {
18976         Roo.log('onDecrementHours');
18977         this.time = this.time.add(Date.HOUR, -1);
18978         this.update();
18979     },
18980     
18981     onIncrementMinutes: function()
18982     {
18983         Roo.log('onIncrementMinutes');
18984         this.time = this.time.add(Date.MINUTE, 1);
18985         this.update();
18986     },
18987     
18988     onDecrementMinutes: function()
18989     {
18990         Roo.log('onDecrementMinutes');
18991         this.time = this.time.add(Date.MINUTE, -1);
18992         this.update();
18993     },
18994     
18995     onTogglePeriod: function()
18996     {
18997         Roo.log('onTogglePeriod');
18998         this.time = this.time.add(Date.HOUR, 12);
18999         this.update();
19000     }
19001     
19002    
19003 });
19004
19005 Roo.apply(Roo.bootstrap.TimeField,  {
19006     
19007     content : {
19008         tag: 'tbody',
19009         cn: [
19010             {
19011                 tag: 'tr',
19012                 cn: [
19013                 {
19014                     tag: 'td',
19015                     colspan: '7'
19016                 }
19017                 ]
19018             }
19019         ]
19020     },
19021     
19022     footer : {
19023         tag: 'tfoot',
19024         cn: [
19025             {
19026                 tag: 'tr',
19027                 cn: [
19028                 {
19029                     tag: 'th',
19030                     colspan: '7',
19031                     cls: '',
19032                     cn: [
19033                         {
19034                             tag: 'button',
19035                             cls: 'btn btn-info ok',
19036                             html: 'OK'
19037                         }
19038                     ]
19039                 }
19040
19041                 ]
19042             }
19043         ]
19044     }
19045 });
19046
19047 Roo.apply(Roo.bootstrap.TimeField,  {
19048   
19049     template : {
19050         tag: 'div',
19051         cls: 'datepicker dropdown-menu',
19052         cn: [
19053             {
19054                 tag: 'div',
19055                 cls: 'datepicker-time',
19056                 cn: [
19057                 {
19058                     tag: 'table',
19059                     cls: 'table-condensed',
19060                     cn:[
19061                     Roo.bootstrap.TimeField.content,
19062                     Roo.bootstrap.TimeField.footer
19063                     ]
19064                 }
19065                 ]
19066             }
19067         ]
19068     }
19069 });
19070
19071  
19072
19073  /*
19074  * - LGPL
19075  *
19076  * MonthField
19077  * 
19078  */
19079
19080 /**
19081  * @class Roo.bootstrap.MonthField
19082  * @extends Roo.bootstrap.Input
19083  * Bootstrap MonthField class
19084  * 
19085  * @cfg {String} language default en
19086  * 
19087  * @constructor
19088  * Create a new MonthField
19089  * @param {Object} config The config object
19090  */
19091
19092 Roo.bootstrap.MonthField = function(config){
19093     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19094     
19095     this.addEvents({
19096         /**
19097          * @event show
19098          * Fires when this field show.
19099          * @param {Roo.bootstrap.MonthField} this
19100          * @param {Mixed} date The date value
19101          */
19102         show : true,
19103         /**
19104          * @event show
19105          * Fires when this field hide.
19106          * @param {Roo.bootstrap.MonthField} this
19107          * @param {Mixed} date The date value
19108          */
19109         hide : true,
19110         /**
19111          * @event select
19112          * Fires when select a date.
19113          * @param {Roo.bootstrap.MonthField} this
19114          * @param {String} oldvalue The old value
19115          * @param {String} newvalue The new value
19116          */
19117         select : true
19118     });
19119 };
19120
19121 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19122     
19123     onRender: function(ct, position)
19124     {
19125         
19126         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19127         
19128         this.language = this.language || 'en';
19129         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19130         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19131         
19132         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19133         this.isInline = false;
19134         this.isInput = true;
19135         this.component = this.el.select('.add-on', true).first() || false;
19136         this.component = (this.component && this.component.length === 0) ? false : this.component;
19137         this.hasInput = this.component && this.inputEL().length;
19138         
19139         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19140         
19141         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19142         
19143         this.picker().on('mousedown', this.onMousedown, this);
19144         this.picker().on('click', this.onClick, this);
19145         
19146         this.picker().addClass('datepicker-dropdown');
19147         
19148         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19149             v.setStyle('width', '189px');
19150         });
19151         
19152         this.fillMonths();
19153         
19154         this.update();
19155         
19156         if(this.isInline) {
19157             this.show();
19158         }
19159         
19160     },
19161     
19162     setValue: function(v, suppressEvent)
19163     {   
19164         var o = this.getValue();
19165         
19166         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19167         
19168         this.update();
19169
19170         if(suppressEvent !== true){
19171             this.fireEvent('select', this, o, v);
19172         }
19173         
19174     },
19175     
19176     getValue: function()
19177     {
19178         return this.value;
19179     },
19180     
19181     onClick: function(e) 
19182     {
19183         e.stopPropagation();
19184         e.preventDefault();
19185         
19186         var target = e.getTarget();
19187         
19188         if(target.nodeName.toLowerCase() === 'i'){
19189             target = Roo.get(target).dom.parentNode;
19190         }
19191         
19192         var nodeName = target.nodeName;
19193         var className = target.className;
19194         var html = target.innerHTML;
19195         
19196         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19197             return;
19198         }
19199         
19200         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19201         
19202         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19203         
19204         this.hide();
19205                         
19206     },
19207     
19208     picker : function()
19209     {
19210         return this.pickerEl;
19211     },
19212     
19213     fillMonths: function()
19214     {    
19215         var i = 0;
19216         var months = this.picker().select('>.datepicker-months td', true).first();
19217         
19218         months.dom.innerHTML = '';
19219         
19220         while (i < 12) {
19221             var month = {
19222                 tag: 'span',
19223                 cls: 'month',
19224                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19225             };
19226             
19227             months.createChild(month);
19228         }
19229         
19230     },
19231     
19232     update: function()
19233     {
19234         var _this = this;
19235         
19236         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19237             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19238         }
19239         
19240         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19241             e.removeClass('active');
19242             
19243             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19244                 e.addClass('active');
19245             }
19246         })
19247     },
19248     
19249     place: function()
19250     {
19251         if(this.isInline) {
19252             return;
19253         }
19254         
19255         this.picker().removeClass(['bottom', 'top']);
19256         
19257         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19258             /*
19259              * place to the top of element!
19260              *
19261              */
19262             
19263             this.picker().addClass('top');
19264             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19265             
19266             return;
19267         }
19268         
19269         this.picker().addClass('bottom');
19270         
19271         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19272     },
19273     
19274     onFocus : function()
19275     {
19276         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19277         this.show();
19278     },
19279     
19280     onBlur : function()
19281     {
19282         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19283         
19284         var d = this.inputEl().getValue();
19285         
19286         this.setValue(d);
19287                 
19288         this.hide();
19289     },
19290     
19291     show : function()
19292     {
19293         this.picker().show();
19294         this.picker().select('>.datepicker-months', true).first().show();
19295         this.update();
19296         this.place();
19297         
19298         this.fireEvent('show', this, this.date);
19299     },
19300     
19301     hide : function()
19302     {
19303         if(this.isInline) {
19304             return;
19305         }
19306         this.picker().hide();
19307         this.fireEvent('hide', this, this.date);
19308         
19309     },
19310     
19311     onMousedown: function(e)
19312     {
19313         e.stopPropagation();
19314         e.preventDefault();
19315     },
19316     
19317     keyup: function(e)
19318     {
19319         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19320         this.update();
19321     },
19322
19323     fireKey: function(e)
19324     {
19325         if (!this.picker().isVisible()){
19326             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19327                 this.show();
19328             }
19329             return;
19330         }
19331         
19332         var dir;
19333         
19334         switch(e.keyCode){
19335             case 27: // escape
19336                 this.hide();
19337                 e.preventDefault();
19338                 break;
19339             case 37: // left
19340             case 39: // right
19341                 dir = e.keyCode == 37 ? -1 : 1;
19342                 
19343                 this.vIndex = this.vIndex + dir;
19344                 
19345                 if(this.vIndex < 0){
19346                     this.vIndex = 0;
19347                 }
19348                 
19349                 if(this.vIndex > 11){
19350                     this.vIndex = 11;
19351                 }
19352                 
19353                 if(isNaN(this.vIndex)){
19354                     this.vIndex = 0;
19355                 }
19356                 
19357                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19358                 
19359                 break;
19360             case 38: // up
19361             case 40: // down
19362                 
19363                 dir = e.keyCode == 38 ? -1 : 1;
19364                 
19365                 this.vIndex = this.vIndex + dir * 4;
19366                 
19367                 if(this.vIndex < 0){
19368                     this.vIndex = 0;
19369                 }
19370                 
19371                 if(this.vIndex > 11){
19372                     this.vIndex = 11;
19373                 }
19374                 
19375                 if(isNaN(this.vIndex)){
19376                     this.vIndex = 0;
19377                 }
19378                 
19379                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19380                 break;
19381                 
19382             case 13: // enter
19383                 
19384                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19385                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19386                 }
19387                 
19388                 this.hide();
19389                 e.preventDefault();
19390                 break;
19391             case 9: // tab
19392                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19393                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19394                 }
19395                 this.hide();
19396                 break;
19397             case 16: // shift
19398             case 17: // ctrl
19399             case 18: // alt
19400                 break;
19401             default :
19402                 this.hide();
19403                 
19404         }
19405     },
19406     
19407     remove: function() 
19408     {
19409         this.picker().remove();
19410     }
19411    
19412 });
19413
19414 Roo.apply(Roo.bootstrap.MonthField,  {
19415     
19416     content : {
19417         tag: 'tbody',
19418         cn: [
19419         {
19420             tag: 'tr',
19421             cn: [
19422             {
19423                 tag: 'td',
19424                 colspan: '7'
19425             }
19426             ]
19427         }
19428         ]
19429     },
19430     
19431     dates:{
19432         en: {
19433             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19434             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19435         }
19436     }
19437 });
19438
19439 Roo.apply(Roo.bootstrap.MonthField,  {
19440   
19441     template : {
19442         tag: 'div',
19443         cls: 'datepicker dropdown-menu roo-dynamic',
19444         cn: [
19445             {
19446                 tag: 'div',
19447                 cls: 'datepicker-months',
19448                 cn: [
19449                 {
19450                     tag: 'table',
19451                     cls: 'table-condensed',
19452                     cn:[
19453                         Roo.bootstrap.DateField.content
19454                     ]
19455                 }
19456                 ]
19457             }
19458         ]
19459     }
19460 });
19461
19462  
19463
19464  
19465  /*
19466  * - LGPL
19467  *
19468  * CheckBox
19469  * 
19470  */
19471
19472 /**
19473  * @class Roo.bootstrap.CheckBox
19474  * @extends Roo.bootstrap.Input
19475  * Bootstrap CheckBox class
19476  * 
19477  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
19478  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
19479  * @cfg {String} boxLabel The text that appears beside the checkbox
19480  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
19481  * @cfg {Boolean} checked initnal the element
19482  * @cfg {Boolean} inline inline the element (default false)
19483  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
19484  * 
19485  * @constructor
19486  * Create a new CheckBox
19487  * @param {Object} config The config object
19488  */
19489
19490 Roo.bootstrap.CheckBox = function(config){
19491     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
19492    
19493     this.addEvents({
19494         /**
19495         * @event check
19496         * Fires when the element is checked or unchecked.
19497         * @param {Roo.bootstrap.CheckBox} this This input
19498         * @param {Boolean} checked The new checked value
19499         */
19500        check : true
19501     });
19502     
19503 };
19504
19505 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
19506   
19507     inputType: 'checkbox',
19508     inputValue: 1,
19509     valueOff: 0,
19510     boxLabel: false,
19511     checked: false,
19512     weight : false,
19513     inline: false,
19514     
19515     getAutoCreate : function()
19516     {
19517         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19518         
19519         var id = Roo.id();
19520         
19521         var cfg = {};
19522         
19523         cfg.cls = 'form-group ' + this.inputType; //input-group
19524         
19525         if(this.inline){
19526             cfg.cls += ' ' + this.inputType + '-inline';
19527         }
19528         
19529         var input =  {
19530             tag: 'input',
19531             id : id,
19532             type : this.inputType,
19533             value : this.inputValue,
19534             cls : 'roo-' + this.inputType, //'form-box',
19535             placeholder : this.placeholder || ''
19536             
19537         };
19538         
19539         if(this.inputType != 'radio'){
19540             var hidden =  {
19541                 tag: 'input',
19542                 type : 'hidden',
19543                 cls : 'roo-hidden-value',
19544                 value : this.checked ? this.valueOff : this.inputValue
19545             };
19546         }
19547         
19548             
19549         if (this.weight) { // Validity check?
19550             cfg.cls += " " + this.inputType + "-" + this.weight;
19551         }
19552         
19553         if (this.disabled) {
19554             input.disabled=true;
19555         }
19556         
19557         if(this.checked){
19558             input.checked = this.checked;
19559             
19560         }
19561         
19562         
19563         if (this.name) {
19564             
19565             input.name = this.name;
19566             
19567             if(this.inputType != 'radio'){
19568                 hidden.name = this.name;
19569                 input.name = '_hidden_' + this.name;
19570             }
19571         }
19572         
19573         if (this.size) {
19574             input.cls += ' input-' + this.size;
19575         }
19576         
19577         var settings=this;
19578         
19579         ['xs','sm','md','lg'].map(function(size){
19580             if (settings[size]) {
19581                 cfg.cls += ' col-' + size + '-' + settings[size];
19582             }
19583         });
19584         
19585         var inputblock = input;
19586          
19587         if (this.before || this.after) {
19588             
19589             inputblock = {
19590                 cls : 'input-group',
19591                 cn :  [] 
19592             };
19593             
19594             if (this.before) {
19595                 inputblock.cn.push({
19596                     tag :'span',
19597                     cls : 'input-group-addon',
19598                     html : this.before
19599                 });
19600             }
19601             
19602             inputblock.cn.push(input);
19603             
19604             if(this.inputType != 'radio'){
19605                 inputblock.cn.push(hidden);
19606             }
19607             
19608             if (this.after) {
19609                 inputblock.cn.push({
19610                     tag :'span',
19611                     cls : 'input-group-addon',
19612                     html : this.after
19613                 });
19614             }
19615             
19616         }
19617         
19618         if (align ==='left' && this.fieldLabel.length) {
19619 //                Roo.log("left and has label");
19620                 cfg.cn = [
19621                     
19622                     {
19623                         tag: 'label',
19624                         'for' :  id,
19625                         cls : 'control-label col-md-' + this.labelWidth,
19626                         html : this.fieldLabel
19627                         
19628                     },
19629                     {
19630                         cls : "col-md-" + (12 - this.labelWidth), 
19631                         cn: [
19632                             inputblock
19633                         ]
19634                     }
19635                     
19636                 ];
19637         } else if ( this.fieldLabel.length) {
19638 //                Roo.log(" label");
19639                 cfg.cn = [
19640                    
19641                     {
19642                         tag: this.boxLabel ? 'span' : 'label',
19643                         'for': id,
19644                         cls: 'control-label box-input-label',
19645                         //cls : 'input-group-addon',
19646                         html : this.fieldLabel
19647                         
19648                     },
19649                     
19650                     inputblock
19651                     
19652                 ];
19653
19654         } else {
19655             
19656 //                Roo.log(" no label && no align");
19657                 cfg.cn = [  inputblock ] ;
19658                 
19659                 
19660         }
19661         
19662         if(this.boxLabel){
19663              var boxLabelCfg = {
19664                 tag: 'label',
19665                 //'for': id, // box label is handled by onclick - so no for...
19666                 cls: 'box-label',
19667                 html: this.boxLabel
19668             };
19669             
19670             if(this.tooltip){
19671                 boxLabelCfg.tooltip = this.tooltip;
19672             }
19673              
19674             cfg.cn.push(boxLabelCfg);
19675         }
19676         
19677         if(this.inputType != 'radio'){
19678             cfg.cn.push(hidden);
19679         }
19680         
19681         return cfg;
19682         
19683     },
19684     
19685     /**
19686      * return the real input element.
19687      */
19688     inputEl: function ()
19689     {
19690         return this.el.select('input.roo-' + this.inputType,true).first();
19691     },
19692     hiddenEl: function ()
19693     {
19694         return this.el.select('input.roo-hidden-value',true).first();
19695     },
19696     
19697     labelEl: function()
19698     {
19699         return this.el.select('label.control-label',true).first();
19700     },
19701     /* depricated... */
19702     
19703     label: function()
19704     {
19705         return this.labelEl();
19706     },
19707     
19708     boxLabelEl: function()
19709     {
19710         return this.el.select('label.box-label',true).first();
19711     },
19712     
19713     initEvents : function()
19714     {
19715 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19716         
19717         this.inputEl().on('click', this.onClick,  this);
19718         
19719         if (this.boxLabel) { 
19720             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
19721         }
19722         
19723         this.startValue = this.getValue();
19724         
19725         if(this.groupId){
19726             Roo.bootstrap.CheckBox.register(this);
19727         }
19728     },
19729     
19730     onClick : function()
19731     {   
19732         this.setChecked(!this.checked);
19733     },
19734     
19735     setChecked : function(state,suppressEvent)
19736     {
19737         this.startValue = this.getValue();
19738         
19739         if(this.inputType == 'radio'){
19740             
19741             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19742                 e.dom.checked = false;
19743             });
19744             
19745             this.inputEl().dom.checked = true;
19746             
19747             this.inputEl().dom.value = this.inputValue;
19748             
19749             if(suppressEvent !== true){
19750                 this.fireEvent('check', this, true);
19751             }
19752             
19753             this.validate();
19754             
19755             return;
19756         }
19757         
19758         this.checked = state;
19759         
19760         this.inputEl().dom.checked = state;
19761         
19762         
19763         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
19764         
19765         if(suppressEvent !== true){
19766             this.fireEvent('check', this, state);
19767         }
19768         
19769         this.validate();
19770     },
19771     
19772     getValue : function()
19773     {
19774         if(this.inputType == 'radio'){
19775             return this.getGroupValue();
19776         }
19777         
19778         return this.hiddenEl().dom.value;
19779         
19780     },
19781     
19782     getGroupValue : function()
19783     {
19784         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19785             return '';
19786         }
19787         
19788         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19789     },
19790     
19791     setValue : function(v,suppressEvent)
19792     {
19793         if(this.inputType == 'radio'){
19794             this.setGroupValue(v, suppressEvent);
19795             return;
19796         }
19797         
19798         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19799         
19800         this.validate();
19801     },
19802     
19803     setGroupValue : function(v, suppressEvent)
19804     {
19805         this.startValue = this.getValue();
19806         
19807         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19808             e.dom.checked = false;
19809             
19810             if(e.dom.value == v){
19811                 e.dom.checked = true;
19812             }
19813         });
19814         
19815         if(suppressEvent !== true){
19816             this.fireEvent('check', this, true);
19817         }
19818
19819         this.validate();
19820         
19821         return;
19822     },
19823     
19824     validate : function()
19825     {
19826         if(
19827                 this.disabled || 
19828                 (this.inputType == 'radio' && this.validateRadio()) ||
19829                 (this.inputType == 'checkbox' && this.validateCheckbox())
19830         ){
19831             this.markValid();
19832             return true;
19833         }
19834         
19835         this.markInvalid();
19836         return false;
19837     },
19838     
19839     validateRadio : function()
19840     {
19841         if(this.allowBlank){
19842             return true;
19843         }
19844         
19845         var valid = false;
19846         
19847         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19848             if(!e.dom.checked){
19849                 return;
19850             }
19851             
19852             valid = true;
19853             
19854             return false;
19855         });
19856         
19857         return valid;
19858     },
19859     
19860     validateCheckbox : function()
19861     {
19862         if(!this.groupId){
19863             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19864         }
19865         
19866         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19867         
19868         if(!group){
19869             return false;
19870         }
19871         
19872         var r = false;
19873         
19874         for(var i in group){
19875             if(r){
19876                 break;
19877             }
19878             
19879             r = (group[i].getValue() == group[i].inputValue) ? true : false;
19880         }
19881         
19882         return r;
19883     },
19884     
19885     /**
19886      * Mark this field as valid
19887      */
19888     markValid : function()
19889     {
19890         if(this.allowBlank){
19891             return;
19892         }
19893         
19894         var _this = this;
19895         
19896         this.fireEvent('valid', this);
19897         
19898         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19899         
19900         if(this.groupId){
19901             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19902         }
19903         
19904         if(label){
19905             label.markValid();
19906         }
19907         
19908         if(this.inputType == 'radio'){
19909             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19910                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19911                 e.findParent('.form-group', false, true).addClass(_this.validClass);
19912             });
19913             
19914             return;
19915         }
19916         
19917         if(!this.groupId){
19918             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19919             this.el.findParent('.form-group', false, true).addClass(this.validClass);
19920             return;
19921         }
19922         
19923         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19924             
19925         if(!group){
19926             return;
19927         }
19928         
19929         for(var i in group){
19930             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19931             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
19932         }
19933     },
19934     
19935      /**
19936      * Mark this field as invalid
19937      * @param {String} msg The validation message
19938      */
19939     markInvalid : function(msg)
19940     {
19941         if(this.allowBlank){
19942             return;
19943         }
19944         
19945         var _this = this;
19946         
19947         this.fireEvent('invalid', this, msg);
19948         
19949         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19950         
19951         if(this.groupId){
19952             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19953         }
19954         
19955         if(label){
19956             label.markInvalid();
19957         }
19958             
19959         if(this.inputType == 'radio'){
19960             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19961                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19962                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
19963             });
19964             
19965             return;
19966         }
19967         
19968         if(!this.groupId){
19969             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19970             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
19971             return;
19972         }
19973         
19974         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19975         
19976         if(!group){
19977             return;
19978         }
19979         
19980         for(var i in group){
19981             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19982             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
19983         }
19984         
19985     },
19986     
19987     disable : function()
19988     {
19989         if(this.inputType != 'radio'){
19990             Roo.bootstrap.CheckBox.superclass.disable.call(this);
19991             return;
19992         }
19993         
19994         var _this = this;
19995         
19996         if(this.rendered){
19997             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19998                 _this.getActionEl().addClass(this.disabledClass);
19999                 e.dom.disabled = true;
20000             });
20001         }
20002         
20003         this.disabled = true;
20004         this.fireEvent("disable", this);
20005         return this;
20006     },
20007
20008     enable : function()
20009     {
20010         if(this.inputType != 'radio'){
20011             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20012             return;
20013         }
20014         
20015         var _this = this;
20016         
20017         if(this.rendered){
20018             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20019                 _this.getActionEl().removeClass(this.disabledClass);
20020                 e.dom.disabled = false;
20021             });
20022         }
20023         
20024         this.disabled = false;
20025         this.fireEvent("enable", this);
20026         return this;
20027     }
20028
20029 });
20030
20031 Roo.apply(Roo.bootstrap.CheckBox, {
20032     
20033     groups: {},
20034     
20035      /**
20036     * register a CheckBox Group
20037     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20038     */
20039     register : function(checkbox)
20040     {
20041         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20042             this.groups[checkbox.groupId] = {};
20043         }
20044         
20045         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20046             return;
20047         }
20048         
20049         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20050         
20051     },
20052     /**
20053     * fetch a CheckBox Group based on the group ID
20054     * @param {string} the group ID
20055     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20056     */
20057     get: function(groupId) {
20058         if (typeof(this.groups[groupId]) == 'undefined') {
20059             return false;
20060         }
20061         
20062         return this.groups[groupId] ;
20063     }
20064     
20065     
20066 });
20067 /*
20068  * - LGPL
20069  *
20070  * Radio
20071  *
20072  *
20073  * not inline
20074  *<div class="radio">
20075   <label>
20076     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
20077     Option one is this and that&mdash;be sure to include why it's great
20078   </label>
20079 </div>
20080  *
20081  *
20082  *inline
20083  *<span>
20084  *<label class="radio-inline">fieldLabel</label>
20085  *<label class="radio-inline">
20086   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
20087 </label>
20088 <span>
20089  *
20090  *
20091  */
20092
20093 /**
20094  * @class Roo.bootstrap.Radio
20095  * @extends Roo.bootstrap.CheckBox
20096  * Bootstrap Radio class
20097
20098  * @constructor
20099  * Create a new Radio
20100  * @param {Object} config The config object
20101  */
20102
20103 Roo.bootstrap.Radio = function(config){
20104     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20105
20106 };
20107
20108 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
20109
20110     inputType: 'radio',
20111     inputValue: '',
20112     valueOff: '',
20113
20114     getAutoCreate : function()
20115     {
20116         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20117         align = align || 'left'; // default...
20118
20119
20120
20121         var id = Roo.id();
20122
20123         var cfg = {
20124                 tag : this.inline ? 'span' : 'div',
20125                 cls : 'form-group',
20126                 cn : []
20127         };
20128
20129         var inline = this.inline ? ' radio-inline' : '';
20130
20131         var lbl = {
20132                 tag: 'label' ,
20133                 // does not need for, as we wrap the input with it..
20134                 'for' : id,
20135                 cls : 'control-label box-label' + inline,
20136                 cn : []
20137         };
20138         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
20139
20140         var fieldLabel = {
20141             tag: 'label' ,
20142             //cls : 'control-label' + inline,
20143             html : this.fieldLabel,
20144             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
20145         };
20146
20147         var input =  {
20148             tag: 'input',
20149             id : id,
20150             type : this.inputType,
20151             //value : (!this.checked) ? this.valueOff : this.inputValue,
20152             value : this.inputValue,
20153             cls : 'roo-radio',
20154             placeholder : this.placeholder || '' // ?? needed????
20155
20156         };
20157         if (this.weight) { // Validity check?
20158             input.cls += " radio-" + this.weight;
20159         }
20160         if (this.disabled) {
20161             input.disabled=true;
20162         }
20163
20164         if(this.checked){
20165             input.checked = this.checked;
20166         }
20167
20168         if (this.name) {
20169             input.name = this.name;
20170         }
20171
20172         if (this.size) {
20173             input.cls += ' input-' + this.size;
20174         }
20175
20176         //?? can span's inline have a width??
20177
20178         var settings=this;
20179         ['xs','sm','md','lg'].map(function(size){
20180             if (settings[size]) {
20181                 cfg.cls += ' col-' + size + '-' + settings[size];
20182             }
20183         });
20184
20185         var inputblock = input;
20186
20187         if (this.before || this.after) {
20188
20189             inputblock = {
20190                 cls : 'input-group',
20191                 tag : 'span',
20192                 cn :  []
20193             };
20194             if (this.before) {
20195                 inputblock.cn.push({
20196                     tag :'span',
20197                     cls : 'input-group-addon',
20198                     html : this.before
20199                 });
20200             }
20201             inputblock.cn.push(input);
20202             if (this.after) {
20203                 inputblock.cn.push({
20204                     tag :'span',
20205                     cls : 'input-group-addon',
20206                     html : this.after
20207                 });
20208             }
20209
20210         };
20211
20212
20213         if (this.fieldLabel && this.fieldLabel.length) {
20214             cfg.cn.push(fieldLabel);
20215         }
20216
20217         // normal bootstrap puts the input inside the label.
20218         // however with our styled version - it has to go after the input.
20219
20220         //lbl.cn.push(inputblock);
20221
20222         var lblwrap =  {
20223             tag: 'span',
20224             cls: 'radio' + inline,
20225             cn: [
20226                 inputblock,
20227                 lbl
20228             ]
20229         };
20230
20231         cfg.cn.push( lblwrap);
20232
20233         if(this.boxLabel){
20234             lbl.cn.push({
20235                 tag: 'span',
20236                 html: this.boxLabel
20237             })
20238         }
20239
20240
20241         return cfg;
20242
20243     },
20244
20245     initEvents : function()
20246     {
20247 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20248
20249         this.inputEl().on('click', this.onClick,  this);
20250         if (this.boxLabel) {
20251             //Roo.log('find label');
20252             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
20253         }
20254
20255     },
20256
20257     inputEl: function ()
20258     {
20259         return this.el.select('input.roo-radio',true).first();
20260     },
20261     onClick : function()
20262     {
20263         Roo.log("click");
20264         this.setChecked(true);
20265     },
20266
20267     setChecked : function(state,suppressEvent)
20268     {
20269         if(state){
20270             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
20271                 v.dom.checked = false;
20272             });
20273         }
20274         Roo.log(this.inputEl().dom);
20275         this.checked = state;
20276         this.inputEl().dom.checked = state;
20277
20278         if(suppressEvent !== true){
20279             this.fireEvent('check', this, state);
20280         }
20281
20282         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
20283
20284     },
20285
20286     getGroupValue : function()
20287     {
20288         var value = '';
20289         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
20290             if(v.dom.checked == true){
20291                 value = v.dom.value;
20292             }
20293         });
20294
20295         return value;
20296     },
20297
20298     /**
20299      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
20300      * @return {Mixed} value The field value
20301      */
20302     getValue : function(){
20303         return this.getGroupValue();
20304     }
20305
20306 });
20307 //<script type="text/javascript">
20308
20309 /*
20310  * Based  Ext JS Library 1.1.1
20311  * Copyright(c) 2006-2007, Ext JS, LLC.
20312  * LGPL
20313  *
20314  */
20315  
20316 /**
20317  * @class Roo.HtmlEditorCore
20318  * @extends Roo.Component
20319  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20320  *
20321  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20322  */
20323
20324 Roo.HtmlEditorCore = function(config){
20325     
20326     
20327     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20328     
20329     
20330     this.addEvents({
20331         /**
20332          * @event initialize
20333          * Fires when the editor is fully initialized (including the iframe)
20334          * @param {Roo.HtmlEditorCore} this
20335          */
20336         initialize: true,
20337         /**
20338          * @event activate
20339          * Fires when the editor is first receives the focus. Any insertion must wait
20340          * until after this event.
20341          * @param {Roo.HtmlEditorCore} this
20342          */
20343         activate: true,
20344          /**
20345          * @event beforesync
20346          * Fires before the textarea is updated with content from the editor iframe. Return false
20347          * to cancel the sync.
20348          * @param {Roo.HtmlEditorCore} this
20349          * @param {String} html
20350          */
20351         beforesync: true,
20352          /**
20353          * @event beforepush
20354          * Fires before the iframe editor is updated with content from the textarea. Return false
20355          * to cancel the push.
20356          * @param {Roo.HtmlEditorCore} this
20357          * @param {String} html
20358          */
20359         beforepush: true,
20360          /**
20361          * @event sync
20362          * Fires when the textarea is updated with content from the editor iframe.
20363          * @param {Roo.HtmlEditorCore} this
20364          * @param {String} html
20365          */
20366         sync: true,
20367          /**
20368          * @event push
20369          * Fires when the iframe editor is updated with content from the textarea.
20370          * @param {Roo.HtmlEditorCore} this
20371          * @param {String} html
20372          */
20373         push: true,
20374         
20375         /**
20376          * @event editorevent
20377          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20378          * @param {Roo.HtmlEditorCore} this
20379          */
20380         editorevent: true
20381         
20382     });
20383     
20384     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20385     
20386     // defaults : white / black...
20387     this.applyBlacklists();
20388     
20389     
20390     
20391 };
20392
20393
20394 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20395
20396
20397      /**
20398      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20399      */
20400     
20401     owner : false,
20402     
20403      /**
20404      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20405      *                        Roo.resizable.
20406      */
20407     resizable : false,
20408      /**
20409      * @cfg {Number} height (in pixels)
20410      */   
20411     height: 300,
20412    /**
20413      * @cfg {Number} width (in pixels)
20414      */   
20415     width: 500,
20416     
20417     /**
20418      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20419      * 
20420      */
20421     stylesheets: false,
20422     
20423     // id of frame..
20424     frameId: false,
20425     
20426     // private properties
20427     validationEvent : false,
20428     deferHeight: true,
20429     initialized : false,
20430     activated : false,
20431     sourceEditMode : false,
20432     onFocus : Roo.emptyFn,
20433     iframePad:3,
20434     hideMode:'offsets',
20435     
20436     clearUp: true,
20437     
20438     // blacklist + whitelisted elements..
20439     black: false,
20440     white: false,
20441      
20442     
20443
20444     /**
20445      * Protected method that will not generally be called directly. It
20446      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20447      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20448      */
20449     getDocMarkup : function(){
20450         // body styles..
20451         var st = '';
20452         
20453         // inherit styels from page...?? 
20454         if (this.stylesheets === false) {
20455             
20456             Roo.get(document.head).select('style').each(function(node) {
20457                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20458             });
20459             
20460             Roo.get(document.head).select('link').each(function(node) { 
20461                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20462             });
20463             
20464         } else if (!this.stylesheets.length) {
20465                 // simple..
20466                 st = '<style type="text/css">' +
20467                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20468                    '</style>';
20469         } else { 
20470             
20471         }
20472         
20473         st +=  '<style type="text/css">' +
20474             'IMG { cursor: pointer } ' +
20475         '</style>';
20476
20477         
20478         return '<html><head>' + st  +
20479             //<style type="text/css">' +
20480             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20481             //'</style>' +
20482             ' </head><body class="roo-htmleditor-body"></body></html>';
20483     },
20484
20485     // private
20486     onRender : function(ct, position)
20487     {
20488         var _t = this;
20489         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20490         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20491         
20492         
20493         this.el.dom.style.border = '0 none';
20494         this.el.dom.setAttribute('tabIndex', -1);
20495         this.el.addClass('x-hidden hide');
20496         
20497         
20498         
20499         if(Roo.isIE){ // fix IE 1px bogus margin
20500             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20501         }
20502        
20503         
20504         this.frameId = Roo.id();
20505         
20506          
20507         
20508         var iframe = this.owner.wrap.createChild({
20509             tag: 'iframe',
20510             cls: 'form-control', // bootstrap..
20511             id: this.frameId,
20512             name: this.frameId,
20513             frameBorder : 'no',
20514             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20515         }, this.el
20516         );
20517         
20518         
20519         this.iframe = iframe.dom;
20520
20521          this.assignDocWin();
20522         
20523         this.doc.designMode = 'on';
20524        
20525         this.doc.open();
20526         this.doc.write(this.getDocMarkup());
20527         this.doc.close();
20528
20529         
20530         var task = { // must defer to wait for browser to be ready
20531             run : function(){
20532                 //console.log("run task?" + this.doc.readyState);
20533                 this.assignDocWin();
20534                 if(this.doc.body || this.doc.readyState == 'complete'){
20535                     try {
20536                         this.doc.designMode="on";
20537                     } catch (e) {
20538                         return;
20539                     }
20540                     Roo.TaskMgr.stop(task);
20541                     this.initEditor.defer(10, this);
20542                 }
20543             },
20544             interval : 10,
20545             duration: 10000,
20546             scope: this
20547         };
20548         Roo.TaskMgr.start(task);
20549
20550     },
20551
20552     // private
20553     onResize : function(w, h)
20554     {
20555          Roo.log('resize: ' +w + ',' + h );
20556         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20557         if(!this.iframe){
20558             return;
20559         }
20560         if(typeof w == 'number'){
20561             
20562             this.iframe.style.width = w + 'px';
20563         }
20564         if(typeof h == 'number'){
20565             
20566             this.iframe.style.height = h + 'px';
20567             if(this.doc){
20568                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20569             }
20570         }
20571         
20572     },
20573
20574     /**
20575      * Toggles the editor between standard and source edit mode.
20576      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20577      */
20578     toggleSourceEdit : function(sourceEditMode){
20579         
20580         this.sourceEditMode = sourceEditMode === true;
20581         
20582         if(this.sourceEditMode){
20583  
20584             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20585             
20586         }else{
20587             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20588             //this.iframe.className = '';
20589             this.deferFocus();
20590         }
20591         //this.setSize(this.owner.wrap.getSize());
20592         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20593     },
20594
20595     
20596   
20597
20598     /**
20599      * Protected method that will not generally be called directly. If you need/want
20600      * custom HTML cleanup, this is the method you should override.
20601      * @param {String} html The HTML to be cleaned
20602      * return {String} The cleaned HTML
20603      */
20604     cleanHtml : function(html){
20605         html = String(html);
20606         if(html.length > 5){
20607             if(Roo.isSafari){ // strip safari nonsense
20608                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20609             }
20610         }
20611         if(html == '&nbsp;'){
20612             html = '';
20613         }
20614         return html;
20615     },
20616
20617     /**
20618      * HTML Editor -> Textarea
20619      * Protected method that will not generally be called directly. Syncs the contents
20620      * of the editor iframe with the textarea.
20621      */
20622     syncValue : function(){
20623         if(this.initialized){
20624             var bd = (this.doc.body || this.doc.documentElement);
20625             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20626             var html = bd.innerHTML;
20627             if(Roo.isSafari){
20628                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20629                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20630                 if(m && m[1]){
20631                     html = '<div style="'+m[0]+'">' + html + '</div>';
20632                 }
20633             }
20634             html = this.cleanHtml(html);
20635             // fix up the special chars.. normaly like back quotes in word...
20636             // however we do not want to do this with chinese..
20637             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20638                 var cc = b.charCodeAt();
20639                 if (
20640                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20641                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20642                     (cc >= 0xf900 && cc < 0xfb00 )
20643                 ) {
20644                         return b;
20645                 }
20646                 return "&#"+cc+";" 
20647             });
20648             if(this.owner.fireEvent('beforesync', this, html) !== false){
20649                 this.el.dom.value = html;
20650                 this.owner.fireEvent('sync', this, html);
20651             }
20652         }
20653     },
20654
20655     /**
20656      * Protected method that will not generally be called directly. Pushes the value of the textarea
20657      * into the iframe editor.
20658      */
20659     pushValue : function(){
20660         if(this.initialized){
20661             var v = this.el.dom.value.trim();
20662             
20663 //            if(v.length < 1){
20664 //                v = '&#160;';
20665 //            }
20666             
20667             if(this.owner.fireEvent('beforepush', this, v) !== false){
20668                 var d = (this.doc.body || this.doc.documentElement);
20669                 d.innerHTML = v;
20670                 this.cleanUpPaste();
20671                 this.el.dom.value = d.innerHTML;
20672                 this.owner.fireEvent('push', this, v);
20673             }
20674         }
20675     },
20676
20677     // private
20678     deferFocus : function(){
20679         this.focus.defer(10, this);
20680     },
20681
20682     // doc'ed in Field
20683     focus : function(){
20684         if(this.win && !this.sourceEditMode){
20685             this.win.focus();
20686         }else{
20687             this.el.focus();
20688         }
20689     },
20690     
20691     assignDocWin: function()
20692     {
20693         var iframe = this.iframe;
20694         
20695          if(Roo.isIE){
20696             this.doc = iframe.contentWindow.document;
20697             this.win = iframe.contentWindow;
20698         } else {
20699 //            if (!Roo.get(this.frameId)) {
20700 //                return;
20701 //            }
20702 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20703 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20704             
20705             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20706                 return;
20707             }
20708             
20709             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20710             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20711         }
20712     },
20713     
20714     // private
20715     initEditor : function(){
20716         //console.log("INIT EDITOR");
20717         this.assignDocWin();
20718         
20719         
20720         
20721         this.doc.designMode="on";
20722         this.doc.open();
20723         this.doc.write(this.getDocMarkup());
20724         this.doc.close();
20725         
20726         var dbody = (this.doc.body || this.doc.documentElement);
20727         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20728         // this copies styles from the containing element into thsi one..
20729         // not sure why we need all of this..
20730         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20731         
20732         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20733         //ss['background-attachment'] = 'fixed'; // w3c
20734         dbody.bgProperties = 'fixed'; // ie
20735         //Roo.DomHelper.applyStyles(dbody, ss);
20736         Roo.EventManager.on(this.doc, {
20737             //'mousedown': this.onEditorEvent,
20738             'mouseup': this.onEditorEvent,
20739             'dblclick': this.onEditorEvent,
20740             'click': this.onEditorEvent,
20741             'keyup': this.onEditorEvent,
20742             buffer:100,
20743             scope: this
20744         });
20745         if(Roo.isGecko){
20746             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20747         }
20748         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20749             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20750         }
20751         this.initialized = true;
20752
20753         this.owner.fireEvent('initialize', this);
20754         this.pushValue();
20755     },
20756
20757     // private
20758     onDestroy : function(){
20759         
20760         
20761         
20762         if(this.rendered){
20763             
20764             //for (var i =0; i < this.toolbars.length;i++) {
20765             //    // fixme - ask toolbars for heights?
20766             //    this.toolbars[i].onDestroy();
20767            // }
20768             
20769             //this.wrap.dom.innerHTML = '';
20770             //this.wrap.remove();
20771         }
20772     },
20773
20774     // private
20775     onFirstFocus : function(){
20776         
20777         this.assignDocWin();
20778         
20779         
20780         this.activated = true;
20781          
20782     
20783         if(Roo.isGecko){ // prevent silly gecko errors
20784             this.win.focus();
20785             var s = this.win.getSelection();
20786             if(!s.focusNode || s.focusNode.nodeType != 3){
20787                 var r = s.getRangeAt(0);
20788                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20789                 r.collapse(true);
20790                 this.deferFocus();
20791             }
20792             try{
20793                 this.execCmd('useCSS', true);
20794                 this.execCmd('styleWithCSS', false);
20795             }catch(e){}
20796         }
20797         this.owner.fireEvent('activate', this);
20798     },
20799
20800     // private
20801     adjustFont: function(btn){
20802         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20803         //if(Roo.isSafari){ // safari
20804         //    adjust *= 2;
20805        // }
20806         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20807         if(Roo.isSafari){ // safari
20808             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20809             v =  (v < 10) ? 10 : v;
20810             v =  (v > 48) ? 48 : v;
20811             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20812             
20813         }
20814         
20815         
20816         v = Math.max(1, v+adjust);
20817         
20818         this.execCmd('FontSize', v  );
20819     },
20820
20821     onEditorEvent : function(e)
20822     {
20823         this.owner.fireEvent('editorevent', this, e);
20824       //  this.updateToolbar();
20825         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20826     },
20827
20828     insertTag : function(tg)
20829     {
20830         // could be a bit smarter... -> wrap the current selected tRoo..
20831         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20832             
20833             range = this.createRange(this.getSelection());
20834             var wrappingNode = this.doc.createElement(tg.toLowerCase());
20835             wrappingNode.appendChild(range.extractContents());
20836             range.insertNode(wrappingNode);
20837
20838             return;
20839             
20840             
20841             
20842         }
20843         this.execCmd("formatblock",   tg);
20844         
20845     },
20846     
20847     insertText : function(txt)
20848     {
20849         
20850         
20851         var range = this.createRange();
20852         range.deleteContents();
20853                //alert(Sender.getAttribute('label'));
20854                
20855         range.insertNode(this.doc.createTextNode(txt));
20856     } ,
20857     
20858      
20859
20860     /**
20861      * Executes a Midas editor command on the editor document and performs necessary focus and
20862      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20863      * @param {String} cmd The Midas command
20864      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20865      */
20866     relayCmd : function(cmd, value){
20867         this.win.focus();
20868         this.execCmd(cmd, value);
20869         this.owner.fireEvent('editorevent', this);
20870         //this.updateToolbar();
20871         this.owner.deferFocus();
20872     },
20873
20874     /**
20875      * Executes a Midas editor command directly on the editor document.
20876      * For visual commands, you should use {@link #relayCmd} instead.
20877      * <b>This should only be called after the editor is initialized.</b>
20878      * @param {String} cmd The Midas command
20879      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20880      */
20881     execCmd : function(cmd, value){
20882         this.doc.execCommand(cmd, false, value === undefined ? null : value);
20883         this.syncValue();
20884     },
20885  
20886  
20887    
20888     /**
20889      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20890      * to insert tRoo.
20891      * @param {String} text | dom node.. 
20892      */
20893     insertAtCursor : function(text)
20894     {
20895         
20896         
20897         
20898         if(!this.activated){
20899             return;
20900         }
20901         /*
20902         if(Roo.isIE){
20903             this.win.focus();
20904             var r = this.doc.selection.createRange();
20905             if(r){
20906                 r.collapse(true);
20907                 r.pasteHTML(text);
20908                 this.syncValue();
20909                 this.deferFocus();
20910             
20911             }
20912             return;
20913         }
20914         */
20915         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20916             this.win.focus();
20917             
20918             
20919             // from jquery ui (MIT licenced)
20920             var range, node;
20921             var win = this.win;
20922             
20923             if (win.getSelection && win.getSelection().getRangeAt) {
20924                 range = win.getSelection().getRangeAt(0);
20925                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20926                 range.insertNode(node);
20927             } else if (win.document.selection && win.document.selection.createRange) {
20928                 // no firefox support
20929                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20930                 win.document.selection.createRange().pasteHTML(txt);
20931             } else {
20932                 // no firefox support
20933                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20934                 this.execCmd('InsertHTML', txt);
20935             } 
20936             
20937             this.syncValue();
20938             
20939             this.deferFocus();
20940         }
20941     },
20942  // private
20943     mozKeyPress : function(e){
20944         if(e.ctrlKey){
20945             var c = e.getCharCode(), cmd;
20946           
20947             if(c > 0){
20948                 c = String.fromCharCode(c).toLowerCase();
20949                 switch(c){
20950                     case 'b':
20951                         cmd = 'bold';
20952                         break;
20953                     case 'i':
20954                         cmd = 'italic';
20955                         break;
20956                     
20957                     case 'u':
20958                         cmd = 'underline';
20959                         break;
20960                     
20961                     case 'v':
20962                         this.cleanUpPaste.defer(100, this);
20963                         return;
20964                         
20965                 }
20966                 if(cmd){
20967                     this.win.focus();
20968                     this.execCmd(cmd);
20969                     this.deferFocus();
20970                     e.preventDefault();
20971                 }
20972                 
20973             }
20974         }
20975     },
20976
20977     // private
20978     fixKeys : function(){ // load time branching for fastest keydown performance
20979         if(Roo.isIE){
20980             return function(e){
20981                 var k = e.getKey(), r;
20982                 if(k == e.TAB){
20983                     e.stopEvent();
20984                     r = this.doc.selection.createRange();
20985                     if(r){
20986                         r.collapse(true);
20987                         r.pasteHTML('&#160;&#160;&#160;&#160;');
20988                         this.deferFocus();
20989                     }
20990                     return;
20991                 }
20992                 
20993                 if(k == e.ENTER){
20994                     r = this.doc.selection.createRange();
20995                     if(r){
20996                         var target = r.parentElement();
20997                         if(!target || target.tagName.toLowerCase() != 'li'){
20998                             e.stopEvent();
20999                             r.pasteHTML('<br />');
21000                             r.collapse(false);
21001                             r.select();
21002                         }
21003                     }
21004                 }
21005                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21006                     this.cleanUpPaste.defer(100, this);
21007                     return;
21008                 }
21009                 
21010                 
21011             };
21012         }else if(Roo.isOpera){
21013             return function(e){
21014                 var k = e.getKey();
21015                 if(k == e.TAB){
21016                     e.stopEvent();
21017                     this.win.focus();
21018                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21019                     this.deferFocus();
21020                 }
21021                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21022                     this.cleanUpPaste.defer(100, this);
21023                     return;
21024                 }
21025                 
21026             };
21027         }else if(Roo.isSafari){
21028             return function(e){
21029                 var k = e.getKey();
21030                 
21031                 if(k == e.TAB){
21032                     e.stopEvent();
21033                     this.execCmd('InsertText','\t');
21034                     this.deferFocus();
21035                     return;
21036                 }
21037                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21038                     this.cleanUpPaste.defer(100, this);
21039                     return;
21040                 }
21041                 
21042              };
21043         }
21044     }(),
21045     
21046     getAllAncestors: function()
21047     {
21048         var p = this.getSelectedNode();
21049         var a = [];
21050         if (!p) {
21051             a.push(p); // push blank onto stack..
21052             p = this.getParentElement();
21053         }
21054         
21055         
21056         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21057             a.push(p);
21058             p = p.parentNode;
21059         }
21060         a.push(this.doc.body);
21061         return a;
21062     },
21063     lastSel : false,
21064     lastSelNode : false,
21065     
21066     
21067     getSelection : function() 
21068     {
21069         this.assignDocWin();
21070         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21071     },
21072     
21073     getSelectedNode: function() 
21074     {
21075         // this may only work on Gecko!!!
21076         
21077         // should we cache this!!!!
21078         
21079         
21080         
21081          
21082         var range = this.createRange(this.getSelection()).cloneRange();
21083         
21084         if (Roo.isIE) {
21085             var parent = range.parentElement();
21086             while (true) {
21087                 var testRange = range.duplicate();
21088                 testRange.moveToElementText(parent);
21089                 if (testRange.inRange(range)) {
21090                     break;
21091                 }
21092                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21093                     break;
21094                 }
21095                 parent = parent.parentElement;
21096             }
21097             return parent;
21098         }
21099         
21100         // is ancestor a text element.
21101         var ac =  range.commonAncestorContainer;
21102         if (ac.nodeType == 3) {
21103             ac = ac.parentNode;
21104         }
21105         
21106         var ar = ac.childNodes;
21107          
21108         var nodes = [];
21109         var other_nodes = [];
21110         var has_other_nodes = false;
21111         for (var i=0;i<ar.length;i++) {
21112             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21113                 continue;
21114             }
21115             // fullly contained node.
21116             
21117             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21118                 nodes.push(ar[i]);
21119                 continue;
21120             }
21121             
21122             // probably selected..
21123             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21124                 other_nodes.push(ar[i]);
21125                 continue;
21126             }
21127             // outer..
21128             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21129                 continue;
21130             }
21131             
21132             
21133             has_other_nodes = true;
21134         }
21135         if (!nodes.length && other_nodes.length) {
21136             nodes= other_nodes;
21137         }
21138         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21139             return false;
21140         }
21141         
21142         return nodes[0];
21143     },
21144     createRange: function(sel)
21145     {
21146         // this has strange effects when using with 
21147         // top toolbar - not sure if it's a great idea.
21148         //this.editor.contentWindow.focus();
21149         if (typeof sel != "undefined") {
21150             try {
21151                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21152             } catch(e) {
21153                 return this.doc.createRange();
21154             }
21155         } else {
21156             return this.doc.createRange();
21157         }
21158     },
21159     getParentElement: function()
21160     {
21161         
21162         this.assignDocWin();
21163         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21164         
21165         var range = this.createRange(sel);
21166          
21167         try {
21168             var p = range.commonAncestorContainer;
21169             while (p.nodeType == 3) { // text node
21170                 p = p.parentNode;
21171             }
21172             return p;
21173         } catch (e) {
21174             return null;
21175         }
21176     
21177     },
21178     /***
21179      *
21180      * Range intersection.. the hard stuff...
21181      *  '-1' = before
21182      *  '0' = hits..
21183      *  '1' = after.
21184      *         [ -- selected range --- ]
21185      *   [fail]                        [fail]
21186      *
21187      *    basically..
21188      *      if end is before start or  hits it. fail.
21189      *      if start is after end or hits it fail.
21190      *
21191      *   if either hits (but other is outside. - then it's not 
21192      *   
21193      *    
21194      **/
21195     
21196     
21197     // @see http://www.thismuchiknow.co.uk/?p=64.
21198     rangeIntersectsNode : function(range, node)
21199     {
21200         var nodeRange = node.ownerDocument.createRange();
21201         try {
21202             nodeRange.selectNode(node);
21203         } catch (e) {
21204             nodeRange.selectNodeContents(node);
21205         }
21206     
21207         var rangeStartRange = range.cloneRange();
21208         rangeStartRange.collapse(true);
21209     
21210         var rangeEndRange = range.cloneRange();
21211         rangeEndRange.collapse(false);
21212     
21213         var nodeStartRange = nodeRange.cloneRange();
21214         nodeStartRange.collapse(true);
21215     
21216         var nodeEndRange = nodeRange.cloneRange();
21217         nodeEndRange.collapse(false);
21218     
21219         return rangeStartRange.compareBoundaryPoints(
21220                  Range.START_TO_START, nodeEndRange) == -1 &&
21221                rangeEndRange.compareBoundaryPoints(
21222                  Range.START_TO_START, nodeStartRange) == 1;
21223         
21224          
21225     },
21226     rangeCompareNode : function(range, node)
21227     {
21228         var nodeRange = node.ownerDocument.createRange();
21229         try {
21230             nodeRange.selectNode(node);
21231         } catch (e) {
21232             nodeRange.selectNodeContents(node);
21233         }
21234         
21235         
21236         range.collapse(true);
21237     
21238         nodeRange.collapse(true);
21239      
21240         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21241         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21242          
21243         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21244         
21245         var nodeIsBefore   =  ss == 1;
21246         var nodeIsAfter    = ee == -1;
21247         
21248         if (nodeIsBefore && nodeIsAfter) {
21249             return 0; // outer
21250         }
21251         if (!nodeIsBefore && nodeIsAfter) {
21252             return 1; //right trailed.
21253         }
21254         
21255         if (nodeIsBefore && !nodeIsAfter) {
21256             return 2;  // left trailed.
21257         }
21258         // fully contined.
21259         return 3;
21260     },
21261
21262     // private? - in a new class?
21263     cleanUpPaste :  function()
21264     {
21265         // cleans up the whole document..
21266         Roo.log('cleanuppaste');
21267         
21268         this.cleanUpChildren(this.doc.body);
21269         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21270         if (clean != this.doc.body.innerHTML) {
21271             this.doc.body.innerHTML = clean;
21272         }
21273         
21274     },
21275     
21276     cleanWordChars : function(input) {// change the chars to hex code
21277         var he = Roo.HtmlEditorCore;
21278         
21279         var output = input;
21280         Roo.each(he.swapCodes, function(sw) { 
21281             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21282             
21283             output = output.replace(swapper, sw[1]);
21284         });
21285         
21286         return output;
21287     },
21288     
21289     
21290     cleanUpChildren : function (n)
21291     {
21292         if (!n.childNodes.length) {
21293             return;
21294         }
21295         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21296            this.cleanUpChild(n.childNodes[i]);
21297         }
21298     },
21299     
21300     
21301         
21302     
21303     cleanUpChild : function (node)
21304     {
21305         var ed = this;
21306         //console.log(node);
21307         if (node.nodeName == "#text") {
21308             // clean up silly Windows -- stuff?
21309             return; 
21310         }
21311         if (node.nodeName == "#comment") {
21312             node.parentNode.removeChild(node);
21313             // clean up silly Windows -- stuff?
21314             return; 
21315         }
21316         var lcname = node.tagName.toLowerCase();
21317         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21318         // whitelist of tags..
21319         
21320         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21321             // remove node.
21322             node.parentNode.removeChild(node);
21323             return;
21324             
21325         }
21326         
21327         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21328         
21329         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21330         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21331         
21332         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21333         //    remove_keep_children = true;
21334         //}
21335         
21336         if (remove_keep_children) {
21337             this.cleanUpChildren(node);
21338             // inserts everything just before this node...
21339             while (node.childNodes.length) {
21340                 var cn = node.childNodes[0];
21341                 node.removeChild(cn);
21342                 node.parentNode.insertBefore(cn, node);
21343             }
21344             node.parentNode.removeChild(node);
21345             return;
21346         }
21347         
21348         if (!node.attributes || !node.attributes.length) {
21349             this.cleanUpChildren(node);
21350             return;
21351         }
21352         
21353         function cleanAttr(n,v)
21354         {
21355             
21356             if (v.match(/^\./) || v.match(/^\//)) {
21357                 return;
21358             }
21359             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
21360                 return;
21361             }
21362             if (v.match(/^#/)) {
21363                 return;
21364             }
21365 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21366             node.removeAttribute(n);
21367             
21368         }
21369         
21370         var cwhite = this.cwhite;
21371         var cblack = this.cblack;
21372             
21373         function cleanStyle(n,v)
21374         {
21375             if (v.match(/expression/)) { //XSS?? should we even bother..
21376                 node.removeAttribute(n);
21377                 return;
21378             }
21379             
21380             var parts = v.split(/;/);
21381             var clean = [];
21382             
21383             Roo.each(parts, function(p) {
21384                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21385                 if (!p.length) {
21386                     return true;
21387                 }
21388                 var l = p.split(':').shift().replace(/\s+/g,'');
21389                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21390                 
21391                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21392 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21393                     //node.removeAttribute(n);
21394                     return true;
21395                 }
21396                 //Roo.log()
21397                 // only allow 'c whitelisted system attributes'
21398                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21399 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21400                     //node.removeAttribute(n);
21401                     return true;
21402                 }
21403                 
21404                 
21405                  
21406                 
21407                 clean.push(p);
21408                 return true;
21409             });
21410             if (clean.length) { 
21411                 node.setAttribute(n, clean.join(';'));
21412             } else {
21413                 node.removeAttribute(n);
21414             }
21415             
21416         }
21417         
21418         
21419         for (var i = node.attributes.length-1; i > -1 ; i--) {
21420             var a = node.attributes[i];
21421             //console.log(a);
21422             
21423             if (a.name.toLowerCase().substr(0,2)=='on')  {
21424                 node.removeAttribute(a.name);
21425                 continue;
21426             }
21427             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21428                 node.removeAttribute(a.name);
21429                 continue;
21430             }
21431             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21432                 cleanAttr(a.name,a.value); // fixme..
21433                 continue;
21434             }
21435             if (a.name == 'style') {
21436                 cleanStyle(a.name,a.value);
21437                 continue;
21438             }
21439             /// clean up MS crap..
21440             // tecnically this should be a list of valid class'es..
21441             
21442             
21443             if (a.name == 'class') {
21444                 if (a.value.match(/^Mso/)) {
21445                     node.className = '';
21446                 }
21447                 
21448                 if (a.value.match(/body/)) {
21449                     node.className = '';
21450                 }
21451                 continue;
21452             }
21453             
21454             // style cleanup!?
21455             // class cleanup?
21456             
21457         }
21458         
21459         
21460         this.cleanUpChildren(node);
21461         
21462         
21463     },
21464     
21465     /**
21466      * Clean up MS wordisms...
21467      */
21468     cleanWord : function(node)
21469     {
21470         
21471         
21472         if (!node) {
21473             this.cleanWord(this.doc.body);
21474             return;
21475         }
21476         if (node.nodeName == "#text") {
21477             // clean up silly Windows -- stuff?
21478             return; 
21479         }
21480         if (node.nodeName == "#comment") {
21481             node.parentNode.removeChild(node);
21482             // clean up silly Windows -- stuff?
21483             return; 
21484         }
21485         
21486         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21487             node.parentNode.removeChild(node);
21488             return;
21489         }
21490         
21491         // remove - but keep children..
21492         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21493             while (node.childNodes.length) {
21494                 var cn = node.childNodes[0];
21495                 node.removeChild(cn);
21496                 node.parentNode.insertBefore(cn, node);
21497             }
21498             node.parentNode.removeChild(node);
21499             this.iterateChildren(node, this.cleanWord);
21500             return;
21501         }
21502         // clean styles
21503         if (node.className.length) {
21504             
21505             var cn = node.className.split(/\W+/);
21506             var cna = [];
21507             Roo.each(cn, function(cls) {
21508                 if (cls.match(/Mso[a-zA-Z]+/)) {
21509                     return;
21510                 }
21511                 cna.push(cls);
21512             });
21513             node.className = cna.length ? cna.join(' ') : '';
21514             if (!cna.length) {
21515                 node.removeAttribute("class");
21516             }
21517         }
21518         
21519         if (node.hasAttribute("lang")) {
21520             node.removeAttribute("lang");
21521         }
21522         
21523         if (node.hasAttribute("style")) {
21524             
21525             var styles = node.getAttribute("style").split(";");
21526             var nstyle = [];
21527             Roo.each(styles, function(s) {
21528                 if (!s.match(/:/)) {
21529                     return;
21530                 }
21531                 var kv = s.split(":");
21532                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21533                     return;
21534                 }
21535                 // what ever is left... we allow.
21536                 nstyle.push(s);
21537             });
21538             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21539             if (!nstyle.length) {
21540                 node.removeAttribute('style');
21541             }
21542         }
21543         this.iterateChildren(node, this.cleanWord);
21544         
21545         
21546         
21547     },
21548     /**
21549      * iterateChildren of a Node, calling fn each time, using this as the scole..
21550      * @param {DomNode} node node to iterate children of.
21551      * @param {Function} fn method of this class to call on each item.
21552      */
21553     iterateChildren : function(node, fn)
21554     {
21555         if (!node.childNodes.length) {
21556                 return;
21557         }
21558         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21559            fn.call(this, node.childNodes[i])
21560         }
21561     },
21562     
21563     
21564     /**
21565      * cleanTableWidths.
21566      *
21567      * Quite often pasting from word etc.. results in tables with column and widths.
21568      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21569      *
21570      */
21571     cleanTableWidths : function(node)
21572     {
21573          
21574          
21575         if (!node) {
21576             this.cleanTableWidths(this.doc.body);
21577             return;
21578         }
21579         
21580         // ignore list...
21581         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21582             return; 
21583         }
21584         Roo.log(node.tagName);
21585         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21586             this.iterateChildren(node, this.cleanTableWidths);
21587             return;
21588         }
21589         if (node.hasAttribute('width')) {
21590             node.removeAttribute('width');
21591         }
21592         
21593          
21594         if (node.hasAttribute("style")) {
21595             // pretty basic...
21596             
21597             var styles = node.getAttribute("style").split(";");
21598             var nstyle = [];
21599             Roo.each(styles, function(s) {
21600                 if (!s.match(/:/)) {
21601                     return;
21602                 }
21603                 var kv = s.split(":");
21604                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21605                     return;
21606                 }
21607                 // what ever is left... we allow.
21608                 nstyle.push(s);
21609             });
21610             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21611             if (!nstyle.length) {
21612                 node.removeAttribute('style');
21613             }
21614         }
21615         
21616         this.iterateChildren(node, this.cleanTableWidths);
21617         
21618         
21619     },
21620     
21621     
21622     
21623     
21624     domToHTML : function(currentElement, depth, nopadtext) {
21625         
21626         depth = depth || 0;
21627         nopadtext = nopadtext || false;
21628     
21629         if (!currentElement) {
21630             return this.domToHTML(this.doc.body);
21631         }
21632         
21633         //Roo.log(currentElement);
21634         var j;
21635         var allText = false;
21636         var nodeName = currentElement.nodeName;
21637         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21638         
21639         if  (nodeName == '#text') {
21640             
21641             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21642         }
21643         
21644         
21645         var ret = '';
21646         if (nodeName != 'BODY') {
21647              
21648             var i = 0;
21649             // Prints the node tagName, such as <A>, <IMG>, etc
21650             if (tagName) {
21651                 var attr = [];
21652                 for(i = 0; i < currentElement.attributes.length;i++) {
21653                     // quoting?
21654                     var aname = currentElement.attributes.item(i).name;
21655                     if (!currentElement.attributes.item(i).value.length) {
21656                         continue;
21657                     }
21658                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21659                 }
21660                 
21661                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21662             } 
21663             else {
21664                 
21665                 // eack
21666             }
21667         } else {
21668             tagName = false;
21669         }
21670         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21671             return ret;
21672         }
21673         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21674             nopadtext = true;
21675         }
21676         
21677         
21678         // Traverse the tree
21679         i = 0;
21680         var currentElementChild = currentElement.childNodes.item(i);
21681         var allText = true;
21682         var innerHTML  = '';
21683         lastnode = '';
21684         while (currentElementChild) {
21685             // Formatting code (indent the tree so it looks nice on the screen)
21686             var nopad = nopadtext;
21687             if (lastnode == 'SPAN') {
21688                 nopad  = true;
21689             }
21690             // text
21691             if  (currentElementChild.nodeName == '#text') {
21692                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21693                 toadd = nopadtext ? toadd : toadd.trim();
21694                 if (!nopad && toadd.length > 80) {
21695                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21696                 }
21697                 innerHTML  += toadd;
21698                 
21699                 i++;
21700                 currentElementChild = currentElement.childNodes.item(i);
21701                 lastNode = '';
21702                 continue;
21703             }
21704             allText = false;
21705             
21706             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21707                 
21708             // Recursively traverse the tree structure of the child node
21709             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21710             lastnode = currentElementChild.nodeName;
21711             i++;
21712             currentElementChild=currentElement.childNodes.item(i);
21713         }
21714         
21715         ret += innerHTML;
21716         
21717         if (!allText) {
21718                 // The remaining code is mostly for formatting the tree
21719             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21720         }
21721         
21722         
21723         if (tagName) {
21724             ret+= "</"+tagName+">";
21725         }
21726         return ret;
21727         
21728     },
21729         
21730     applyBlacklists : function()
21731     {
21732         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
21733         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
21734         
21735         this.white = [];
21736         this.black = [];
21737         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21738             if (b.indexOf(tag) > -1) {
21739                 return;
21740             }
21741             this.white.push(tag);
21742             
21743         }, this);
21744         
21745         Roo.each(w, function(tag) {
21746             if (b.indexOf(tag) > -1) {
21747                 return;
21748             }
21749             if (this.white.indexOf(tag) > -1) {
21750                 return;
21751             }
21752             this.white.push(tag);
21753             
21754         }, this);
21755         
21756         
21757         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21758             if (w.indexOf(tag) > -1) {
21759                 return;
21760             }
21761             this.black.push(tag);
21762             
21763         }, this);
21764         
21765         Roo.each(b, function(tag) {
21766             if (w.indexOf(tag) > -1) {
21767                 return;
21768             }
21769             if (this.black.indexOf(tag) > -1) {
21770                 return;
21771             }
21772             this.black.push(tag);
21773             
21774         }, this);
21775         
21776         
21777         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
21778         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
21779         
21780         this.cwhite = [];
21781         this.cblack = [];
21782         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21783             if (b.indexOf(tag) > -1) {
21784                 return;
21785             }
21786             this.cwhite.push(tag);
21787             
21788         }, this);
21789         
21790         Roo.each(w, function(tag) {
21791             if (b.indexOf(tag) > -1) {
21792                 return;
21793             }
21794             if (this.cwhite.indexOf(tag) > -1) {
21795                 return;
21796             }
21797             this.cwhite.push(tag);
21798             
21799         }, this);
21800         
21801         
21802         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21803             if (w.indexOf(tag) > -1) {
21804                 return;
21805             }
21806             this.cblack.push(tag);
21807             
21808         }, this);
21809         
21810         Roo.each(b, function(tag) {
21811             if (w.indexOf(tag) > -1) {
21812                 return;
21813             }
21814             if (this.cblack.indexOf(tag) > -1) {
21815                 return;
21816             }
21817             this.cblack.push(tag);
21818             
21819         }, this);
21820     },
21821     
21822     setStylesheets : function(stylesheets)
21823     {
21824         if(typeof(stylesheets) == 'string'){
21825             Roo.get(this.iframe.contentDocument.head).createChild({
21826                 tag : 'link',
21827                 rel : 'stylesheet',
21828                 type : 'text/css',
21829                 href : stylesheets
21830             });
21831             
21832             return;
21833         }
21834         var _this = this;
21835      
21836         Roo.each(stylesheets, function(s) {
21837             if(!s.length){
21838                 return;
21839             }
21840             
21841             Roo.get(_this.iframe.contentDocument.head).createChild({
21842                 tag : 'link',
21843                 rel : 'stylesheet',
21844                 type : 'text/css',
21845                 href : s
21846             });
21847         });
21848
21849         
21850     },
21851     
21852     removeStylesheets : function()
21853     {
21854         var _this = this;
21855         
21856         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21857             s.remove();
21858         });
21859     }
21860     
21861     // hide stuff that is not compatible
21862     /**
21863      * @event blur
21864      * @hide
21865      */
21866     /**
21867      * @event change
21868      * @hide
21869      */
21870     /**
21871      * @event focus
21872      * @hide
21873      */
21874     /**
21875      * @event specialkey
21876      * @hide
21877      */
21878     /**
21879      * @cfg {String} fieldClass @hide
21880      */
21881     /**
21882      * @cfg {String} focusClass @hide
21883      */
21884     /**
21885      * @cfg {String} autoCreate @hide
21886      */
21887     /**
21888      * @cfg {String} inputType @hide
21889      */
21890     /**
21891      * @cfg {String} invalidClass @hide
21892      */
21893     /**
21894      * @cfg {String} invalidText @hide
21895      */
21896     /**
21897      * @cfg {String} msgFx @hide
21898      */
21899     /**
21900      * @cfg {String} validateOnBlur @hide
21901      */
21902 });
21903
21904 Roo.HtmlEditorCore.white = [
21905         'area', 'br', 'img', 'input', 'hr', 'wbr',
21906         
21907        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
21908        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
21909        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
21910        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
21911        'table',   'ul',         'xmp', 
21912        
21913        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
21914       'thead',   'tr', 
21915      
21916       'dir', 'menu', 'ol', 'ul', 'dl',
21917        
21918       'embed',  'object'
21919 ];
21920
21921
21922 Roo.HtmlEditorCore.black = [
21923     //    'embed',  'object', // enable - backend responsiblity to clean thiese
21924         'applet', // 
21925         'base',   'basefont', 'bgsound', 'blink',  'body', 
21926         'frame',  'frameset', 'head',    'html',   'ilayer', 
21927         'iframe', 'layer',  'link',     'meta',    'object',   
21928         'script', 'style' ,'title',  'xml' // clean later..
21929 ];
21930 Roo.HtmlEditorCore.clean = [
21931     'script', 'style', 'title', 'xml'
21932 ];
21933 Roo.HtmlEditorCore.remove = [
21934     'font'
21935 ];
21936 // attributes..
21937
21938 Roo.HtmlEditorCore.ablack = [
21939     'on'
21940 ];
21941     
21942 Roo.HtmlEditorCore.aclean = [ 
21943     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
21944 ];
21945
21946 // protocols..
21947 Roo.HtmlEditorCore.pwhite= [
21948         'http',  'https',  'mailto'
21949 ];
21950
21951 // white listed style attributes.
21952 Roo.HtmlEditorCore.cwhite= [
21953       //  'text-align', /// default is to allow most things..
21954       
21955          
21956 //        'font-size'//??
21957 ];
21958
21959 // black listed style attributes.
21960 Roo.HtmlEditorCore.cblack= [
21961       //  'font-size' -- this can be set by the project 
21962 ];
21963
21964
21965 Roo.HtmlEditorCore.swapCodes   =[ 
21966     [    8211, "--" ], 
21967     [    8212, "--" ], 
21968     [    8216,  "'" ],  
21969     [    8217, "'" ],  
21970     [    8220, '"' ],  
21971     [    8221, '"' ],  
21972     [    8226, "*" ],  
21973     [    8230, "..." ]
21974 ]; 
21975
21976     /*
21977  * - LGPL
21978  *
21979  * HtmlEditor
21980  * 
21981  */
21982
21983 /**
21984  * @class Roo.bootstrap.HtmlEditor
21985  * @extends Roo.bootstrap.TextArea
21986  * Bootstrap HtmlEditor class
21987
21988  * @constructor
21989  * Create a new HtmlEditor
21990  * @param {Object} config The config object
21991  */
21992
21993 Roo.bootstrap.HtmlEditor = function(config){
21994     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
21995     if (!this.toolbars) {
21996         this.toolbars = [];
21997     }
21998     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
21999     this.addEvents({
22000             /**
22001              * @event initialize
22002              * Fires when the editor is fully initialized (including the iframe)
22003              * @param {HtmlEditor} this
22004              */
22005             initialize: true,
22006             /**
22007              * @event activate
22008              * Fires when the editor is first receives the focus. Any insertion must wait
22009              * until after this event.
22010              * @param {HtmlEditor} this
22011              */
22012             activate: true,
22013              /**
22014              * @event beforesync
22015              * Fires before the textarea is updated with content from the editor iframe. Return false
22016              * to cancel the sync.
22017              * @param {HtmlEditor} this
22018              * @param {String} html
22019              */
22020             beforesync: true,
22021              /**
22022              * @event beforepush
22023              * Fires before the iframe editor is updated with content from the textarea. Return false
22024              * to cancel the push.
22025              * @param {HtmlEditor} this
22026              * @param {String} html
22027              */
22028             beforepush: true,
22029              /**
22030              * @event sync
22031              * Fires when the textarea is updated with content from the editor iframe.
22032              * @param {HtmlEditor} this
22033              * @param {String} html
22034              */
22035             sync: true,
22036              /**
22037              * @event push
22038              * Fires when the iframe editor is updated with content from the textarea.
22039              * @param {HtmlEditor} this
22040              * @param {String} html
22041              */
22042             push: true,
22043              /**
22044              * @event editmodechange
22045              * Fires when the editor switches edit modes
22046              * @param {HtmlEditor} this
22047              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22048              */
22049             editmodechange: true,
22050             /**
22051              * @event editorevent
22052              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22053              * @param {HtmlEditor} this
22054              */
22055             editorevent: true,
22056             /**
22057              * @event firstfocus
22058              * Fires when on first focus - needed by toolbars..
22059              * @param {HtmlEditor} this
22060              */
22061             firstfocus: true,
22062             /**
22063              * @event autosave
22064              * Auto save the htmlEditor value as a file into Events
22065              * @param {HtmlEditor} this
22066              */
22067             autosave: true,
22068             /**
22069              * @event savedpreview
22070              * preview the saved version of htmlEditor
22071              * @param {HtmlEditor} this
22072              */
22073             savedpreview: true
22074         });
22075 };
22076
22077
22078 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
22079     
22080     
22081       /**
22082      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22083      */
22084     toolbars : false,
22085    
22086      /**
22087      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22088      *                        Roo.resizable.
22089      */
22090     resizable : false,
22091      /**
22092      * @cfg {Number} height (in pixels)
22093      */   
22094     height: 300,
22095    /**
22096      * @cfg {Number} width (in pixels)
22097      */   
22098     width: false,
22099     
22100     /**
22101      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22102      * 
22103      */
22104     stylesheets: false,
22105     
22106     // id of frame..
22107     frameId: false,
22108     
22109     // private properties
22110     validationEvent : false,
22111     deferHeight: true,
22112     initialized : false,
22113     activated : false,
22114     
22115     onFocus : Roo.emptyFn,
22116     iframePad:3,
22117     hideMode:'offsets',
22118     
22119     
22120     tbContainer : false,
22121     
22122     toolbarContainer :function() {
22123         return this.wrap.select('.x-html-editor-tb',true).first();
22124     },
22125
22126     /**
22127      * Protected method that will not generally be called directly. It
22128      * is called when the editor creates its toolbar. Override this method if you need to
22129      * add custom toolbar buttons.
22130      * @param {HtmlEditor} editor
22131      */
22132     createToolbar : function(){
22133         
22134         Roo.log("create toolbars");
22135         
22136         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
22137         this.toolbars[0].render(this.toolbarContainer());
22138         
22139         return;
22140         
22141 //        if (!editor.toolbars || !editor.toolbars.length) {
22142 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
22143 //        }
22144 //        
22145 //        for (var i =0 ; i < editor.toolbars.length;i++) {
22146 //            editor.toolbars[i] = Roo.factory(
22147 //                    typeof(editor.toolbars[i]) == 'string' ?
22148 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
22149 //                Roo.bootstrap.HtmlEditor);
22150 //            editor.toolbars[i].init(editor);
22151 //        }
22152     },
22153
22154      
22155     // private
22156     onRender : function(ct, position)
22157     {
22158        // Roo.log("Call onRender: " + this.xtype);
22159         var _t = this;
22160         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
22161       
22162         this.wrap = this.inputEl().wrap({
22163             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22164         });
22165         
22166         this.editorcore.onRender(ct, position);
22167          
22168         if (this.resizable) {
22169             this.resizeEl = new Roo.Resizable(this.wrap, {
22170                 pinned : true,
22171                 wrap: true,
22172                 dynamic : true,
22173                 minHeight : this.height,
22174                 height: this.height,
22175                 handles : this.resizable,
22176                 width: this.width,
22177                 listeners : {
22178                     resize : function(r, w, h) {
22179                         _t.onResize(w,h); // -something
22180                     }
22181                 }
22182             });
22183             
22184         }
22185         this.createToolbar(this);
22186        
22187         
22188         if(!this.width && this.resizable){
22189             this.setSize(this.wrap.getSize());
22190         }
22191         if (this.resizeEl) {
22192             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22193             // should trigger onReize..
22194         }
22195         
22196     },
22197
22198     // private
22199     onResize : function(w, h)
22200     {
22201         Roo.log('resize: ' +w + ',' + h );
22202         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
22203         var ew = false;
22204         var eh = false;
22205         
22206         if(this.inputEl() ){
22207             if(typeof w == 'number'){
22208                 var aw = w - this.wrap.getFrameWidth('lr');
22209                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
22210                 ew = aw;
22211             }
22212             if(typeof h == 'number'){
22213                  var tbh = -11;  // fixme it needs to tool bar size!
22214                 for (var i =0; i < this.toolbars.length;i++) {
22215                     // fixme - ask toolbars for heights?
22216                     tbh += this.toolbars[i].el.getHeight();
22217                     //if (this.toolbars[i].footer) {
22218                     //    tbh += this.toolbars[i].footer.el.getHeight();
22219                     //}
22220                 }
22221               
22222                 
22223                 
22224                 
22225                 
22226                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22227                 ah -= 5; // knock a few pixes off for look..
22228                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
22229                 var eh = ah;
22230             }
22231         }
22232         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22233         this.editorcore.onResize(ew,eh);
22234         
22235     },
22236
22237     /**
22238      * Toggles the editor between standard and source edit mode.
22239      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22240      */
22241     toggleSourceEdit : function(sourceEditMode)
22242     {
22243         this.editorcore.toggleSourceEdit(sourceEditMode);
22244         
22245         if(this.editorcore.sourceEditMode){
22246             Roo.log('editor - showing textarea');
22247             
22248 //            Roo.log('in');
22249 //            Roo.log(this.syncValue());
22250             this.syncValue();
22251             this.inputEl().removeClass(['hide', 'x-hidden']);
22252             this.inputEl().dom.removeAttribute('tabIndex');
22253             this.inputEl().focus();
22254         }else{
22255             Roo.log('editor - hiding textarea');
22256 //            Roo.log('out')
22257 //            Roo.log(this.pushValue()); 
22258             this.pushValue();
22259             
22260             this.inputEl().addClass(['hide', 'x-hidden']);
22261             this.inputEl().dom.setAttribute('tabIndex', -1);
22262             //this.deferFocus();
22263         }
22264          
22265         if(this.resizable){
22266             this.setSize(this.wrap.getSize());
22267         }
22268         
22269         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22270     },
22271  
22272     // private (for BoxComponent)
22273     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22274
22275     // private (for BoxComponent)
22276     getResizeEl : function(){
22277         return this.wrap;
22278     },
22279
22280     // private (for BoxComponent)
22281     getPositionEl : function(){
22282         return this.wrap;
22283     },
22284
22285     // private
22286     initEvents : function(){
22287         this.originalValue = this.getValue();
22288     },
22289
22290 //    /**
22291 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22292 //     * @method
22293 //     */
22294 //    markInvalid : Roo.emptyFn,
22295 //    /**
22296 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22297 //     * @method
22298 //     */
22299 //    clearInvalid : Roo.emptyFn,
22300
22301     setValue : function(v){
22302         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
22303         this.editorcore.pushValue();
22304     },
22305
22306      
22307     // private
22308     deferFocus : function(){
22309         this.focus.defer(10, this);
22310     },
22311
22312     // doc'ed in Field
22313     focus : function(){
22314         this.editorcore.focus();
22315         
22316     },
22317       
22318
22319     // private
22320     onDestroy : function(){
22321         
22322         
22323         
22324         if(this.rendered){
22325             
22326             for (var i =0; i < this.toolbars.length;i++) {
22327                 // fixme - ask toolbars for heights?
22328                 this.toolbars[i].onDestroy();
22329             }
22330             
22331             this.wrap.dom.innerHTML = '';
22332             this.wrap.remove();
22333         }
22334     },
22335
22336     // private
22337     onFirstFocus : function(){
22338         //Roo.log("onFirstFocus");
22339         this.editorcore.onFirstFocus();
22340          for (var i =0; i < this.toolbars.length;i++) {
22341             this.toolbars[i].onFirstFocus();
22342         }
22343         
22344     },
22345     
22346     // private
22347     syncValue : function()
22348     {   
22349         this.editorcore.syncValue();
22350     },
22351     
22352     pushValue : function()
22353     {   
22354         this.editorcore.pushValue();
22355     }
22356      
22357     
22358     // hide stuff that is not compatible
22359     /**
22360      * @event blur
22361      * @hide
22362      */
22363     /**
22364      * @event change
22365      * @hide
22366      */
22367     /**
22368      * @event focus
22369      * @hide
22370      */
22371     /**
22372      * @event specialkey
22373      * @hide
22374      */
22375     /**
22376      * @cfg {String} fieldClass @hide
22377      */
22378     /**
22379      * @cfg {String} focusClass @hide
22380      */
22381     /**
22382      * @cfg {String} autoCreate @hide
22383      */
22384     /**
22385      * @cfg {String} inputType @hide
22386      */
22387     /**
22388      * @cfg {String} invalidClass @hide
22389      */
22390     /**
22391      * @cfg {String} invalidText @hide
22392      */
22393     /**
22394      * @cfg {String} msgFx @hide
22395      */
22396     /**
22397      * @cfg {String} validateOnBlur @hide
22398      */
22399 });
22400  
22401     
22402    
22403    
22404    
22405       
22406 Roo.namespace('Roo.bootstrap.htmleditor');
22407 /**
22408  * @class Roo.bootstrap.HtmlEditorToolbar1
22409  * Basic Toolbar
22410  * 
22411  * Usage:
22412  *
22413  new Roo.bootstrap.HtmlEditor({
22414     ....
22415     toolbars : [
22416         new Roo.bootstrap.HtmlEditorToolbar1({
22417             disable : { fonts: 1 , format: 1, ..., ... , ...],
22418             btns : [ .... ]
22419         })
22420     }
22421      
22422  * 
22423  * @cfg {Object} disable List of elements to disable..
22424  * @cfg {Array} btns List of additional buttons.
22425  * 
22426  * 
22427  * NEEDS Extra CSS? 
22428  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22429  */
22430  
22431 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
22432 {
22433     
22434     Roo.apply(this, config);
22435     
22436     // default disabled, based on 'good practice'..
22437     this.disable = this.disable || {};
22438     Roo.applyIf(this.disable, {
22439         fontSize : true,
22440         colors : true,
22441         specialElements : true
22442     });
22443     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
22444     
22445     this.editor = config.editor;
22446     this.editorcore = config.editor.editorcore;
22447     
22448     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
22449     
22450     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22451     // dont call parent... till later.
22452 }
22453 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
22454      
22455     bar : true,
22456     
22457     editor : false,
22458     editorcore : false,
22459     
22460     
22461     formats : [
22462         "p" ,  
22463         "h1","h2","h3","h4","h5","h6", 
22464         "pre", "code", 
22465         "abbr", "acronym", "address", "cite", "samp", "var",
22466         'div','span'
22467     ],
22468     
22469     onRender : function(ct, position)
22470     {
22471        // Roo.log("Call onRender: " + this.xtype);
22472         
22473        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
22474        Roo.log(this.el);
22475        this.el.dom.style.marginBottom = '0';
22476        var _this = this;
22477        var editorcore = this.editorcore;
22478        var editor= this.editor;
22479        
22480        var children = [];
22481        var btn = function(id,cmd , toggle, handler){
22482        
22483             var  event = toggle ? 'toggle' : 'click';
22484        
22485             var a = {
22486                 size : 'sm',
22487                 xtype: 'Button',
22488                 xns: Roo.bootstrap,
22489                 glyphicon : id,
22490                 cmd : id || cmd,
22491                 enableToggle:toggle !== false,
22492                 //html : 'submit'
22493                 pressed : toggle ? false : null,
22494                 listeners : {}
22495             };
22496             a.listeners[toggle ? 'toggle' : 'click'] = function() {
22497                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
22498             };
22499             children.push(a);
22500             return a;
22501        }
22502         
22503         var style = {
22504                 xtype: 'Button',
22505                 size : 'sm',
22506                 xns: Roo.bootstrap,
22507                 glyphicon : 'font',
22508                 //html : 'submit'
22509                 menu : {
22510                     xtype: 'Menu',
22511                     xns: Roo.bootstrap,
22512                     items:  []
22513                 }
22514         };
22515         Roo.each(this.formats, function(f) {
22516             style.menu.items.push({
22517                 xtype :'MenuItem',
22518                 xns: Roo.bootstrap,
22519                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
22520                 tagname : f,
22521                 listeners : {
22522                     click : function()
22523                     {
22524                         editorcore.insertTag(this.tagname);
22525                         editor.focus();
22526                     }
22527                 }
22528                 
22529             });
22530         });
22531          children.push(style);   
22532             
22533             
22534         btn('bold',false,true);
22535         btn('italic',false,true);
22536         btn('align-left', 'justifyleft',true);
22537         btn('align-center', 'justifycenter',true);
22538         btn('align-right' , 'justifyright',true);
22539         btn('link', false, false, function(btn) {
22540             //Roo.log("create link?");
22541             var url = prompt(this.createLinkText, this.defaultLinkValue);
22542             if(url && url != 'http:/'+'/'){
22543                 this.editorcore.relayCmd('createlink', url);
22544             }
22545         }),
22546         btn('list','insertunorderedlist',true);
22547         btn('pencil', false,true, function(btn){
22548                 Roo.log(this);
22549                 
22550                 this.toggleSourceEdit(btn.pressed);
22551         });
22552         /*
22553         var cog = {
22554                 xtype: 'Button',
22555                 size : 'sm',
22556                 xns: Roo.bootstrap,
22557                 glyphicon : 'cog',
22558                 //html : 'submit'
22559                 menu : {
22560                     xtype: 'Menu',
22561                     xns: Roo.bootstrap,
22562                     items:  []
22563                 }
22564         };
22565         
22566         cog.menu.items.push({
22567             xtype :'MenuItem',
22568             xns: Roo.bootstrap,
22569             html : Clean styles,
22570             tagname : f,
22571             listeners : {
22572                 click : function()
22573                 {
22574                     editorcore.insertTag(this.tagname);
22575                     editor.focus();
22576                 }
22577             }
22578             
22579         });
22580        */
22581         
22582          
22583        this.xtype = 'NavSimplebar';
22584         
22585         for(var i=0;i< children.length;i++) {
22586             
22587             this.buttons.add(this.addxtypeChild(children[i]));
22588             
22589         }
22590         
22591         editor.on('editorevent', this.updateToolbar, this);
22592     },
22593     onBtnClick : function(id)
22594     {
22595        this.editorcore.relayCmd(id);
22596        this.editorcore.focus();
22597     },
22598     
22599     /**
22600      * Protected method that will not generally be called directly. It triggers
22601      * a toolbar update by reading the markup state of the current selection in the editor.
22602      */
22603     updateToolbar: function(){
22604
22605         if(!this.editorcore.activated){
22606             this.editor.onFirstFocus(); // is this neeed?
22607             return;
22608         }
22609
22610         var btns = this.buttons; 
22611         var doc = this.editorcore.doc;
22612         btns.get('bold').setActive(doc.queryCommandState('bold'));
22613         btns.get('italic').setActive(doc.queryCommandState('italic'));
22614         //btns.get('underline').setActive(doc.queryCommandState('underline'));
22615         
22616         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
22617         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
22618         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
22619         
22620         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
22621         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
22622          /*
22623         
22624         var ans = this.editorcore.getAllAncestors();
22625         if (this.formatCombo) {
22626             
22627             
22628             var store = this.formatCombo.store;
22629             this.formatCombo.setValue("");
22630             for (var i =0; i < ans.length;i++) {
22631                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
22632                     // select it..
22633                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
22634                     break;
22635                 }
22636             }
22637         }
22638         
22639         
22640         
22641         // hides menus... - so this cant be on a menu...
22642         Roo.bootstrap.MenuMgr.hideAll();
22643         */
22644         Roo.bootstrap.MenuMgr.hideAll();
22645         //this.editorsyncValue();
22646     },
22647     onFirstFocus: function() {
22648         this.buttons.each(function(item){
22649            item.enable();
22650         });
22651     },
22652     toggleSourceEdit : function(sourceEditMode){
22653         
22654           
22655         if(sourceEditMode){
22656             Roo.log("disabling buttons");
22657            this.buttons.each( function(item){
22658                 if(item.cmd != 'pencil'){
22659                     item.disable();
22660                 }
22661             });
22662           
22663         }else{
22664             Roo.log("enabling buttons");
22665             if(this.editorcore.initialized){
22666                 this.buttons.each( function(item){
22667                     item.enable();
22668                 });
22669             }
22670             
22671         }
22672         Roo.log("calling toggole on editor");
22673         // tell the editor that it's been pressed..
22674         this.editor.toggleSourceEdit(sourceEditMode);
22675        
22676     }
22677 });
22678
22679
22680
22681
22682
22683 /**
22684  * @class Roo.bootstrap.Table.AbstractSelectionModel
22685  * @extends Roo.util.Observable
22686  * Abstract base class for grid SelectionModels.  It provides the interface that should be
22687  * implemented by descendant classes.  This class should not be directly instantiated.
22688  * @constructor
22689  */
22690 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22691     this.locked = false;
22692     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22693 };
22694
22695
22696 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
22697     /** @ignore Called by the grid automatically. Do not call directly. */
22698     init : function(grid){
22699         this.grid = grid;
22700         this.initEvents();
22701     },
22702
22703     /**
22704      * Locks the selections.
22705      */
22706     lock : function(){
22707         this.locked = true;
22708     },
22709
22710     /**
22711      * Unlocks the selections.
22712      */
22713     unlock : function(){
22714         this.locked = false;
22715     },
22716
22717     /**
22718      * Returns true if the selections are locked.
22719      * @return {Boolean}
22720      */
22721     isLocked : function(){
22722         return this.locked;
22723     }
22724 });
22725 /**
22726  * @extends Roo.bootstrap.Table.AbstractSelectionModel
22727  * @class Roo.bootstrap.Table.RowSelectionModel
22728  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22729  * It supports multiple selections and keyboard selection/navigation. 
22730  * @constructor
22731  * @param {Object} config
22732  */
22733
22734 Roo.bootstrap.Table.RowSelectionModel = function(config){
22735     Roo.apply(this, config);
22736     this.selections = new Roo.util.MixedCollection(false, function(o){
22737         return o.id;
22738     });
22739
22740     this.last = false;
22741     this.lastActive = false;
22742
22743     this.addEvents({
22744         /**
22745              * @event selectionchange
22746              * Fires when the selection changes
22747              * @param {SelectionModel} this
22748              */
22749             "selectionchange" : true,
22750         /**
22751              * @event afterselectionchange
22752              * Fires after the selection changes (eg. by key press or clicking)
22753              * @param {SelectionModel} this
22754              */
22755             "afterselectionchange" : true,
22756         /**
22757              * @event beforerowselect
22758              * Fires when a row is selected being selected, return false to cancel.
22759              * @param {SelectionModel} this
22760              * @param {Number} rowIndex The selected index
22761              * @param {Boolean} keepExisting False if other selections will be cleared
22762              */
22763             "beforerowselect" : true,
22764         /**
22765              * @event rowselect
22766              * Fires when a row is selected.
22767              * @param {SelectionModel} this
22768              * @param {Number} rowIndex The selected index
22769              * @param {Roo.data.Record} r The record
22770              */
22771             "rowselect" : true,
22772         /**
22773              * @event rowdeselect
22774              * Fires when a row is deselected.
22775              * @param {SelectionModel} this
22776              * @param {Number} rowIndex The selected index
22777              */
22778         "rowdeselect" : true
22779     });
22780     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22781     this.locked = false;
22782  };
22783
22784 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
22785     /**
22786      * @cfg {Boolean} singleSelect
22787      * True to allow selection of only one row at a time (defaults to false)
22788      */
22789     singleSelect : false,
22790
22791     // private
22792     initEvents : function()
22793     {
22794
22795         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22796         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
22797         //}else{ // allow click to work like normal
22798          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
22799         //}
22800         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
22801         this.grid.on("rowclick", this.handleMouseDown, this);
22802         
22803         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22804             "up" : function(e){
22805                 if(!e.shiftKey){
22806                     this.selectPrevious(e.shiftKey);
22807                 }else if(this.last !== false && this.lastActive !== false){
22808                     var last = this.last;
22809                     this.selectRange(this.last,  this.lastActive-1);
22810                     this.grid.getView().focusRow(this.lastActive);
22811                     if(last !== false){
22812                         this.last = last;
22813                     }
22814                 }else{
22815                     this.selectFirstRow();
22816                 }
22817                 this.fireEvent("afterselectionchange", this);
22818             },
22819             "down" : function(e){
22820                 if(!e.shiftKey){
22821                     this.selectNext(e.shiftKey);
22822                 }else if(this.last !== false && this.lastActive !== false){
22823                     var last = this.last;
22824                     this.selectRange(this.last,  this.lastActive+1);
22825                     this.grid.getView().focusRow(this.lastActive);
22826                     if(last !== false){
22827                         this.last = last;
22828                     }
22829                 }else{
22830                     this.selectFirstRow();
22831                 }
22832                 this.fireEvent("afterselectionchange", this);
22833             },
22834             scope: this
22835         });
22836         this.grid.store.on('load', function(){
22837             this.selections.clear();
22838         },this);
22839         /*
22840         var view = this.grid.view;
22841         view.on("refresh", this.onRefresh, this);
22842         view.on("rowupdated", this.onRowUpdated, this);
22843         view.on("rowremoved", this.onRemove, this);
22844         */
22845     },
22846
22847     // private
22848     onRefresh : function()
22849     {
22850         var ds = this.grid.store, i, v = this.grid.view;
22851         var s = this.selections;
22852         s.each(function(r){
22853             if((i = ds.indexOfId(r.id)) != -1){
22854                 v.onRowSelect(i);
22855             }else{
22856                 s.remove(r);
22857             }
22858         });
22859     },
22860
22861     // private
22862     onRemove : function(v, index, r){
22863         this.selections.remove(r);
22864     },
22865
22866     // private
22867     onRowUpdated : function(v, index, r){
22868         if(this.isSelected(r)){
22869             v.onRowSelect(index);
22870         }
22871     },
22872
22873     /**
22874      * Select records.
22875      * @param {Array} records The records to select
22876      * @param {Boolean} keepExisting (optional) True to keep existing selections
22877      */
22878     selectRecords : function(records, keepExisting)
22879     {
22880         if(!keepExisting){
22881             this.clearSelections();
22882         }
22883             var ds = this.grid.store;
22884         for(var i = 0, len = records.length; i < len; i++){
22885             this.selectRow(ds.indexOf(records[i]), true);
22886         }
22887     },
22888
22889     /**
22890      * Gets the number of selected rows.
22891      * @return {Number}
22892      */
22893     getCount : function(){
22894         return this.selections.length;
22895     },
22896
22897     /**
22898      * Selects the first row in the grid.
22899      */
22900     selectFirstRow : function(){
22901         this.selectRow(0);
22902     },
22903
22904     /**
22905      * Select the last row.
22906      * @param {Boolean} keepExisting (optional) True to keep existing selections
22907      */
22908     selectLastRow : function(keepExisting){
22909         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22910         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
22911     },
22912
22913     /**
22914      * Selects the row immediately following the last selected row.
22915      * @param {Boolean} keepExisting (optional) True to keep existing selections
22916      */
22917     selectNext : function(keepExisting)
22918     {
22919             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
22920             this.selectRow(this.last+1, keepExisting);
22921             this.grid.getView().focusRow(this.last);
22922         }
22923     },
22924
22925     /**
22926      * Selects the row that precedes the last selected row.
22927      * @param {Boolean} keepExisting (optional) True to keep existing selections
22928      */
22929     selectPrevious : function(keepExisting){
22930         if(this.last){
22931             this.selectRow(this.last-1, keepExisting);
22932             this.grid.getView().focusRow(this.last);
22933         }
22934     },
22935
22936     /**
22937      * Returns the selected records
22938      * @return {Array} Array of selected records
22939      */
22940     getSelections : function(){
22941         return [].concat(this.selections.items);
22942     },
22943
22944     /**
22945      * Returns the first selected record.
22946      * @return {Record}
22947      */
22948     getSelected : function(){
22949         return this.selections.itemAt(0);
22950     },
22951
22952
22953     /**
22954      * Clears all selections.
22955      */
22956     clearSelections : function(fast)
22957     {
22958         if(this.locked) {
22959             return;
22960         }
22961         if(fast !== true){
22962                 var ds = this.grid.store;
22963             var s = this.selections;
22964             s.each(function(r){
22965                 this.deselectRow(ds.indexOfId(r.id));
22966             }, this);
22967             s.clear();
22968         }else{
22969             this.selections.clear();
22970         }
22971         this.last = false;
22972     },
22973
22974
22975     /**
22976      * Selects all rows.
22977      */
22978     selectAll : function(){
22979         if(this.locked) {
22980             return;
22981         }
22982         this.selections.clear();
22983         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
22984             this.selectRow(i, true);
22985         }
22986     },
22987
22988     /**
22989      * Returns True if there is a selection.
22990      * @return {Boolean}
22991      */
22992     hasSelection : function(){
22993         return this.selections.length > 0;
22994     },
22995
22996     /**
22997      * Returns True if the specified row is selected.
22998      * @param {Number/Record} record The record or index of the record to check
22999      * @return {Boolean}
23000      */
23001     isSelected : function(index){
23002             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23003         return (r && this.selections.key(r.id) ? true : false);
23004     },
23005
23006     /**
23007      * Returns True if the specified record id is selected.
23008      * @param {String} id The id of record to check
23009      * @return {Boolean}
23010      */
23011     isIdSelected : function(id){
23012         return (this.selections.key(id) ? true : false);
23013     },
23014
23015
23016     // private
23017     handleMouseDBClick : function(e, t){
23018         
23019     },
23020     // private
23021     handleMouseDown : function(e, t)
23022     {
23023             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23024         if(this.isLocked() || rowIndex < 0 ){
23025             return;
23026         };
23027         if(e.shiftKey && this.last !== false){
23028             var last = this.last;
23029             this.selectRange(last, rowIndex, e.ctrlKey);
23030             this.last = last; // reset the last
23031             t.focus();
23032     
23033         }else{
23034             var isSelected = this.isSelected(rowIndex);
23035             //Roo.log("select row:" + rowIndex);
23036             if(isSelected){
23037                 this.deselectRow(rowIndex);
23038             } else {
23039                         this.selectRow(rowIndex, true);
23040             }
23041     
23042             /*
23043                 if(e.button !== 0 && isSelected){
23044                 alert('rowIndex 2: ' + rowIndex);
23045                     view.focusRow(rowIndex);
23046                 }else if(e.ctrlKey && isSelected){
23047                     this.deselectRow(rowIndex);
23048                 }else if(!isSelected){
23049                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23050                     view.focusRow(rowIndex);
23051                 }
23052             */
23053         }
23054         this.fireEvent("afterselectionchange", this);
23055     },
23056     // private
23057     handleDragableRowClick :  function(grid, rowIndex, e) 
23058     {
23059         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23060             this.selectRow(rowIndex, false);
23061             grid.view.focusRow(rowIndex);
23062              this.fireEvent("afterselectionchange", this);
23063         }
23064     },
23065     
23066     /**
23067      * Selects multiple rows.
23068      * @param {Array} rows Array of the indexes of the row to select
23069      * @param {Boolean} keepExisting (optional) True to keep existing selections
23070      */
23071     selectRows : function(rows, keepExisting){
23072         if(!keepExisting){
23073             this.clearSelections();
23074         }
23075         for(var i = 0, len = rows.length; i < len; i++){
23076             this.selectRow(rows[i], true);
23077         }
23078     },
23079
23080     /**
23081      * Selects a range of rows. All rows in between startRow and endRow are also selected.
23082      * @param {Number} startRow The index of the first row in the range
23083      * @param {Number} endRow The index of the last row in the range
23084      * @param {Boolean} keepExisting (optional) True to retain existing selections
23085      */
23086     selectRange : function(startRow, endRow, keepExisting){
23087         if(this.locked) {
23088             return;
23089         }
23090         if(!keepExisting){
23091             this.clearSelections();
23092         }
23093         if(startRow <= endRow){
23094             for(var i = startRow; i <= endRow; i++){
23095                 this.selectRow(i, true);
23096             }
23097         }else{
23098             for(var i = startRow; i >= endRow; i--){
23099                 this.selectRow(i, true);
23100             }
23101         }
23102     },
23103
23104     /**
23105      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
23106      * @param {Number} startRow The index of the first row in the range
23107      * @param {Number} endRow The index of the last row in the range
23108      */
23109     deselectRange : function(startRow, endRow, preventViewNotify){
23110         if(this.locked) {
23111             return;
23112         }
23113         for(var i = startRow; i <= endRow; i++){
23114             this.deselectRow(i, preventViewNotify);
23115         }
23116     },
23117
23118     /**
23119      * Selects a row.
23120      * @param {Number} row The index of the row to select
23121      * @param {Boolean} keepExisting (optional) True to keep existing selections
23122      */
23123     selectRow : function(index, keepExisting, preventViewNotify)
23124     {
23125             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
23126             return;
23127         }
23128         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
23129             if(!keepExisting || this.singleSelect){
23130                 this.clearSelections();
23131             }
23132             
23133             var r = this.grid.store.getAt(index);
23134             //console.log('selectRow - record id :' + r.id);
23135             
23136             this.selections.add(r);
23137             this.last = this.lastActive = index;
23138             if(!preventViewNotify){
23139                 var proxy = new Roo.Element(
23140                                 this.grid.getRowDom(index)
23141                 );
23142                 proxy.addClass('bg-info info');
23143             }
23144             this.fireEvent("rowselect", this, index, r);
23145             this.fireEvent("selectionchange", this);
23146         }
23147     },
23148
23149     /**
23150      * Deselects a row.
23151      * @param {Number} row The index of the row to deselect
23152      */
23153     deselectRow : function(index, preventViewNotify)
23154     {
23155         if(this.locked) {
23156             return;
23157         }
23158         if(this.last == index){
23159             this.last = false;
23160         }
23161         if(this.lastActive == index){
23162             this.lastActive = false;
23163         }
23164         
23165         var r = this.grid.store.getAt(index);
23166         if (!r) {
23167             return;
23168         }
23169         
23170         this.selections.remove(r);
23171         //.console.log('deselectRow - record id :' + r.id);
23172         if(!preventViewNotify){
23173         
23174             var proxy = new Roo.Element(
23175                 this.grid.getRowDom(index)
23176             );
23177             proxy.removeClass('bg-info info');
23178         }
23179         this.fireEvent("rowdeselect", this, index);
23180         this.fireEvent("selectionchange", this);
23181     },
23182
23183     // private
23184     restoreLast : function(){
23185         if(this._last){
23186             this.last = this._last;
23187         }
23188     },
23189
23190     // private
23191     acceptsNav : function(row, col, cm){
23192         return !cm.isHidden(col) && cm.isCellEditable(col, row);
23193     },
23194
23195     // private
23196     onEditorKey : function(field, e){
23197         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
23198         if(k == e.TAB){
23199             e.stopEvent();
23200             ed.completeEdit();
23201             if(e.shiftKey){
23202                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
23203             }else{
23204                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
23205             }
23206         }else if(k == e.ENTER && !e.ctrlKey){
23207             e.stopEvent();
23208             ed.completeEdit();
23209             if(e.shiftKey){
23210                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
23211             }else{
23212                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
23213             }
23214         }else if(k == e.ESC){
23215             ed.cancelEdit();
23216         }
23217         if(newCell){
23218             g.startEditing(newCell[0], newCell[1]);
23219         }
23220     }
23221 });
23222 /*
23223  * Based on:
23224  * Ext JS Library 1.1.1
23225  * Copyright(c) 2006-2007, Ext JS, LLC.
23226  *
23227  * Originally Released Under LGPL - original licence link has changed is not relivant.
23228  *
23229  * Fork - LGPL
23230  * <script type="text/javascript">
23231  */
23232  
23233 /**
23234  * @class Roo.bootstrap.PagingToolbar
23235  * @extends Roo.bootstrap.NavSimplebar
23236  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
23237  * @constructor
23238  * Create a new PagingToolbar
23239  * @param {Object} config The config object
23240  * @param {Roo.data.Store} store
23241  */
23242 Roo.bootstrap.PagingToolbar = function(config)
23243 {
23244     // old args format still supported... - xtype is prefered..
23245         // created from xtype...
23246     
23247     this.ds = config.dataSource;
23248     
23249     if (config.store && !this.ds) {
23250         this.store= Roo.factory(config.store, Roo.data);
23251         this.ds = this.store;
23252         this.ds.xmodule = this.xmodule || false;
23253     }
23254     
23255     this.toolbarItems = [];
23256     if (config.items) {
23257         this.toolbarItems = config.items;
23258     }
23259     
23260     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
23261     
23262     this.cursor = 0;
23263     
23264     if (this.ds) { 
23265         this.bind(this.ds);
23266     }
23267     
23268     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
23269     
23270 };
23271
23272 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
23273     /**
23274      * @cfg {Roo.data.Store} dataSource
23275      * The underlying data store providing the paged data
23276      */
23277     /**
23278      * @cfg {String/HTMLElement/Element} container
23279      * container The id or element that will contain the toolbar
23280      */
23281     /**
23282      * @cfg {Boolean} displayInfo
23283      * True to display the displayMsg (defaults to false)
23284      */
23285     /**
23286      * @cfg {Number} pageSize
23287      * The number of records to display per page (defaults to 20)
23288      */
23289     pageSize: 20,
23290     /**
23291      * @cfg {String} displayMsg
23292      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
23293      */
23294     displayMsg : 'Displaying {0} - {1} of {2}',
23295     /**
23296      * @cfg {String} emptyMsg
23297      * The message to display when no records are found (defaults to "No data to display")
23298      */
23299     emptyMsg : 'No data to display',
23300     /**
23301      * Customizable piece of the default paging text (defaults to "Page")
23302      * @type String
23303      */
23304     beforePageText : "Page",
23305     /**
23306      * Customizable piece of the default paging text (defaults to "of %0")
23307      * @type String
23308      */
23309     afterPageText : "of {0}",
23310     /**
23311      * Customizable piece of the default paging text (defaults to "First Page")
23312      * @type String
23313      */
23314     firstText : "First Page",
23315     /**
23316      * Customizable piece of the default paging text (defaults to "Previous Page")
23317      * @type String
23318      */
23319     prevText : "Previous Page",
23320     /**
23321      * Customizable piece of the default paging text (defaults to "Next Page")
23322      * @type String
23323      */
23324     nextText : "Next Page",
23325     /**
23326      * Customizable piece of the default paging text (defaults to "Last Page")
23327      * @type String
23328      */
23329     lastText : "Last Page",
23330     /**
23331      * Customizable piece of the default paging text (defaults to "Refresh")
23332      * @type String
23333      */
23334     refreshText : "Refresh",
23335
23336     buttons : false,
23337     // private
23338     onRender : function(ct, position) 
23339     {
23340         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
23341         this.navgroup.parentId = this.id;
23342         this.navgroup.onRender(this.el, null);
23343         // add the buttons to the navgroup
23344         
23345         if(this.displayInfo){
23346             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
23347             this.displayEl = this.el.select('.x-paging-info', true).first();
23348 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
23349 //            this.displayEl = navel.el.select('span',true).first();
23350         }
23351         
23352         var _this = this;
23353         
23354         if(this.buttons){
23355             Roo.each(_this.buttons, function(e){ // this might need to use render????
23356                Roo.factory(e).onRender(_this.el, null);
23357             });
23358         }
23359             
23360         Roo.each(_this.toolbarItems, function(e) {
23361             _this.navgroup.addItem(e);
23362         });
23363         
23364         
23365         this.first = this.navgroup.addItem({
23366             tooltip: this.firstText,
23367             cls: "prev",
23368             icon : 'fa fa-backward',
23369             disabled: true,
23370             preventDefault: true,
23371             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
23372         });
23373         
23374         this.prev =  this.navgroup.addItem({
23375             tooltip: this.prevText,
23376             cls: "prev",
23377             icon : 'fa fa-step-backward',
23378             disabled: true,
23379             preventDefault: true,
23380             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
23381         });
23382     //this.addSeparator();
23383         
23384         
23385         var field = this.navgroup.addItem( {
23386             tagtype : 'span',
23387             cls : 'x-paging-position',
23388             
23389             html : this.beforePageText  +
23390                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
23391                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
23392          } ); //?? escaped?
23393         
23394         this.field = field.el.select('input', true).first();
23395         this.field.on("keydown", this.onPagingKeydown, this);
23396         this.field.on("focus", function(){this.dom.select();});
23397     
23398     
23399         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
23400         //this.field.setHeight(18);
23401         //this.addSeparator();
23402         this.next = this.navgroup.addItem({
23403             tooltip: this.nextText,
23404             cls: "next",
23405             html : ' <i class="fa fa-step-forward">',
23406             disabled: true,
23407             preventDefault: true,
23408             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
23409         });
23410         this.last = this.navgroup.addItem({
23411             tooltip: this.lastText,
23412             icon : 'fa fa-forward',
23413             cls: "next",
23414             disabled: true,
23415             preventDefault: true,
23416             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
23417         });
23418     //this.addSeparator();
23419         this.loading = this.navgroup.addItem({
23420             tooltip: this.refreshText,
23421             icon: 'fa fa-refresh',
23422             preventDefault: true,
23423             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
23424         });
23425         
23426     },
23427
23428     // private
23429     updateInfo : function(){
23430         if(this.displayEl){
23431             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
23432             var msg = count == 0 ?
23433                 this.emptyMsg :
23434                 String.format(
23435                     this.displayMsg,
23436                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
23437                 );
23438             this.displayEl.update(msg);
23439         }
23440     },
23441
23442     // private
23443     onLoad : function(ds, r, o){
23444        this.cursor = o.params ? o.params.start : 0;
23445        var d = this.getPageData(),
23446             ap = d.activePage,
23447             ps = d.pages;
23448         
23449        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
23450        this.field.dom.value = ap;
23451        this.first.setDisabled(ap == 1);
23452        this.prev.setDisabled(ap == 1);
23453        this.next.setDisabled(ap == ps);
23454        this.last.setDisabled(ap == ps);
23455        this.loading.enable();
23456        this.updateInfo();
23457     },
23458
23459     // private
23460     getPageData : function(){
23461         var total = this.ds.getTotalCount();
23462         return {
23463             total : total,
23464             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
23465             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
23466         };
23467     },
23468
23469     // private
23470     onLoadError : function(){
23471         this.loading.enable();
23472     },
23473
23474     // private
23475     onPagingKeydown : function(e){
23476         var k = e.getKey();
23477         var d = this.getPageData();
23478         if(k == e.RETURN){
23479             var v = this.field.dom.value, pageNum;
23480             if(!v || isNaN(pageNum = parseInt(v, 10))){
23481                 this.field.dom.value = d.activePage;
23482                 return;
23483             }
23484             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
23485             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23486             e.stopEvent();
23487         }
23488         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))
23489         {
23490           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
23491           this.field.dom.value = pageNum;
23492           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
23493           e.stopEvent();
23494         }
23495         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
23496         {
23497           var v = this.field.dom.value, pageNum; 
23498           var increment = (e.shiftKey) ? 10 : 1;
23499           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
23500                 increment *= -1;
23501           }
23502           if(!v || isNaN(pageNum = parseInt(v, 10))) {
23503             this.field.dom.value = d.activePage;
23504             return;
23505           }
23506           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
23507           {
23508             this.field.dom.value = parseInt(v, 10) + increment;
23509             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
23510             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23511           }
23512           e.stopEvent();
23513         }
23514     },
23515
23516     // private
23517     beforeLoad : function(){
23518         if(this.loading){
23519             this.loading.disable();
23520         }
23521     },
23522
23523     // private
23524     onClick : function(which){
23525         
23526         var ds = this.ds;
23527         if (!ds) {
23528             return;
23529         }
23530         
23531         switch(which){
23532             case "first":
23533                 ds.load({params:{start: 0, limit: this.pageSize}});
23534             break;
23535             case "prev":
23536                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
23537             break;
23538             case "next":
23539                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
23540             break;
23541             case "last":
23542                 var total = ds.getTotalCount();
23543                 var extra = total % this.pageSize;
23544                 var lastStart = extra ? (total - extra) : total-this.pageSize;
23545                 ds.load({params:{start: lastStart, limit: this.pageSize}});
23546             break;
23547             case "refresh":
23548                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
23549             break;
23550         }
23551     },
23552
23553     /**
23554      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
23555      * @param {Roo.data.Store} store The data store to unbind
23556      */
23557     unbind : function(ds){
23558         ds.un("beforeload", this.beforeLoad, this);
23559         ds.un("load", this.onLoad, this);
23560         ds.un("loadexception", this.onLoadError, this);
23561         ds.un("remove", this.updateInfo, this);
23562         ds.un("add", this.updateInfo, this);
23563         this.ds = undefined;
23564     },
23565
23566     /**
23567      * Binds the paging toolbar to the specified {@link Roo.data.Store}
23568      * @param {Roo.data.Store} store The data store to bind
23569      */
23570     bind : function(ds){
23571         ds.on("beforeload", this.beforeLoad, this);
23572         ds.on("load", this.onLoad, this);
23573         ds.on("loadexception", this.onLoadError, this);
23574         ds.on("remove", this.updateInfo, this);
23575         ds.on("add", this.updateInfo, this);
23576         this.ds = ds;
23577     }
23578 });/*
23579  * - LGPL
23580  *
23581  * element
23582  * 
23583  */
23584
23585 /**
23586  * @class Roo.bootstrap.MessageBar
23587  * @extends Roo.bootstrap.Component
23588  * Bootstrap MessageBar class
23589  * @cfg {String} html contents of the MessageBar
23590  * @cfg {String} weight (info | success | warning | danger) default info
23591  * @cfg {String} beforeClass insert the bar before the given class
23592  * @cfg {Boolean} closable (true | false) default false
23593  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
23594  * 
23595  * @constructor
23596  * Create a new Element
23597  * @param {Object} config The config object
23598  */
23599
23600 Roo.bootstrap.MessageBar = function(config){
23601     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
23602 };
23603
23604 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
23605     
23606     html: '',
23607     weight: 'info',
23608     closable: false,
23609     fixed: false,
23610     beforeClass: 'bootstrap-sticky-wrap',
23611     
23612     getAutoCreate : function(){
23613         
23614         var cfg = {
23615             tag: 'div',
23616             cls: 'alert alert-dismissable alert-' + this.weight,
23617             cn: [
23618                 {
23619                     tag: 'span',
23620                     cls: 'message',
23621                     html: this.html || ''
23622                 }
23623             ]
23624         };
23625         
23626         if(this.fixed){
23627             cfg.cls += ' alert-messages-fixed';
23628         }
23629         
23630         if(this.closable){
23631             cfg.cn.push({
23632                 tag: 'button',
23633                 cls: 'close',
23634                 html: 'x'
23635             });
23636         }
23637         
23638         return cfg;
23639     },
23640     
23641     onRender : function(ct, position)
23642     {
23643         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
23644         
23645         if(!this.el){
23646             var cfg = Roo.apply({},  this.getAutoCreate());
23647             cfg.id = Roo.id();
23648             
23649             if (this.cls) {
23650                 cfg.cls += ' ' + this.cls;
23651             }
23652             if (this.style) {
23653                 cfg.style = this.style;
23654             }
23655             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
23656             
23657             this.el.setVisibilityMode(Roo.Element.DISPLAY);
23658         }
23659         
23660         this.el.select('>button.close').on('click', this.hide, this);
23661         
23662     },
23663     
23664     show : function()
23665     {
23666         if (!this.rendered) {
23667             this.render();
23668         }
23669         
23670         this.el.show();
23671         
23672         this.fireEvent('show', this);
23673         
23674     },
23675     
23676     hide : function()
23677     {
23678         if (!this.rendered) {
23679             this.render();
23680         }
23681         
23682         this.el.hide();
23683         
23684         this.fireEvent('hide', this);
23685     },
23686     
23687     update : function()
23688     {
23689 //        var e = this.el.dom.firstChild;
23690 //        
23691 //        if(this.closable){
23692 //            e = e.nextSibling;
23693 //        }
23694 //        
23695 //        e.data = this.html || '';
23696
23697         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
23698     }
23699    
23700 });
23701
23702  
23703
23704      /*
23705  * - LGPL
23706  *
23707  * Graph
23708  * 
23709  */
23710
23711
23712 /**
23713  * @class Roo.bootstrap.Graph
23714  * @extends Roo.bootstrap.Component
23715  * Bootstrap Graph class
23716 > Prameters
23717  -sm {number} sm 4
23718  -md {number} md 5
23719  @cfg {String} graphtype  bar | vbar | pie
23720  @cfg {number} g_x coodinator | centre x (pie)
23721  @cfg {number} g_y coodinator | centre y (pie)
23722  @cfg {number} g_r radius (pie)
23723  @cfg {number} g_height height of the chart (respected by all elements in the set)
23724  @cfg {number} g_width width of the chart (respected by all elements in the set)
23725  @cfg {Object} title The title of the chart
23726     
23727  -{Array}  values
23728  -opts (object) options for the chart 
23729      o {
23730      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
23731      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
23732      o vgutter (number)
23733      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.
23734      o stacked (boolean) whether or not to tread values as in a stacked bar chart
23735      o to
23736      o stretch (boolean)
23737      o }
23738  -opts (object) options for the pie
23739      o{
23740      o cut
23741      o startAngle (number)
23742      o endAngle (number)
23743      } 
23744  *
23745  * @constructor
23746  * Create a new Input
23747  * @param {Object} config The config object
23748  */
23749
23750 Roo.bootstrap.Graph = function(config){
23751     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
23752     
23753     this.addEvents({
23754         // img events
23755         /**
23756          * @event click
23757          * The img click event for the img.
23758          * @param {Roo.EventObject} e
23759          */
23760         "click" : true
23761     });
23762 };
23763
23764 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
23765     
23766     sm: 4,
23767     md: 5,
23768     graphtype: 'bar',
23769     g_height: 250,
23770     g_width: 400,
23771     g_x: 50,
23772     g_y: 50,
23773     g_r: 30,
23774     opts:{
23775         //g_colors: this.colors,
23776         g_type: 'soft',
23777         g_gutter: '20%'
23778
23779     },
23780     title : false,
23781
23782     getAutoCreate : function(){
23783         
23784         var cfg = {
23785             tag: 'div',
23786             html : null
23787         };
23788         
23789         
23790         return  cfg;
23791     },
23792
23793     onRender : function(ct,position){
23794         
23795         
23796         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23797         
23798         if (typeof(Raphael) == 'undefined') {
23799             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23800             return;
23801         }
23802         
23803         this.raphael = Raphael(this.el.dom);
23804         
23805                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23806                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23807                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23808                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23809                 /*
23810                 r.text(160, 10, "Single Series Chart").attr(txtattr);
23811                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23812                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23813                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23814                 
23815                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23816                 r.barchart(330, 10, 300, 220, data1);
23817                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23818                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23819                 */
23820                 
23821                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23822                 // r.barchart(30, 30, 560, 250,  xdata, {
23823                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23824                 //     axis : "0 0 1 1",
23825                 //     axisxlabels :  xdata
23826                 //     //yvalues : cols,
23827                    
23828                 // });
23829 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23830 //        
23831 //        this.load(null,xdata,{
23832 //                axis : "0 0 1 1",
23833 //                axisxlabels :  xdata
23834 //                });
23835
23836     },
23837
23838     load : function(graphtype,xdata,opts)
23839     {
23840         this.raphael.clear();
23841         if(!graphtype) {
23842             graphtype = this.graphtype;
23843         }
23844         if(!opts){
23845             opts = this.opts;
23846         }
23847         var r = this.raphael,
23848             fin = function () {
23849                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23850             },
23851             fout = function () {
23852                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23853             },
23854             pfin = function() {
23855                 this.sector.stop();
23856                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23857
23858                 if (this.label) {
23859                     this.label[0].stop();
23860                     this.label[0].attr({ r: 7.5 });
23861                     this.label[1].attr({ "font-weight": 800 });
23862                 }
23863             },
23864             pfout = function() {
23865                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23866
23867                 if (this.label) {
23868                     this.label[0].animate({ r: 5 }, 500, "bounce");
23869                     this.label[1].attr({ "font-weight": 400 });
23870                 }
23871             };
23872
23873         switch(graphtype){
23874             case 'bar':
23875                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23876                 break;
23877             case 'hbar':
23878                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23879                 break;
23880             case 'pie':
23881 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
23882 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23883 //            
23884                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23885                 
23886                 break;
23887
23888         }
23889         
23890         if(this.title){
23891             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23892         }
23893         
23894     },
23895     
23896     setTitle: function(o)
23897     {
23898         this.title = o;
23899     },
23900     
23901     initEvents: function() {
23902         
23903         if(!this.href){
23904             this.el.on('click', this.onClick, this);
23905         }
23906     },
23907     
23908     onClick : function(e)
23909     {
23910         Roo.log('img onclick');
23911         this.fireEvent('click', this, e);
23912     }
23913    
23914 });
23915
23916  
23917 /*
23918  * - LGPL
23919  *
23920  * numberBox
23921  * 
23922  */
23923 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23924
23925 /**
23926  * @class Roo.bootstrap.dash.NumberBox
23927  * @extends Roo.bootstrap.Component
23928  * Bootstrap NumberBox class
23929  * @cfg {String} headline Box headline
23930  * @cfg {String} content Box content
23931  * @cfg {String} icon Box icon
23932  * @cfg {String} footer Footer text
23933  * @cfg {String} fhref Footer href
23934  * 
23935  * @constructor
23936  * Create a new NumberBox
23937  * @param {Object} config The config object
23938  */
23939
23940
23941 Roo.bootstrap.dash.NumberBox = function(config){
23942     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23943     
23944 };
23945
23946 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
23947     
23948     headline : '',
23949     content : '',
23950     icon : '',
23951     footer : '',
23952     fhref : '',
23953     ficon : '',
23954     
23955     getAutoCreate : function(){
23956         
23957         var cfg = {
23958             tag : 'div',
23959             cls : 'small-box ',
23960             cn : [
23961                 {
23962                     tag : 'div',
23963                     cls : 'inner',
23964                     cn :[
23965                         {
23966                             tag : 'h3',
23967                             cls : 'roo-headline',
23968                             html : this.headline
23969                         },
23970                         {
23971                             tag : 'p',
23972                             cls : 'roo-content',
23973                             html : this.content
23974                         }
23975                     ]
23976                 }
23977             ]
23978         };
23979         
23980         if(this.icon){
23981             cfg.cn.push({
23982                 tag : 'div',
23983                 cls : 'icon',
23984                 cn :[
23985                     {
23986                         tag : 'i',
23987                         cls : 'ion ' + this.icon
23988                     }
23989                 ]
23990             });
23991         }
23992         
23993         if(this.footer){
23994             var footer = {
23995                 tag : 'a',
23996                 cls : 'small-box-footer',
23997                 href : this.fhref || '#',
23998                 html : this.footer
23999             };
24000             
24001             cfg.cn.push(footer);
24002             
24003         }
24004         
24005         return  cfg;
24006     },
24007
24008     onRender : function(ct,position){
24009         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24010
24011
24012        
24013                 
24014     },
24015
24016     setHeadline: function (value)
24017     {
24018         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24019     },
24020     
24021     setFooter: function (value, href)
24022     {
24023         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24024         
24025         if(href){
24026             this.el.select('a.small-box-footer',true).first().attr('href', href);
24027         }
24028         
24029     },
24030
24031     setContent: function (value)
24032     {
24033         this.el.select('.roo-content',true).first().dom.innerHTML = value;
24034     },
24035
24036     initEvents: function() 
24037     {   
24038         
24039     }
24040     
24041 });
24042
24043  
24044 /*
24045  * - LGPL
24046  *
24047  * TabBox
24048  * 
24049  */
24050 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24051
24052 /**
24053  * @class Roo.bootstrap.dash.TabBox
24054  * @extends Roo.bootstrap.Component
24055  * Bootstrap TabBox class
24056  * @cfg {String} title Title of the TabBox
24057  * @cfg {String} icon Icon of the TabBox
24058  * @cfg {Boolean} showtabs (true|false) show the tabs default true
24059  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24060  * 
24061  * @constructor
24062  * Create a new TabBox
24063  * @param {Object} config The config object
24064  */
24065
24066
24067 Roo.bootstrap.dash.TabBox = function(config){
24068     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24069     this.addEvents({
24070         // raw events
24071         /**
24072          * @event addpane
24073          * When a pane is added
24074          * @param {Roo.bootstrap.dash.TabPane} pane
24075          */
24076         "addpane" : true,
24077         /**
24078          * @event activatepane
24079          * When a pane is activated
24080          * @param {Roo.bootstrap.dash.TabPane} pane
24081          */
24082         "activatepane" : true
24083         
24084          
24085     });
24086     
24087     this.panes = [];
24088 };
24089
24090 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
24091
24092     title : '',
24093     icon : false,
24094     showtabs : true,
24095     tabScrollable : false,
24096     
24097     getChildContainer : function()
24098     {
24099         return this.el.select('.tab-content', true).first();
24100     },
24101     
24102     getAutoCreate : function(){
24103         
24104         var header = {
24105             tag: 'li',
24106             cls: 'pull-left header',
24107             html: this.title,
24108             cn : []
24109         };
24110         
24111         if(this.icon){
24112             header.cn.push({
24113                 tag: 'i',
24114                 cls: 'fa ' + this.icon
24115             });
24116         }
24117         
24118         var h = {
24119             tag: 'ul',
24120             cls: 'nav nav-tabs pull-right',
24121             cn: [
24122                 header
24123             ]
24124         };
24125         
24126         if(this.tabScrollable){
24127             h = {
24128                 tag: 'div',
24129                 cls: 'tab-header',
24130                 cn: [
24131                     {
24132                         tag: 'ul',
24133                         cls: 'nav nav-tabs pull-right',
24134                         cn: [
24135                             header
24136                         ]
24137                     }
24138                 ]
24139             };
24140         }
24141         
24142         var cfg = {
24143             tag: 'div',
24144             cls: 'nav-tabs-custom',
24145             cn: [
24146                 h,
24147                 {
24148                     tag: 'div',
24149                     cls: 'tab-content no-padding',
24150                     cn: []
24151                 }
24152             ]
24153         };
24154
24155         return  cfg;
24156     },
24157     initEvents : function()
24158     {
24159         //Roo.log('add add pane handler');
24160         this.on('addpane', this.onAddPane, this);
24161     },
24162      /**
24163      * Updates the box title
24164      * @param {String} html to set the title to.
24165      */
24166     setTitle : function(value)
24167     {
24168         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
24169     },
24170     onAddPane : function(pane)
24171     {
24172         this.panes.push(pane);
24173         //Roo.log('addpane');
24174         //Roo.log(pane);
24175         // tabs are rendere left to right..
24176         if(!this.showtabs){
24177             return;
24178         }
24179         
24180         var ctr = this.el.select('.nav-tabs', true).first();
24181          
24182          
24183         var existing = ctr.select('.nav-tab',true);
24184         var qty = existing.getCount();;
24185         
24186         
24187         var tab = ctr.createChild({
24188             tag : 'li',
24189             cls : 'nav-tab' + (qty ? '' : ' active'),
24190             cn : [
24191                 {
24192                     tag : 'a',
24193                     href:'#',
24194                     html : pane.title
24195                 }
24196             ]
24197         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
24198         pane.tab = tab;
24199         
24200         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
24201         if (!qty) {
24202             pane.el.addClass('active');
24203         }
24204         
24205                 
24206     },
24207     onTabClick : function(ev,un,ob,pane)
24208     {
24209         //Roo.log('tab - prev default');
24210         ev.preventDefault();
24211         
24212         
24213         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
24214         pane.tab.addClass('active');
24215         //Roo.log(pane.title);
24216         this.getChildContainer().select('.tab-pane',true).removeClass('active');
24217         // technically we should have a deactivate event.. but maybe add later.
24218         // and it should not de-activate the selected tab...
24219         this.fireEvent('activatepane', pane);
24220         pane.el.addClass('active');
24221         pane.fireEvent('activate');
24222         
24223         
24224     },
24225     
24226     getActivePane : function()
24227     {
24228         var r = false;
24229         Roo.each(this.panes, function(p) {
24230             if(p.el.hasClass('active')){
24231                 r = p;
24232                 return false;
24233             }
24234             
24235             return;
24236         });
24237         
24238         return r;
24239     }
24240     
24241     
24242 });
24243
24244  
24245 /*
24246  * - LGPL
24247  *
24248  * Tab pane
24249  * 
24250  */
24251 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24252 /**
24253  * @class Roo.bootstrap.TabPane
24254  * @extends Roo.bootstrap.Component
24255  * Bootstrap TabPane class
24256  * @cfg {Boolean} active (false | true) Default false
24257  * @cfg {String} title title of panel
24258
24259  * 
24260  * @constructor
24261  * Create a new TabPane
24262  * @param {Object} config The config object
24263  */
24264
24265 Roo.bootstrap.dash.TabPane = function(config){
24266     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
24267     
24268     this.addEvents({
24269         // raw events
24270         /**
24271          * @event activate
24272          * When a pane is activated
24273          * @param {Roo.bootstrap.dash.TabPane} pane
24274          */
24275         "activate" : true
24276          
24277     });
24278 };
24279
24280 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
24281     
24282     active : false,
24283     title : '',
24284     
24285     // the tabBox that this is attached to.
24286     tab : false,
24287      
24288     getAutoCreate : function() 
24289     {
24290         var cfg = {
24291             tag: 'div',
24292             cls: 'tab-pane'
24293         };
24294         
24295         if(this.active){
24296             cfg.cls += ' active';
24297         }
24298         
24299         return cfg;
24300     },
24301     initEvents  : function()
24302     {
24303         //Roo.log('trigger add pane handler');
24304         this.parent().fireEvent('addpane', this)
24305     },
24306     
24307      /**
24308      * Updates the tab title 
24309      * @param {String} html to set the title to.
24310      */
24311     setTitle: function(str)
24312     {
24313         if (!this.tab) {
24314             return;
24315         }
24316         this.title = str;
24317         this.tab.select('a', true).first().dom.innerHTML = str;
24318         
24319     }
24320     
24321     
24322     
24323 });
24324
24325  
24326
24327
24328  /*
24329  * - LGPL
24330  *
24331  * menu
24332  * 
24333  */
24334 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24335
24336 /**
24337  * @class Roo.bootstrap.menu.Menu
24338  * @extends Roo.bootstrap.Component
24339  * Bootstrap Menu class - container for Menu
24340  * @cfg {String} html Text of the menu
24341  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
24342  * @cfg {String} icon Font awesome icon
24343  * @cfg {String} pos Menu align to (top | bottom) default bottom
24344  * 
24345  * 
24346  * @constructor
24347  * Create a new Menu
24348  * @param {Object} config The config object
24349  */
24350
24351
24352 Roo.bootstrap.menu.Menu = function(config){
24353     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
24354     
24355     this.addEvents({
24356         /**
24357          * @event beforeshow
24358          * Fires before this menu is displayed
24359          * @param {Roo.bootstrap.menu.Menu} this
24360          */
24361         beforeshow : true,
24362         /**
24363          * @event beforehide
24364          * Fires before this menu is hidden
24365          * @param {Roo.bootstrap.menu.Menu} this
24366          */
24367         beforehide : true,
24368         /**
24369          * @event show
24370          * Fires after this menu is displayed
24371          * @param {Roo.bootstrap.menu.Menu} this
24372          */
24373         show : true,
24374         /**
24375          * @event hide
24376          * Fires after this menu is hidden
24377          * @param {Roo.bootstrap.menu.Menu} this
24378          */
24379         hide : true,
24380         /**
24381          * @event click
24382          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
24383          * @param {Roo.bootstrap.menu.Menu} this
24384          * @param {Roo.EventObject} e
24385          */
24386         click : true
24387     });
24388     
24389 };
24390
24391 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
24392     
24393     submenu : false,
24394     html : '',
24395     weight : 'default',
24396     icon : false,
24397     pos : 'bottom',
24398     
24399     
24400     getChildContainer : function() {
24401         if(this.isSubMenu){
24402             return this.el;
24403         }
24404         
24405         return this.el.select('ul.dropdown-menu', true).first();  
24406     },
24407     
24408     getAutoCreate : function()
24409     {
24410         var text = [
24411             {
24412                 tag : 'span',
24413                 cls : 'roo-menu-text',
24414                 html : this.html
24415             }
24416         ];
24417         
24418         if(this.icon){
24419             text.unshift({
24420                 tag : 'i',
24421                 cls : 'fa ' + this.icon
24422             })
24423         }
24424         
24425         
24426         var cfg = {
24427             tag : 'div',
24428             cls : 'btn-group',
24429             cn : [
24430                 {
24431                     tag : 'button',
24432                     cls : 'dropdown-button btn btn-' + this.weight,
24433                     cn : text
24434                 },
24435                 {
24436                     tag : 'button',
24437                     cls : 'dropdown-toggle btn btn-' + this.weight,
24438                     cn : [
24439                         {
24440                             tag : 'span',
24441                             cls : 'caret'
24442                         }
24443                     ]
24444                 },
24445                 {
24446                     tag : 'ul',
24447                     cls : 'dropdown-menu'
24448                 }
24449             ]
24450             
24451         };
24452         
24453         if(this.pos == 'top'){
24454             cfg.cls += ' dropup';
24455         }
24456         
24457         if(this.isSubMenu){
24458             cfg = {
24459                 tag : 'ul',
24460                 cls : 'dropdown-menu'
24461             }
24462         }
24463         
24464         return cfg;
24465     },
24466     
24467     onRender : function(ct, position)
24468     {
24469         this.isSubMenu = ct.hasClass('dropdown-submenu');
24470         
24471         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
24472     },
24473     
24474     initEvents : function() 
24475     {
24476         if(this.isSubMenu){
24477             return;
24478         }
24479         
24480         this.hidden = true;
24481         
24482         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
24483         this.triggerEl.on('click', this.onTriggerPress, this);
24484         
24485         this.buttonEl = this.el.select('button.dropdown-button', true).first();
24486         this.buttonEl.on('click', this.onClick, this);
24487         
24488     },
24489     
24490     list : function()
24491     {
24492         if(this.isSubMenu){
24493             return this.el;
24494         }
24495         
24496         return this.el.select('ul.dropdown-menu', true).first();
24497     },
24498     
24499     onClick : function(e)
24500     {
24501         this.fireEvent("click", this, e);
24502     },
24503     
24504     onTriggerPress  : function(e)
24505     {   
24506         if (this.isVisible()) {
24507             this.hide();
24508         } else {
24509             this.show();
24510         }
24511     },
24512     
24513     isVisible : function(){
24514         return !this.hidden;
24515     },
24516     
24517     show : function()
24518     {
24519         this.fireEvent("beforeshow", this);
24520         
24521         this.hidden = false;
24522         this.el.addClass('open');
24523         
24524         Roo.get(document).on("mouseup", this.onMouseUp, this);
24525         
24526         this.fireEvent("show", this);
24527         
24528         
24529     },
24530     
24531     hide : function()
24532     {
24533         this.fireEvent("beforehide", this);
24534         
24535         this.hidden = true;
24536         this.el.removeClass('open');
24537         
24538         Roo.get(document).un("mouseup", this.onMouseUp);
24539         
24540         this.fireEvent("hide", this);
24541     },
24542     
24543     onMouseUp : function()
24544     {
24545         this.hide();
24546     }
24547     
24548 });
24549
24550  
24551  /*
24552  * - LGPL
24553  *
24554  * menu item
24555  * 
24556  */
24557 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24558
24559 /**
24560  * @class Roo.bootstrap.menu.Item
24561  * @extends Roo.bootstrap.Component
24562  * Bootstrap MenuItem class
24563  * @cfg {Boolean} submenu (true | false) default false
24564  * @cfg {String} html text of the item
24565  * @cfg {String} href the link
24566  * @cfg {Boolean} disable (true | false) default false
24567  * @cfg {Boolean} preventDefault (true | false) default true
24568  * @cfg {String} icon Font awesome icon
24569  * @cfg {String} pos Submenu align to (left | right) default right 
24570  * 
24571  * 
24572  * @constructor
24573  * Create a new Item
24574  * @param {Object} config The config object
24575  */
24576
24577
24578 Roo.bootstrap.menu.Item = function(config){
24579     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
24580     this.addEvents({
24581         /**
24582          * @event mouseover
24583          * Fires when the mouse is hovering over this menu
24584          * @param {Roo.bootstrap.menu.Item} this
24585          * @param {Roo.EventObject} e
24586          */
24587         mouseover : true,
24588         /**
24589          * @event mouseout
24590          * Fires when the mouse exits this menu
24591          * @param {Roo.bootstrap.menu.Item} this
24592          * @param {Roo.EventObject} e
24593          */
24594         mouseout : true,
24595         // raw events
24596         /**
24597          * @event click
24598          * The raw click event for the entire grid.
24599          * @param {Roo.EventObject} e
24600          */
24601         click : true
24602     });
24603 };
24604
24605 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
24606     
24607     submenu : false,
24608     href : '',
24609     html : '',
24610     preventDefault: true,
24611     disable : false,
24612     icon : false,
24613     pos : 'right',
24614     
24615     getAutoCreate : function()
24616     {
24617         var text = [
24618             {
24619                 tag : 'span',
24620                 cls : 'roo-menu-item-text',
24621                 html : this.html
24622             }
24623         ];
24624         
24625         if(this.icon){
24626             text.unshift({
24627                 tag : 'i',
24628                 cls : 'fa ' + this.icon
24629             })
24630         }
24631         
24632         var cfg = {
24633             tag : 'li',
24634             cn : [
24635                 {
24636                     tag : 'a',
24637                     href : this.href || '#',
24638                     cn : text
24639                 }
24640             ]
24641         };
24642         
24643         if(this.disable){
24644             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
24645         }
24646         
24647         if(this.submenu){
24648             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
24649             
24650             if(this.pos == 'left'){
24651                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
24652             }
24653         }
24654         
24655         return cfg;
24656     },
24657     
24658     initEvents : function() 
24659     {
24660         this.el.on('mouseover', this.onMouseOver, this);
24661         this.el.on('mouseout', this.onMouseOut, this);
24662         
24663         this.el.select('a', true).first().on('click', this.onClick, this);
24664         
24665     },
24666     
24667     onClick : function(e)
24668     {
24669         if(this.preventDefault){
24670             e.preventDefault();
24671         }
24672         
24673         this.fireEvent("click", this, e);
24674     },
24675     
24676     onMouseOver : function(e)
24677     {
24678         if(this.submenu && this.pos == 'left'){
24679             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
24680         }
24681         
24682         this.fireEvent("mouseover", this, e);
24683     },
24684     
24685     onMouseOut : function(e)
24686     {
24687         this.fireEvent("mouseout", this, e);
24688     }
24689 });
24690
24691  
24692
24693  /*
24694  * - LGPL
24695  *
24696  * menu separator
24697  * 
24698  */
24699 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24700
24701 /**
24702  * @class Roo.bootstrap.menu.Separator
24703  * @extends Roo.bootstrap.Component
24704  * Bootstrap Separator class
24705  * 
24706  * @constructor
24707  * Create a new Separator
24708  * @param {Object} config The config object
24709  */
24710
24711
24712 Roo.bootstrap.menu.Separator = function(config){
24713     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
24714 };
24715
24716 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
24717     
24718     getAutoCreate : function(){
24719         var cfg = {
24720             tag : 'li',
24721             cls: 'divider'
24722         };
24723         
24724         return cfg;
24725     }
24726    
24727 });
24728
24729  
24730
24731  /*
24732  * - LGPL
24733  *
24734  * Tooltip
24735  * 
24736  */
24737
24738 /**
24739  * @class Roo.bootstrap.Tooltip
24740  * Bootstrap Tooltip class
24741  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
24742  * to determine which dom element triggers the tooltip.
24743  * 
24744  * It needs to add support for additional attributes like tooltip-position
24745  * 
24746  * @constructor
24747  * Create a new Toolti
24748  * @param {Object} config The config object
24749  */
24750
24751 Roo.bootstrap.Tooltip = function(config){
24752     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
24753 };
24754
24755 Roo.apply(Roo.bootstrap.Tooltip, {
24756     /**
24757      * @function init initialize tooltip monitoring.
24758      * @static
24759      */
24760     currentEl : false,
24761     currentTip : false,
24762     currentRegion : false,
24763     
24764     //  init : delay?
24765     
24766     init : function()
24767     {
24768         Roo.get(document).on('mouseover', this.enter ,this);
24769         Roo.get(document).on('mouseout', this.leave, this);
24770          
24771         
24772         this.currentTip = new Roo.bootstrap.Tooltip();
24773     },
24774     
24775     enter : function(ev)
24776     {
24777         var dom = ev.getTarget();
24778         
24779         //Roo.log(['enter',dom]);
24780         var el = Roo.fly(dom);
24781         if (this.currentEl) {
24782             //Roo.log(dom);
24783             //Roo.log(this.currentEl);
24784             //Roo.log(this.currentEl.contains(dom));
24785             if (this.currentEl == el) {
24786                 return;
24787             }
24788             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24789                 return;
24790             }
24791
24792         }
24793         
24794         if (this.currentTip.el) {
24795             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24796         }    
24797         //Roo.log(ev);
24798         
24799         if(!el || el.dom == document){
24800             return;
24801         }
24802         
24803         var bindEl = el;
24804         
24805         // you can not look for children, as if el is the body.. then everythign is the child..
24806         if (!el.attr('tooltip')) { //
24807             if (!el.select("[tooltip]").elements.length) {
24808                 return;
24809             }
24810             // is the mouse over this child...?
24811             bindEl = el.select("[tooltip]").first();
24812             var xy = ev.getXY();
24813             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24814                 //Roo.log("not in region.");
24815                 return;
24816             }
24817             //Roo.log("child element over..");
24818             
24819         }
24820         this.currentEl = bindEl;
24821         this.currentTip.bind(bindEl);
24822         this.currentRegion = Roo.lib.Region.getRegion(dom);
24823         this.currentTip.enter();
24824         
24825     },
24826     leave : function(ev)
24827     {
24828         var dom = ev.getTarget();
24829         //Roo.log(['leave',dom]);
24830         if (!this.currentEl) {
24831             return;
24832         }
24833         
24834         
24835         if (dom != this.currentEl.dom) {
24836             return;
24837         }
24838         var xy = ev.getXY();
24839         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
24840             return;
24841         }
24842         // only activate leave if mouse cursor is outside... bounding box..
24843         
24844         
24845         
24846         
24847         if (this.currentTip) {
24848             this.currentTip.leave();
24849         }
24850         //Roo.log('clear currentEl');
24851         this.currentEl = false;
24852         
24853         
24854     },
24855     alignment : {
24856         'left' : ['r-l', [-2,0], 'right'],
24857         'right' : ['l-r', [2,0], 'left'],
24858         'bottom' : ['t-b', [0,2], 'top'],
24859         'top' : [ 'b-t', [0,-2], 'bottom']
24860     }
24861     
24862 });
24863
24864
24865 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
24866     
24867     
24868     bindEl : false,
24869     
24870     delay : null, // can be { show : 300 , hide: 500}
24871     
24872     timeout : null,
24873     
24874     hoverState : null, //???
24875     
24876     placement : 'bottom', 
24877     
24878     getAutoCreate : function(){
24879     
24880         var cfg = {
24881            cls : 'tooltip',
24882            role : 'tooltip',
24883            cn : [
24884                 {
24885                     cls : 'tooltip-arrow'
24886                 },
24887                 {
24888                     cls : 'tooltip-inner'
24889                 }
24890            ]
24891         };
24892         
24893         return cfg;
24894     },
24895     bind : function(el)
24896     {
24897         this.bindEl = el;
24898     },
24899       
24900     
24901     enter : function () {
24902        
24903         if (this.timeout != null) {
24904             clearTimeout(this.timeout);
24905         }
24906         
24907         this.hoverState = 'in';
24908          //Roo.log("enter - show");
24909         if (!this.delay || !this.delay.show) {
24910             this.show();
24911             return;
24912         }
24913         var _t = this;
24914         this.timeout = setTimeout(function () {
24915             if (_t.hoverState == 'in') {
24916                 _t.show();
24917             }
24918         }, this.delay.show);
24919     },
24920     leave : function()
24921     {
24922         clearTimeout(this.timeout);
24923     
24924         this.hoverState = 'out';
24925          if (!this.delay || !this.delay.hide) {
24926             this.hide();
24927             return;
24928         }
24929        
24930         var _t = this;
24931         this.timeout = setTimeout(function () {
24932             //Roo.log("leave - timeout");
24933             
24934             if (_t.hoverState == 'out') {
24935                 _t.hide();
24936                 Roo.bootstrap.Tooltip.currentEl = false;
24937             }
24938         }, delay);
24939     },
24940     
24941     show : function ()
24942     {
24943         if (!this.el) {
24944             this.render(document.body);
24945         }
24946         // set content.
24947         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24948         
24949         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24950         
24951         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24952         
24953         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24954         
24955         var placement = typeof this.placement == 'function' ?
24956             this.placement.call(this, this.el, on_el) :
24957             this.placement;
24958             
24959         var autoToken = /\s?auto?\s?/i;
24960         var autoPlace = autoToken.test(placement);
24961         if (autoPlace) {
24962             placement = placement.replace(autoToken, '') || 'top';
24963         }
24964         
24965         //this.el.detach()
24966         //this.el.setXY([0,0]);
24967         this.el.show();
24968         //this.el.dom.style.display='block';
24969         
24970         //this.el.appendTo(on_el);
24971         
24972         var p = this.getPosition();
24973         var box = this.el.getBox();
24974         
24975         if (autoPlace) {
24976             // fixme..
24977         }
24978         
24979         var align = Roo.bootstrap.Tooltip.alignment[placement];
24980         
24981         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
24982         
24983         if(placement == 'top' || placement == 'bottom'){
24984             if(xy[0] < 0){
24985                 placement = 'right';
24986             }
24987             
24988             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
24989                 placement = 'left';
24990             }
24991             
24992             var scroll = Roo.select('body', true).first().getScroll();
24993             
24994             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
24995                 placement = 'top';
24996             }
24997             
24998         }
24999         
25000         align = Roo.bootstrap.Tooltip.alignment[placement];
25001         
25002         this.el.alignTo(this.bindEl, align[0],align[1]);
25003         //var arrow = this.el.select('.arrow',true).first();
25004         //arrow.set(align[2], 
25005         
25006         this.el.addClass(placement);
25007         
25008         this.el.addClass('in fade');
25009         
25010         this.hoverState = null;
25011         
25012         if (this.el.hasClass('fade')) {
25013             // fade it?
25014         }
25015         
25016     },
25017     hide : function()
25018     {
25019          
25020         if (!this.el) {
25021             return;
25022         }
25023         //this.el.setXY([0,0]);
25024         this.el.removeClass('in');
25025         //this.el.hide();
25026         
25027     }
25028     
25029 });
25030  
25031
25032  /*
25033  * - LGPL
25034  *
25035  * Location Picker
25036  * 
25037  */
25038
25039 /**
25040  * @class Roo.bootstrap.LocationPicker
25041  * @extends Roo.bootstrap.Component
25042  * Bootstrap LocationPicker class
25043  * @cfg {Number} latitude Position when init default 0
25044  * @cfg {Number} longitude Position when init default 0
25045  * @cfg {Number} zoom default 15
25046  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25047  * @cfg {Boolean} mapTypeControl default false
25048  * @cfg {Boolean} disableDoubleClickZoom default false
25049  * @cfg {Boolean} scrollwheel default true
25050  * @cfg {Boolean} streetViewControl default false
25051  * @cfg {Number} radius default 0
25052  * @cfg {String} locationName
25053  * @cfg {Boolean} draggable default true
25054  * @cfg {Boolean} enableAutocomplete default false
25055  * @cfg {Boolean} enableReverseGeocode default true
25056  * @cfg {String} markerTitle
25057  * 
25058  * @constructor
25059  * Create a new LocationPicker
25060  * @param {Object} config The config object
25061  */
25062
25063
25064 Roo.bootstrap.LocationPicker = function(config){
25065     
25066     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25067     
25068     this.addEvents({
25069         /**
25070          * @event initial
25071          * Fires when the picker initialized.
25072          * @param {Roo.bootstrap.LocationPicker} this
25073          * @param {Google Location} location
25074          */
25075         initial : true,
25076         /**
25077          * @event positionchanged
25078          * Fires when the picker position changed.
25079          * @param {Roo.bootstrap.LocationPicker} this
25080          * @param {Google Location} location
25081          */
25082         positionchanged : true,
25083         /**
25084          * @event resize
25085          * Fires when the map resize.
25086          * @param {Roo.bootstrap.LocationPicker} this
25087          */
25088         resize : true,
25089         /**
25090          * @event show
25091          * Fires when the map show.
25092          * @param {Roo.bootstrap.LocationPicker} this
25093          */
25094         show : true,
25095         /**
25096          * @event hide
25097          * Fires when the map hide.
25098          * @param {Roo.bootstrap.LocationPicker} this
25099          */
25100         hide : true,
25101         /**
25102          * @event mapClick
25103          * Fires when click the map.
25104          * @param {Roo.bootstrap.LocationPicker} this
25105          * @param {Map event} e
25106          */
25107         mapClick : true,
25108         /**
25109          * @event mapRightClick
25110          * Fires when right click the map.
25111          * @param {Roo.bootstrap.LocationPicker} this
25112          * @param {Map event} e
25113          */
25114         mapRightClick : true,
25115         /**
25116          * @event markerClick
25117          * Fires when click the marker.
25118          * @param {Roo.bootstrap.LocationPicker} this
25119          * @param {Map event} e
25120          */
25121         markerClick : true,
25122         /**
25123          * @event markerRightClick
25124          * Fires when right click the marker.
25125          * @param {Roo.bootstrap.LocationPicker} this
25126          * @param {Map event} e
25127          */
25128         markerRightClick : true,
25129         /**
25130          * @event OverlayViewDraw
25131          * Fires when OverlayView Draw
25132          * @param {Roo.bootstrap.LocationPicker} this
25133          */
25134         OverlayViewDraw : true,
25135         /**
25136          * @event OverlayViewOnAdd
25137          * Fires when OverlayView Draw
25138          * @param {Roo.bootstrap.LocationPicker} this
25139          */
25140         OverlayViewOnAdd : true,
25141         /**
25142          * @event OverlayViewOnRemove
25143          * Fires when OverlayView Draw
25144          * @param {Roo.bootstrap.LocationPicker} this
25145          */
25146         OverlayViewOnRemove : true,
25147         /**
25148          * @event OverlayViewShow
25149          * Fires when OverlayView Draw
25150          * @param {Roo.bootstrap.LocationPicker} this
25151          * @param {Pixel} cpx
25152          */
25153         OverlayViewShow : true,
25154         /**
25155          * @event OverlayViewHide
25156          * Fires when OverlayView Draw
25157          * @param {Roo.bootstrap.LocationPicker} this
25158          */
25159         OverlayViewHide : true,
25160         /**
25161          * @event loadexception
25162          * Fires when load google lib failed.
25163          * @param {Roo.bootstrap.LocationPicker} this
25164          */
25165         loadexception : true
25166     });
25167         
25168 };
25169
25170 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
25171     
25172     gMapContext: false,
25173     
25174     latitude: 0,
25175     longitude: 0,
25176     zoom: 15,
25177     mapTypeId: false,
25178     mapTypeControl: false,
25179     disableDoubleClickZoom: false,
25180     scrollwheel: true,
25181     streetViewControl: false,
25182     radius: 0,
25183     locationName: '',
25184     draggable: true,
25185     enableAutocomplete: false,
25186     enableReverseGeocode: true,
25187     markerTitle: '',
25188     
25189     getAutoCreate: function()
25190     {
25191
25192         var cfg = {
25193             tag: 'div',
25194             cls: 'roo-location-picker'
25195         };
25196         
25197         return cfg
25198     },
25199     
25200     initEvents: function(ct, position)
25201     {       
25202         if(!this.el.getWidth() || this.isApplied()){
25203             return;
25204         }
25205         
25206         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25207         
25208         this.initial();
25209     },
25210     
25211     initial: function()
25212     {
25213         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
25214             this.fireEvent('loadexception', this);
25215             return;
25216         }
25217         
25218         if(!this.mapTypeId){
25219             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
25220         }
25221         
25222         this.gMapContext = this.GMapContext();
25223         
25224         this.initOverlayView();
25225         
25226         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
25227         
25228         var _this = this;
25229                 
25230         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
25231             _this.setPosition(_this.gMapContext.marker.position);
25232         });
25233         
25234         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
25235             _this.fireEvent('mapClick', this, event);
25236             
25237         });
25238
25239         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
25240             _this.fireEvent('mapRightClick', this, event);
25241             
25242         });
25243         
25244         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
25245             _this.fireEvent('markerClick', this, event);
25246             
25247         });
25248
25249         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
25250             _this.fireEvent('markerRightClick', this, event);
25251             
25252         });
25253         
25254         this.setPosition(this.gMapContext.location);
25255         
25256         this.fireEvent('initial', this, this.gMapContext.location);
25257     },
25258     
25259     initOverlayView: function()
25260     {
25261         var _this = this;
25262         
25263         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
25264             
25265             draw: function()
25266             {
25267                 _this.fireEvent('OverlayViewDraw', _this);
25268             },
25269             
25270             onAdd: function()
25271             {
25272                 _this.fireEvent('OverlayViewOnAdd', _this);
25273             },
25274             
25275             onRemove: function()
25276             {
25277                 _this.fireEvent('OverlayViewOnRemove', _this);
25278             },
25279             
25280             show: function(cpx)
25281             {
25282                 _this.fireEvent('OverlayViewShow', _this, cpx);
25283             },
25284             
25285             hide: function()
25286             {
25287                 _this.fireEvent('OverlayViewHide', _this);
25288             }
25289             
25290         });
25291     },
25292     
25293     fromLatLngToContainerPixel: function(event)
25294     {
25295         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
25296     },
25297     
25298     isApplied: function() 
25299     {
25300         return this.getGmapContext() == false ? false : true;
25301     },
25302     
25303     getGmapContext: function() 
25304     {
25305         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
25306     },
25307     
25308     GMapContext: function() 
25309     {
25310         var position = new google.maps.LatLng(this.latitude, this.longitude);
25311         
25312         var _map = new google.maps.Map(this.el.dom, {
25313             center: position,
25314             zoom: this.zoom,
25315             mapTypeId: this.mapTypeId,
25316             mapTypeControl: this.mapTypeControl,
25317             disableDoubleClickZoom: this.disableDoubleClickZoom,
25318             scrollwheel: this.scrollwheel,
25319             streetViewControl: this.streetViewControl,
25320             locationName: this.locationName,
25321             draggable: this.draggable,
25322             enableAutocomplete: this.enableAutocomplete,
25323             enableReverseGeocode: this.enableReverseGeocode
25324         });
25325         
25326         var _marker = new google.maps.Marker({
25327             position: position,
25328             map: _map,
25329             title: this.markerTitle,
25330             draggable: this.draggable
25331         });
25332         
25333         return {
25334             map: _map,
25335             marker: _marker,
25336             circle: null,
25337             location: position,
25338             radius: this.radius,
25339             locationName: this.locationName,
25340             addressComponents: {
25341                 formatted_address: null,
25342                 addressLine1: null,
25343                 addressLine2: null,
25344                 streetName: null,
25345                 streetNumber: null,
25346                 city: null,
25347                 district: null,
25348                 state: null,
25349                 stateOrProvince: null
25350             },
25351             settings: this,
25352             domContainer: this.el.dom,
25353             geodecoder: new google.maps.Geocoder()
25354         };
25355     },
25356     
25357     drawCircle: function(center, radius, options) 
25358     {
25359         if (this.gMapContext.circle != null) {
25360             this.gMapContext.circle.setMap(null);
25361         }
25362         if (radius > 0) {
25363             radius *= 1;
25364             options = Roo.apply({}, options, {
25365                 strokeColor: "#0000FF",
25366                 strokeOpacity: .35,
25367                 strokeWeight: 2,
25368                 fillColor: "#0000FF",
25369                 fillOpacity: .2
25370             });
25371             
25372             options.map = this.gMapContext.map;
25373             options.radius = radius;
25374             options.center = center;
25375             this.gMapContext.circle = new google.maps.Circle(options);
25376             return this.gMapContext.circle;
25377         }
25378         
25379         return null;
25380     },
25381     
25382     setPosition: function(location) 
25383     {
25384         this.gMapContext.location = location;
25385         this.gMapContext.marker.setPosition(location);
25386         this.gMapContext.map.panTo(location);
25387         this.drawCircle(location, this.gMapContext.radius, {});
25388         
25389         var _this = this;
25390         
25391         if (this.gMapContext.settings.enableReverseGeocode) {
25392             this.gMapContext.geodecoder.geocode({
25393                 latLng: this.gMapContext.location
25394             }, function(results, status) {
25395                 
25396                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
25397                     _this.gMapContext.locationName = results[0].formatted_address;
25398                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
25399                     
25400                     _this.fireEvent('positionchanged', this, location);
25401                 }
25402             });
25403             
25404             return;
25405         }
25406         
25407         this.fireEvent('positionchanged', this, location);
25408     },
25409     
25410     resize: function()
25411     {
25412         google.maps.event.trigger(this.gMapContext.map, "resize");
25413         
25414         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
25415         
25416         this.fireEvent('resize', this);
25417     },
25418     
25419     setPositionByLatLng: function(latitude, longitude)
25420     {
25421         this.setPosition(new google.maps.LatLng(latitude, longitude));
25422     },
25423     
25424     getCurrentPosition: function() 
25425     {
25426         return {
25427             latitude: this.gMapContext.location.lat(),
25428             longitude: this.gMapContext.location.lng()
25429         };
25430     },
25431     
25432     getAddressName: function() 
25433     {
25434         return this.gMapContext.locationName;
25435     },
25436     
25437     getAddressComponents: function() 
25438     {
25439         return this.gMapContext.addressComponents;
25440     },
25441     
25442     address_component_from_google_geocode: function(address_components) 
25443     {
25444         var result = {};
25445         
25446         for (var i = 0; i < address_components.length; i++) {
25447             var component = address_components[i];
25448             if (component.types.indexOf("postal_code") >= 0) {
25449                 result.postalCode = component.short_name;
25450             } else if (component.types.indexOf("street_number") >= 0) {
25451                 result.streetNumber = component.short_name;
25452             } else if (component.types.indexOf("route") >= 0) {
25453                 result.streetName = component.short_name;
25454             } else if (component.types.indexOf("neighborhood") >= 0) {
25455                 result.city = component.short_name;
25456             } else if (component.types.indexOf("locality") >= 0) {
25457                 result.city = component.short_name;
25458             } else if (component.types.indexOf("sublocality") >= 0) {
25459                 result.district = component.short_name;
25460             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
25461                 result.stateOrProvince = component.short_name;
25462             } else if (component.types.indexOf("country") >= 0) {
25463                 result.country = component.short_name;
25464             }
25465         }
25466         
25467         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
25468         result.addressLine2 = "";
25469         return result;
25470     },
25471     
25472     setZoomLevel: function(zoom)
25473     {
25474         this.gMapContext.map.setZoom(zoom);
25475     },
25476     
25477     show: function()
25478     {
25479         if(!this.el){
25480             return;
25481         }
25482         
25483         this.el.show();
25484         
25485         this.resize();
25486         
25487         this.fireEvent('show', this);
25488     },
25489     
25490     hide: function()
25491     {
25492         if(!this.el){
25493             return;
25494         }
25495         
25496         this.el.hide();
25497         
25498         this.fireEvent('hide', this);
25499     }
25500     
25501 });
25502
25503 Roo.apply(Roo.bootstrap.LocationPicker, {
25504     
25505     OverlayView : function(map, options)
25506     {
25507         options = options || {};
25508         
25509         this.setMap(map);
25510     }
25511     
25512     
25513 });/*
25514  * - LGPL
25515  *
25516  * Alert
25517  * 
25518  */
25519
25520 /**
25521  * @class Roo.bootstrap.Alert
25522  * @extends Roo.bootstrap.Component
25523  * Bootstrap Alert class
25524  * @cfg {String} title The title of alert
25525  * @cfg {String} html The content of alert
25526  * @cfg {String} weight (  success | info | warning | danger )
25527  * @cfg {String} faicon font-awesomeicon
25528  * 
25529  * @constructor
25530  * Create a new alert
25531  * @param {Object} config The config object
25532  */
25533
25534
25535 Roo.bootstrap.Alert = function(config){
25536     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
25537     
25538 };
25539
25540 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
25541     
25542     title: '',
25543     html: '',
25544     weight: false,
25545     faicon: false,
25546     
25547     getAutoCreate : function()
25548     {
25549         
25550         var cfg = {
25551             tag : 'div',
25552             cls : 'alert',
25553             cn : [
25554                 {
25555                     tag : 'i',
25556                     cls : 'roo-alert-icon'
25557                     
25558                 },
25559                 {
25560                     tag : 'b',
25561                     cls : 'roo-alert-title',
25562                     html : this.title
25563                 },
25564                 {
25565                     tag : 'span',
25566                     cls : 'roo-alert-text',
25567                     html : this.html
25568                 }
25569             ]
25570         };
25571         
25572         if(this.faicon){
25573             cfg.cn[0].cls += ' fa ' + this.faicon;
25574         }
25575         
25576         if(this.weight){
25577             cfg.cls += ' alert-' + this.weight;
25578         }
25579         
25580         return cfg;
25581     },
25582     
25583     initEvents: function() 
25584     {
25585         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25586     },
25587     
25588     setTitle : function(str)
25589     {
25590         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
25591     },
25592     
25593     setText : function(str)
25594     {
25595         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
25596     },
25597     
25598     setWeight : function(weight)
25599     {
25600         if(this.weight){
25601             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
25602         }
25603         
25604         this.weight = weight;
25605         
25606         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
25607     },
25608     
25609     setIcon : function(icon)
25610     {
25611         if(this.faicon){
25612             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
25613         }
25614         
25615         this.faicon = icon;
25616         
25617         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
25618     },
25619     
25620     hide: function() 
25621     {
25622         this.el.hide();   
25623     },
25624     
25625     show: function() 
25626     {  
25627         this.el.show();   
25628     }
25629     
25630 });
25631
25632  
25633 /*
25634 * Licence: LGPL
25635 */
25636
25637 /**
25638  * @class Roo.bootstrap.UploadCropbox
25639  * @extends Roo.bootstrap.Component
25640  * Bootstrap UploadCropbox class
25641  * @cfg {String} emptyText show when image has been loaded
25642  * @cfg {String} rotateNotify show when image too small to rotate
25643  * @cfg {Number} errorTimeout default 3000
25644  * @cfg {Number} minWidth default 300
25645  * @cfg {Number} minHeight default 300
25646  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
25647  * @cfg {Boolean} isDocument (true|false) default false
25648  * @cfg {String} url action url
25649  * @cfg {String} paramName default 'imageUpload'
25650  * @cfg {String} method default POST
25651  * @cfg {Boolean} loadMask (true|false) default true
25652  * @cfg {Boolean} loadingText default 'Loading...'
25653  * 
25654  * @constructor
25655  * Create a new UploadCropbox
25656  * @param {Object} config The config object
25657  */
25658
25659 Roo.bootstrap.UploadCropbox = function(config){
25660     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
25661     
25662     this.addEvents({
25663         /**
25664          * @event beforeselectfile
25665          * Fire before select file
25666          * @param {Roo.bootstrap.UploadCropbox} this
25667          */
25668         "beforeselectfile" : true,
25669         /**
25670          * @event initial
25671          * Fire after initEvent
25672          * @param {Roo.bootstrap.UploadCropbox} this
25673          */
25674         "initial" : true,
25675         /**
25676          * @event crop
25677          * Fire after initEvent
25678          * @param {Roo.bootstrap.UploadCropbox} this
25679          * @param {String} data
25680          */
25681         "crop" : true,
25682         /**
25683          * @event prepare
25684          * Fire when preparing the file data
25685          * @param {Roo.bootstrap.UploadCropbox} this
25686          * @param {Object} file
25687          */
25688         "prepare" : true,
25689         /**
25690          * @event exception
25691          * Fire when get exception
25692          * @param {Roo.bootstrap.UploadCropbox} this
25693          * @param {XMLHttpRequest} xhr
25694          */
25695         "exception" : true,
25696         /**
25697          * @event beforeloadcanvas
25698          * Fire before load the canvas
25699          * @param {Roo.bootstrap.UploadCropbox} this
25700          * @param {String} src
25701          */
25702         "beforeloadcanvas" : true,
25703         /**
25704          * @event trash
25705          * Fire when trash image
25706          * @param {Roo.bootstrap.UploadCropbox} this
25707          */
25708         "trash" : true,
25709         /**
25710          * @event download
25711          * Fire when download the image
25712          * @param {Roo.bootstrap.UploadCropbox} this
25713          */
25714         "download" : true,
25715         /**
25716          * @event footerbuttonclick
25717          * Fire when footerbuttonclick
25718          * @param {Roo.bootstrap.UploadCropbox} this
25719          * @param {String} type
25720          */
25721         "footerbuttonclick" : true,
25722         /**
25723          * @event resize
25724          * Fire when resize
25725          * @param {Roo.bootstrap.UploadCropbox} this
25726          */
25727         "resize" : true,
25728         /**
25729          * @event rotate
25730          * Fire when rotate the image
25731          * @param {Roo.bootstrap.UploadCropbox} this
25732          * @param {String} pos
25733          */
25734         "rotate" : true,
25735         /**
25736          * @event inspect
25737          * Fire when inspect the file
25738          * @param {Roo.bootstrap.UploadCropbox} this
25739          * @param {Object} file
25740          */
25741         "inspect" : true,
25742         /**
25743          * @event upload
25744          * Fire when xhr upload the file
25745          * @param {Roo.bootstrap.UploadCropbox} this
25746          * @param {Object} data
25747          */
25748         "upload" : true,
25749         /**
25750          * @event arrange
25751          * Fire when arrange the file data
25752          * @param {Roo.bootstrap.UploadCropbox} this
25753          * @param {Object} formData
25754          */
25755         "arrange" : true
25756     });
25757     
25758     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25759 };
25760
25761 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
25762     
25763     emptyText : 'Click to upload image',
25764     rotateNotify : 'Image is too small to rotate',
25765     errorTimeout : 3000,
25766     scale : 0,
25767     baseScale : 1,
25768     rotate : 0,
25769     dragable : false,
25770     pinching : false,
25771     mouseX : 0,
25772     mouseY : 0,
25773     cropData : false,
25774     minWidth : 300,
25775     minHeight : 300,
25776     file : false,
25777     exif : {},
25778     baseRotate : 1,
25779     cropType : 'image/jpeg',
25780     buttons : false,
25781     canvasLoaded : false,
25782     isDocument : false,
25783     method : 'POST',
25784     paramName : 'imageUpload',
25785     loadMask : true,
25786     loadingText : 'Loading...',
25787     maskEl : false,
25788     
25789     getAutoCreate : function()
25790     {
25791         var cfg = {
25792             tag : 'div',
25793             cls : 'roo-upload-cropbox',
25794             cn : [
25795                 {
25796                     tag : 'input',
25797                     cls : 'roo-upload-cropbox-selector',
25798                     type : 'file'
25799                 },
25800                 {
25801                     tag : 'div',
25802                     cls : 'roo-upload-cropbox-body',
25803                     style : 'cursor:pointer',
25804                     cn : [
25805                         {
25806                             tag : 'div',
25807                             cls : 'roo-upload-cropbox-preview'
25808                         },
25809                         {
25810                             tag : 'div',
25811                             cls : 'roo-upload-cropbox-thumb'
25812                         },
25813                         {
25814                             tag : 'div',
25815                             cls : 'roo-upload-cropbox-empty-notify',
25816                             html : this.emptyText
25817                         },
25818                         {
25819                             tag : 'div',
25820                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25821                             html : this.rotateNotify
25822                         }
25823                     ]
25824                 },
25825                 {
25826                     tag : 'div',
25827                     cls : 'roo-upload-cropbox-footer',
25828                     cn : {
25829                         tag : 'div',
25830                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25831                         cn : []
25832                     }
25833                 }
25834             ]
25835         };
25836         
25837         return cfg;
25838     },
25839     
25840     onRender : function(ct, position)
25841     {
25842         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25843         
25844         if (this.buttons.length) {
25845             
25846             Roo.each(this.buttons, function(bb) {
25847                 
25848                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25849                 
25850                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25851                 
25852             }, this);
25853         }
25854         
25855         if(this.loadMask){
25856             this.maskEl = this.el;
25857         }
25858     },
25859     
25860     initEvents : function()
25861     {
25862         this.urlAPI = (window.createObjectURL && window) || 
25863                                 (window.URL && URL.revokeObjectURL && URL) || 
25864                                 (window.webkitURL && webkitURL);
25865                         
25866         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25867         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25868         
25869         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25870         this.selectorEl.hide();
25871         
25872         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25873         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25874         
25875         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25876         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25877         this.thumbEl.hide();
25878         
25879         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25880         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25881         
25882         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25883         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25884         this.errorEl.hide();
25885         
25886         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25887         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25888         this.footerEl.hide();
25889         
25890         this.setThumbBoxSize();
25891         
25892         this.bind();
25893         
25894         this.resize();
25895         
25896         this.fireEvent('initial', this);
25897     },
25898
25899     bind : function()
25900     {
25901         var _this = this;
25902         
25903         window.addEventListener("resize", function() { _this.resize(); } );
25904         
25905         this.bodyEl.on('click', this.beforeSelectFile, this);
25906         
25907         if(Roo.isTouch){
25908             this.bodyEl.on('touchstart', this.onTouchStart, this);
25909             this.bodyEl.on('touchmove', this.onTouchMove, this);
25910             this.bodyEl.on('touchend', this.onTouchEnd, this);
25911         }
25912         
25913         if(!Roo.isTouch){
25914             this.bodyEl.on('mousedown', this.onMouseDown, this);
25915             this.bodyEl.on('mousemove', this.onMouseMove, this);
25916             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25917             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25918             Roo.get(document).on('mouseup', this.onMouseUp, this);
25919         }
25920         
25921         this.selectorEl.on('change', this.onFileSelected, this);
25922     },
25923     
25924     reset : function()
25925     {    
25926         this.scale = 0;
25927         this.baseScale = 1;
25928         this.rotate = 0;
25929         this.baseRotate = 1;
25930         this.dragable = false;
25931         this.pinching = false;
25932         this.mouseX = 0;
25933         this.mouseY = 0;
25934         this.cropData = false;
25935         this.notifyEl.dom.innerHTML = this.emptyText;
25936         
25937         this.selectorEl.dom.value = '';
25938         
25939     },
25940     
25941     resize : function()
25942     {
25943         if(this.fireEvent('resize', this) != false){
25944             this.setThumbBoxPosition();
25945             this.setCanvasPosition();
25946         }
25947     },
25948     
25949     onFooterButtonClick : function(e, el, o, type)
25950     {
25951         switch (type) {
25952             case 'rotate-left' :
25953                 this.onRotateLeft(e);
25954                 break;
25955             case 'rotate-right' :
25956                 this.onRotateRight(e);
25957                 break;
25958             case 'picture' :
25959                 this.beforeSelectFile(e);
25960                 break;
25961             case 'trash' :
25962                 this.trash(e);
25963                 break;
25964             case 'crop' :
25965                 this.crop(e);
25966                 break;
25967             case 'download' :
25968                 this.download(e);
25969                 break;
25970             default :
25971                 break;
25972         }
25973         
25974         this.fireEvent('footerbuttonclick', this, type);
25975     },
25976     
25977     beforeSelectFile : function(e)
25978     {
25979         e.preventDefault();
25980         
25981         if(this.fireEvent('beforeselectfile', this) != false){
25982             this.selectorEl.dom.click();
25983         }
25984     },
25985     
25986     onFileSelected : function(e)
25987     {
25988         e.preventDefault();
25989         
25990         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25991             return;
25992         }
25993         
25994         var file = this.selectorEl.dom.files[0];
25995         
25996         if(this.fireEvent('inspect', this, file) != false){
25997             this.prepare(file);
25998         }
25999         
26000     },
26001     
26002     trash : function(e)
26003     {
26004         this.fireEvent('trash', this);
26005     },
26006     
26007     download : function(e)
26008     {
26009         this.fireEvent('download', this);
26010     },
26011     
26012     loadCanvas : function(src)
26013     {   
26014         if(this.fireEvent('beforeloadcanvas', this, src) != false){
26015             
26016             this.reset();
26017             
26018             this.imageEl = document.createElement('img');
26019             
26020             var _this = this;
26021             
26022             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26023             
26024             this.imageEl.src = src;
26025         }
26026     },
26027     
26028     onLoadCanvas : function()
26029     {   
26030         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26031         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26032         
26033         this.bodyEl.un('click', this.beforeSelectFile, this);
26034         
26035         this.notifyEl.hide();
26036         this.thumbEl.show();
26037         this.footerEl.show();
26038         
26039         this.baseRotateLevel();
26040         
26041         if(this.isDocument){
26042             this.setThumbBoxSize();
26043         }
26044         
26045         this.setThumbBoxPosition();
26046         
26047         this.baseScaleLevel();
26048         
26049         this.draw();
26050         
26051         this.resize();
26052         
26053         this.canvasLoaded = true;
26054         
26055         if(this.loadMask){
26056             this.maskEl.unmask();
26057         }
26058         
26059     },
26060     
26061     setCanvasPosition : function()
26062     {   
26063         if(!this.canvasEl){
26064             return;
26065         }
26066         
26067         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26068         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26069         
26070         this.previewEl.setLeft(pw);
26071         this.previewEl.setTop(ph);
26072         
26073     },
26074     
26075     onMouseDown : function(e)
26076     {   
26077         e.stopEvent();
26078         
26079         this.dragable = true;
26080         this.pinching = false;
26081         
26082         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26083             this.dragable = false;
26084             return;
26085         }
26086         
26087         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26088         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26089         
26090     },
26091     
26092     onMouseMove : function(e)
26093     {   
26094         e.stopEvent();
26095         
26096         if(!this.canvasLoaded){
26097             return;
26098         }
26099         
26100         if (!this.dragable){
26101             return;
26102         }
26103         
26104         var minX = Math.ceil(this.thumbEl.getLeft(true));
26105         var minY = Math.ceil(this.thumbEl.getTop(true));
26106         
26107         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
26108         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
26109         
26110         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26111         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26112         
26113         x = x - this.mouseX;
26114         y = y - this.mouseY;
26115         
26116         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
26117         var bgY = Math.ceil(y + this.previewEl.getTop(true));
26118         
26119         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
26120         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
26121         
26122         this.previewEl.setLeft(bgX);
26123         this.previewEl.setTop(bgY);
26124         
26125         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26126         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26127     },
26128     
26129     onMouseUp : function(e)
26130     {   
26131         e.stopEvent();
26132         
26133         this.dragable = false;
26134     },
26135     
26136     onMouseWheel : function(e)
26137     {   
26138         e.stopEvent();
26139         
26140         this.startScale = this.scale;
26141         
26142         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
26143         
26144         if(!this.zoomable()){
26145             this.scale = this.startScale;
26146             return;
26147         }
26148         
26149         this.draw();
26150         
26151         return;
26152     },
26153     
26154     zoomable : function()
26155     {
26156         var minScale = this.thumbEl.getWidth() / this.minWidth;
26157         
26158         if(this.minWidth < this.minHeight){
26159             minScale = this.thumbEl.getHeight() / this.minHeight;
26160         }
26161         
26162         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
26163         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
26164         
26165         if(
26166                 this.isDocument &&
26167                 (this.rotate == 0 || this.rotate == 180) && 
26168                 (
26169                     width > this.imageEl.OriginWidth || 
26170                     height > this.imageEl.OriginHeight ||
26171                     (width < this.minWidth && height < this.minHeight)
26172                 )
26173         ){
26174             return false;
26175         }
26176         
26177         if(
26178                 this.isDocument &&
26179                 (this.rotate == 90 || this.rotate == 270) && 
26180                 (
26181                     width > this.imageEl.OriginWidth || 
26182                     height > this.imageEl.OriginHeight ||
26183                     (width < this.minHeight && height < this.minWidth)
26184                 )
26185         ){
26186             return false;
26187         }
26188         
26189         if(
26190                 !this.isDocument &&
26191                 (this.rotate == 0 || this.rotate == 180) && 
26192                 (
26193                     width < this.minWidth || 
26194                     width > this.imageEl.OriginWidth || 
26195                     height < this.minHeight || 
26196                     height > this.imageEl.OriginHeight
26197                 )
26198         ){
26199             return false;
26200         }
26201         
26202         if(
26203                 !this.isDocument &&
26204                 (this.rotate == 90 || this.rotate == 270) && 
26205                 (
26206                     width < this.minHeight || 
26207                     width > this.imageEl.OriginWidth || 
26208                     height < this.minWidth || 
26209                     height > this.imageEl.OriginHeight
26210                 )
26211         ){
26212             return false;
26213         }
26214         
26215         return true;
26216         
26217     },
26218     
26219     onRotateLeft : function(e)
26220     {   
26221         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26222             
26223             var minScale = this.thumbEl.getWidth() / this.minWidth;
26224             
26225             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26226             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26227             
26228             this.startScale = this.scale;
26229             
26230             while (this.getScaleLevel() < minScale){
26231             
26232                 this.scale = this.scale + 1;
26233                 
26234                 if(!this.zoomable()){
26235                     break;
26236                 }
26237                 
26238                 if(
26239                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26240                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26241                 ){
26242                     continue;
26243                 }
26244                 
26245                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26246
26247                 this.draw();
26248                 
26249                 return;
26250             }
26251             
26252             this.scale = this.startScale;
26253             
26254             this.onRotateFail();
26255             
26256             return false;
26257         }
26258         
26259         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26260
26261         if(this.isDocument){
26262             this.setThumbBoxSize();
26263             this.setThumbBoxPosition();
26264             this.setCanvasPosition();
26265         }
26266         
26267         this.draw();
26268         
26269         this.fireEvent('rotate', this, 'left');
26270         
26271     },
26272     
26273     onRotateRight : function(e)
26274     {
26275         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26276             
26277             var minScale = this.thumbEl.getWidth() / this.minWidth;
26278         
26279             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26280             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26281             
26282             this.startScale = this.scale;
26283             
26284             while (this.getScaleLevel() < minScale){
26285             
26286                 this.scale = this.scale + 1;
26287                 
26288                 if(!this.zoomable()){
26289                     break;
26290                 }
26291                 
26292                 if(
26293                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26294                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26295                 ){
26296                     continue;
26297                 }
26298                 
26299                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26300
26301                 this.draw();
26302                 
26303                 return;
26304             }
26305             
26306             this.scale = this.startScale;
26307             
26308             this.onRotateFail();
26309             
26310             return false;
26311         }
26312         
26313         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26314
26315         if(this.isDocument){
26316             this.setThumbBoxSize();
26317             this.setThumbBoxPosition();
26318             this.setCanvasPosition();
26319         }
26320         
26321         this.draw();
26322         
26323         this.fireEvent('rotate', this, 'right');
26324     },
26325     
26326     onRotateFail : function()
26327     {
26328         this.errorEl.show(true);
26329         
26330         var _this = this;
26331         
26332         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
26333     },
26334     
26335     draw : function()
26336     {
26337         this.previewEl.dom.innerHTML = '';
26338         
26339         var canvasEl = document.createElement("canvas");
26340         
26341         var contextEl = canvasEl.getContext("2d");
26342         
26343         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26344         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26345         var center = this.imageEl.OriginWidth / 2;
26346         
26347         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
26348             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26349             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26350             center = this.imageEl.OriginHeight / 2;
26351         }
26352         
26353         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
26354         
26355         contextEl.translate(center, center);
26356         contextEl.rotate(this.rotate * Math.PI / 180);
26357
26358         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26359         
26360         this.canvasEl = document.createElement("canvas");
26361         
26362         this.contextEl = this.canvasEl.getContext("2d");
26363         
26364         switch (this.rotate) {
26365             case 0 :
26366                 
26367                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26368                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26369                 
26370                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26371                 
26372                 break;
26373             case 90 : 
26374                 
26375                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26376                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26377                 
26378                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26379                     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);
26380                     break;
26381                 }
26382                 
26383                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26384                 
26385                 break;
26386             case 180 :
26387                 
26388                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26389                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26390                 
26391                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26392                     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);
26393                     break;
26394                 }
26395                 
26396                 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);
26397                 
26398                 break;
26399             case 270 :
26400                 
26401                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26402                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26403         
26404                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26405                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26406                     break;
26407                 }
26408                 
26409                 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);
26410                 
26411                 break;
26412             default : 
26413                 break;
26414         }
26415         
26416         this.previewEl.appendChild(this.canvasEl);
26417         
26418         this.setCanvasPosition();
26419     },
26420     
26421     crop : function()
26422     {
26423         if(!this.canvasLoaded){
26424             return;
26425         }
26426         
26427         var imageCanvas = document.createElement("canvas");
26428         
26429         var imageContext = imageCanvas.getContext("2d");
26430         
26431         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26432         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26433         
26434         var center = imageCanvas.width / 2;
26435         
26436         imageContext.translate(center, center);
26437         
26438         imageContext.rotate(this.rotate * Math.PI / 180);
26439         
26440         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26441         
26442         var canvas = document.createElement("canvas");
26443         
26444         var context = canvas.getContext("2d");
26445                 
26446         canvas.width = this.minWidth;
26447         canvas.height = this.minHeight;
26448
26449         switch (this.rotate) {
26450             case 0 :
26451                 
26452                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26453                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26454                 
26455                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26456                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26457                 
26458                 var targetWidth = this.minWidth - 2 * x;
26459                 var targetHeight = this.minHeight - 2 * y;
26460                 
26461                 var scale = 1;
26462                 
26463                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26464                     scale = targetWidth / width;
26465                 }
26466                 
26467                 if(x > 0 && y == 0){
26468                     scale = targetHeight / height;
26469                 }
26470                 
26471                 if(x > 0 && y > 0){
26472                     scale = targetWidth / width;
26473                     
26474                     if(width < height){
26475                         scale = targetHeight / height;
26476                     }
26477                 }
26478                 
26479                 context.scale(scale, scale);
26480                 
26481                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26482                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26483
26484                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26485                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26486
26487                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26488                 
26489                 break;
26490             case 90 : 
26491                 
26492                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26493                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26494                 
26495                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26496                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26497                 
26498                 var targetWidth = this.minWidth - 2 * x;
26499                 var targetHeight = this.minHeight - 2 * y;
26500                 
26501                 var scale = 1;
26502                 
26503                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26504                     scale = targetWidth / width;
26505                 }
26506                 
26507                 if(x > 0 && y == 0){
26508                     scale = targetHeight / height;
26509                 }
26510                 
26511                 if(x > 0 && y > 0){
26512                     scale = targetWidth / width;
26513                     
26514                     if(width < height){
26515                         scale = targetHeight / height;
26516                     }
26517                 }
26518                 
26519                 context.scale(scale, scale);
26520                 
26521                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26522                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26523
26524                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26525                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26526                 
26527                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26528                 
26529                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26530                 
26531                 break;
26532             case 180 :
26533                 
26534                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26535                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26536                 
26537                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26538                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26539                 
26540                 var targetWidth = this.minWidth - 2 * x;
26541                 var targetHeight = this.minHeight - 2 * y;
26542                 
26543                 var scale = 1;
26544                 
26545                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26546                     scale = targetWidth / width;
26547                 }
26548                 
26549                 if(x > 0 && y == 0){
26550                     scale = targetHeight / height;
26551                 }
26552                 
26553                 if(x > 0 && y > 0){
26554                     scale = targetWidth / width;
26555                     
26556                     if(width < height){
26557                         scale = targetHeight / height;
26558                     }
26559                 }
26560                 
26561                 context.scale(scale, scale);
26562                 
26563                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26564                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26565
26566                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26567                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26568
26569                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26570                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26571                 
26572                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26573                 
26574                 break;
26575             case 270 :
26576                 
26577                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26578                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26579                 
26580                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26581                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26582                 
26583                 var targetWidth = this.minWidth - 2 * x;
26584                 var targetHeight = this.minHeight - 2 * y;
26585                 
26586                 var scale = 1;
26587                 
26588                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26589                     scale = targetWidth / width;
26590                 }
26591                 
26592                 if(x > 0 && y == 0){
26593                     scale = targetHeight / height;
26594                 }
26595                 
26596                 if(x > 0 && y > 0){
26597                     scale = targetWidth / width;
26598                     
26599                     if(width < height){
26600                         scale = targetHeight / height;
26601                     }
26602                 }
26603                 
26604                 context.scale(scale, scale);
26605                 
26606                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26607                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26608
26609                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26610                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26611                 
26612                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26613                 
26614                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26615                 
26616                 break;
26617             default : 
26618                 break;
26619         }
26620         
26621         this.cropData = canvas.toDataURL(this.cropType);
26622         
26623         if(this.fireEvent('crop', this, this.cropData) !== false){
26624             this.process(this.file, this.cropData);
26625         }
26626         
26627         return;
26628         
26629     },
26630     
26631     setThumbBoxSize : function()
26632     {
26633         var width, height;
26634         
26635         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
26636             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
26637             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
26638             
26639             this.minWidth = width;
26640             this.minHeight = height;
26641             
26642             if(this.rotate == 90 || this.rotate == 270){
26643                 this.minWidth = height;
26644                 this.minHeight = width;
26645             }
26646         }
26647         
26648         height = 300;
26649         width = Math.ceil(this.minWidth * height / this.minHeight);
26650         
26651         if(this.minWidth > this.minHeight){
26652             width = 300;
26653             height = Math.ceil(this.minHeight * width / this.minWidth);
26654         }
26655         
26656         this.thumbEl.setStyle({
26657             width : width + 'px',
26658             height : height + 'px'
26659         });
26660
26661         return;
26662             
26663     },
26664     
26665     setThumbBoxPosition : function()
26666     {
26667         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
26668         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
26669         
26670         this.thumbEl.setLeft(x);
26671         this.thumbEl.setTop(y);
26672         
26673     },
26674     
26675     baseRotateLevel : function()
26676     {
26677         this.baseRotate = 1;
26678         
26679         if(
26680                 typeof(this.exif) != 'undefined' &&
26681                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
26682                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
26683         ){
26684             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
26685         }
26686         
26687         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
26688         
26689     },
26690     
26691     baseScaleLevel : function()
26692     {
26693         var width, height;
26694         
26695         if(this.isDocument){
26696             
26697             if(this.baseRotate == 6 || this.baseRotate == 8){
26698             
26699                 height = this.thumbEl.getHeight();
26700                 this.baseScale = height / this.imageEl.OriginWidth;
26701
26702                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
26703                     width = this.thumbEl.getWidth();
26704                     this.baseScale = width / this.imageEl.OriginHeight;
26705                 }
26706
26707                 return;
26708             }
26709
26710             height = this.thumbEl.getHeight();
26711             this.baseScale = height / this.imageEl.OriginHeight;
26712
26713             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
26714                 width = this.thumbEl.getWidth();
26715                 this.baseScale = width / this.imageEl.OriginWidth;
26716             }
26717
26718             return;
26719         }
26720         
26721         if(this.baseRotate == 6 || this.baseRotate == 8){
26722             
26723             width = this.thumbEl.getHeight();
26724             this.baseScale = width / this.imageEl.OriginHeight;
26725             
26726             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
26727                 height = this.thumbEl.getWidth();
26728                 this.baseScale = height / this.imageEl.OriginHeight;
26729             }
26730             
26731             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26732                 height = this.thumbEl.getWidth();
26733                 this.baseScale = height / this.imageEl.OriginHeight;
26734                 
26735                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
26736                     width = this.thumbEl.getHeight();
26737                     this.baseScale = width / this.imageEl.OriginWidth;
26738                 }
26739             }
26740             
26741             return;
26742         }
26743         
26744         width = this.thumbEl.getWidth();
26745         this.baseScale = width / this.imageEl.OriginWidth;
26746         
26747         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
26748             height = this.thumbEl.getHeight();
26749             this.baseScale = height / this.imageEl.OriginHeight;
26750         }
26751         
26752         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26753             
26754             height = this.thumbEl.getHeight();
26755             this.baseScale = height / this.imageEl.OriginHeight;
26756             
26757             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26758                 width = this.thumbEl.getWidth();
26759                 this.baseScale = width / this.imageEl.OriginWidth;
26760             }
26761             
26762         }
26763         
26764         return;
26765     },
26766     
26767     getScaleLevel : function()
26768     {
26769         return this.baseScale * Math.pow(1.1, this.scale);
26770     },
26771     
26772     onTouchStart : function(e)
26773     {
26774         if(!this.canvasLoaded){
26775             this.beforeSelectFile(e);
26776             return;
26777         }
26778         
26779         var touches = e.browserEvent.touches;
26780         
26781         if(!touches){
26782             return;
26783         }
26784         
26785         if(touches.length == 1){
26786             this.onMouseDown(e);
26787             return;
26788         }
26789         
26790         if(touches.length != 2){
26791             return;
26792         }
26793         
26794         var coords = [];
26795         
26796         for(var i = 0, finger; finger = touches[i]; i++){
26797             coords.push(finger.pageX, finger.pageY);
26798         }
26799         
26800         var x = Math.pow(coords[0] - coords[2], 2);
26801         var y = Math.pow(coords[1] - coords[3], 2);
26802         
26803         this.startDistance = Math.sqrt(x + y);
26804         
26805         this.startScale = this.scale;
26806         
26807         this.pinching = true;
26808         this.dragable = false;
26809         
26810     },
26811     
26812     onTouchMove : function(e)
26813     {
26814         if(!this.pinching && !this.dragable){
26815             return;
26816         }
26817         
26818         var touches = e.browserEvent.touches;
26819         
26820         if(!touches){
26821             return;
26822         }
26823         
26824         if(this.dragable){
26825             this.onMouseMove(e);
26826             return;
26827         }
26828         
26829         var coords = [];
26830         
26831         for(var i = 0, finger; finger = touches[i]; i++){
26832             coords.push(finger.pageX, finger.pageY);
26833         }
26834         
26835         var x = Math.pow(coords[0] - coords[2], 2);
26836         var y = Math.pow(coords[1] - coords[3], 2);
26837         
26838         this.endDistance = Math.sqrt(x + y);
26839         
26840         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26841         
26842         if(!this.zoomable()){
26843             this.scale = this.startScale;
26844             return;
26845         }
26846         
26847         this.draw();
26848         
26849     },
26850     
26851     onTouchEnd : function(e)
26852     {
26853         this.pinching = false;
26854         this.dragable = false;
26855         
26856     },
26857     
26858     process : function(file, crop)
26859     {
26860         if(this.loadMask){
26861             this.maskEl.mask(this.loadingText);
26862         }
26863         
26864         this.xhr = new XMLHttpRequest();
26865         
26866         file.xhr = this.xhr;
26867
26868         this.xhr.open(this.method, this.url, true);
26869         
26870         var headers = {
26871             "Accept": "application/json",
26872             "Cache-Control": "no-cache",
26873             "X-Requested-With": "XMLHttpRequest"
26874         };
26875         
26876         for (var headerName in headers) {
26877             var headerValue = headers[headerName];
26878             if (headerValue) {
26879                 this.xhr.setRequestHeader(headerName, headerValue);
26880             }
26881         }
26882         
26883         var _this = this;
26884         
26885         this.xhr.onload = function()
26886         {
26887             _this.xhrOnLoad(_this.xhr);
26888         }
26889         
26890         this.xhr.onerror = function()
26891         {
26892             _this.xhrOnError(_this.xhr);
26893         }
26894         
26895         var formData = new FormData();
26896
26897         formData.append('returnHTML', 'NO');
26898         
26899         if(crop){
26900             formData.append('crop', crop);
26901         }
26902         
26903         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26904             formData.append(this.paramName, file, file.name);
26905         }
26906         
26907         if(typeof(file.filename) != 'undefined'){
26908             formData.append('filename', file.filename);
26909         }
26910         
26911         if(typeof(file.mimetype) != 'undefined'){
26912             formData.append('mimetype', file.mimetype);
26913         }
26914         
26915         if(this.fireEvent('arrange', this, formData) != false){
26916             this.xhr.send(formData);
26917         };
26918     },
26919     
26920     xhrOnLoad : function(xhr)
26921     {
26922         if(this.loadMask){
26923             this.maskEl.unmask();
26924         }
26925         
26926         if (xhr.readyState !== 4) {
26927             this.fireEvent('exception', this, xhr);
26928             return;
26929         }
26930
26931         var response = Roo.decode(xhr.responseText);
26932         
26933         if(!response.success){
26934             this.fireEvent('exception', this, xhr);
26935             return;
26936         }
26937         
26938         var response = Roo.decode(xhr.responseText);
26939         
26940         this.fireEvent('upload', this, response);
26941         
26942     },
26943     
26944     xhrOnError : function()
26945     {
26946         if(this.loadMask){
26947             this.maskEl.unmask();
26948         }
26949         
26950         Roo.log('xhr on error');
26951         
26952         var response = Roo.decode(xhr.responseText);
26953           
26954         Roo.log(response);
26955         
26956     },
26957     
26958     prepare : function(file)
26959     {   
26960         if(this.loadMask){
26961             this.maskEl.mask(this.loadingText);
26962         }
26963         
26964         this.file = false;
26965         this.exif = {};
26966         
26967         if(typeof(file) === 'string'){
26968             this.loadCanvas(file);
26969             return;
26970         }
26971         
26972         if(!file || !this.urlAPI){
26973             return;
26974         }
26975         
26976         this.file = file;
26977         this.cropType = file.type;
26978         
26979         var _this = this;
26980         
26981         if(this.fireEvent('prepare', this, this.file) != false){
26982             
26983             var reader = new FileReader();
26984             
26985             reader.onload = function (e) {
26986                 if (e.target.error) {
26987                     Roo.log(e.target.error);
26988                     return;
26989                 }
26990                 
26991                 var buffer = e.target.result,
26992                     dataView = new DataView(buffer),
26993                     offset = 2,
26994                     maxOffset = dataView.byteLength - 4,
26995                     markerBytes,
26996                     markerLength;
26997                 
26998                 if (dataView.getUint16(0) === 0xffd8) {
26999                     while (offset < maxOffset) {
27000                         markerBytes = dataView.getUint16(offset);
27001                         
27002                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27003                             markerLength = dataView.getUint16(offset + 2) + 2;
27004                             if (offset + markerLength > dataView.byteLength) {
27005                                 Roo.log('Invalid meta data: Invalid segment size.');
27006                                 break;
27007                             }
27008                             
27009                             if(markerBytes == 0xffe1){
27010                                 _this.parseExifData(
27011                                     dataView,
27012                                     offset,
27013                                     markerLength
27014                                 );
27015                             }
27016                             
27017                             offset += markerLength;
27018                             
27019                             continue;
27020                         }
27021                         
27022                         break;
27023                     }
27024                     
27025                 }
27026                 
27027                 var url = _this.urlAPI.createObjectURL(_this.file);
27028                 
27029                 _this.loadCanvas(url);
27030                 
27031                 return;
27032             }
27033             
27034             reader.readAsArrayBuffer(this.file);
27035             
27036         }
27037         
27038     },
27039     
27040     parseExifData : function(dataView, offset, length)
27041     {
27042         var tiffOffset = offset + 10,
27043             littleEndian,
27044             dirOffset;
27045     
27046         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27047             // No Exif data, might be XMP data instead
27048             return;
27049         }
27050         
27051         // Check for the ASCII code for "Exif" (0x45786966):
27052         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27053             // No Exif data, might be XMP data instead
27054             return;
27055         }
27056         if (tiffOffset + 8 > dataView.byteLength) {
27057             Roo.log('Invalid Exif data: Invalid segment size.');
27058             return;
27059         }
27060         // Check for the two null bytes:
27061         if (dataView.getUint16(offset + 8) !== 0x0000) {
27062             Roo.log('Invalid Exif data: Missing byte alignment offset.');
27063             return;
27064         }
27065         // Check the byte alignment:
27066         switch (dataView.getUint16(tiffOffset)) {
27067         case 0x4949:
27068             littleEndian = true;
27069             break;
27070         case 0x4D4D:
27071             littleEndian = false;
27072             break;
27073         default:
27074             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27075             return;
27076         }
27077         // Check for the TIFF tag marker (0x002A):
27078         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27079             Roo.log('Invalid Exif data: Missing TIFF marker.');
27080             return;
27081         }
27082         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27083         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27084         
27085         this.parseExifTags(
27086             dataView,
27087             tiffOffset,
27088             tiffOffset + dirOffset,
27089             littleEndian
27090         );
27091     },
27092     
27093     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27094     {
27095         var tagsNumber,
27096             dirEndOffset,
27097             i;
27098         if (dirOffset + 6 > dataView.byteLength) {
27099             Roo.log('Invalid Exif data: Invalid directory offset.');
27100             return;
27101         }
27102         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
27103         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
27104         if (dirEndOffset + 4 > dataView.byteLength) {
27105             Roo.log('Invalid Exif data: Invalid directory size.');
27106             return;
27107         }
27108         for (i = 0; i < tagsNumber; i += 1) {
27109             this.parseExifTag(
27110                 dataView,
27111                 tiffOffset,
27112                 dirOffset + 2 + 12 * i, // tag offset
27113                 littleEndian
27114             );
27115         }
27116         // Return the offset to the next directory:
27117         return dataView.getUint32(dirEndOffset, littleEndian);
27118     },
27119     
27120     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
27121     {
27122         var tag = dataView.getUint16(offset, littleEndian);
27123         
27124         this.exif[tag] = this.getExifValue(
27125             dataView,
27126             tiffOffset,
27127             offset,
27128             dataView.getUint16(offset + 2, littleEndian), // tag type
27129             dataView.getUint32(offset + 4, littleEndian), // tag length
27130             littleEndian
27131         );
27132     },
27133     
27134     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
27135     {
27136         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
27137             tagSize,
27138             dataOffset,
27139             values,
27140             i,
27141             str,
27142             c;
27143     
27144         if (!tagType) {
27145             Roo.log('Invalid Exif data: Invalid tag type.');
27146             return;
27147         }
27148         
27149         tagSize = tagType.size * length;
27150         // Determine if the value is contained in the dataOffset bytes,
27151         // or if the value at the dataOffset is a pointer to the actual data:
27152         dataOffset = tagSize > 4 ?
27153                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
27154         if (dataOffset + tagSize > dataView.byteLength) {
27155             Roo.log('Invalid Exif data: Invalid data offset.');
27156             return;
27157         }
27158         if (length === 1) {
27159             return tagType.getValue(dataView, dataOffset, littleEndian);
27160         }
27161         values = [];
27162         for (i = 0; i < length; i += 1) {
27163             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
27164         }
27165         
27166         if (tagType.ascii) {
27167             str = '';
27168             // Concatenate the chars:
27169             for (i = 0; i < values.length; i += 1) {
27170                 c = values[i];
27171                 // Ignore the terminating NULL byte(s):
27172                 if (c === '\u0000') {
27173                     break;
27174                 }
27175                 str += c;
27176             }
27177             return str;
27178         }
27179         return values;
27180     }
27181     
27182 });
27183
27184 Roo.apply(Roo.bootstrap.UploadCropbox, {
27185     tags : {
27186         'Orientation': 0x0112
27187     },
27188     
27189     Orientation: {
27190             1: 0, //'top-left',
27191 //            2: 'top-right',
27192             3: 180, //'bottom-right',
27193 //            4: 'bottom-left',
27194 //            5: 'left-top',
27195             6: 90, //'right-top',
27196 //            7: 'right-bottom',
27197             8: 270 //'left-bottom'
27198     },
27199     
27200     exifTagTypes : {
27201         // byte, 8-bit unsigned int:
27202         1: {
27203             getValue: function (dataView, dataOffset) {
27204                 return dataView.getUint8(dataOffset);
27205             },
27206             size: 1
27207         },
27208         // ascii, 8-bit byte:
27209         2: {
27210             getValue: function (dataView, dataOffset) {
27211                 return String.fromCharCode(dataView.getUint8(dataOffset));
27212             },
27213             size: 1,
27214             ascii: true
27215         },
27216         // short, 16 bit int:
27217         3: {
27218             getValue: function (dataView, dataOffset, littleEndian) {
27219                 return dataView.getUint16(dataOffset, littleEndian);
27220             },
27221             size: 2
27222         },
27223         // long, 32 bit int:
27224         4: {
27225             getValue: function (dataView, dataOffset, littleEndian) {
27226                 return dataView.getUint32(dataOffset, littleEndian);
27227             },
27228             size: 4
27229         },
27230         // rational = two long values, first is numerator, second is denominator:
27231         5: {
27232             getValue: function (dataView, dataOffset, littleEndian) {
27233                 return dataView.getUint32(dataOffset, littleEndian) /
27234                     dataView.getUint32(dataOffset + 4, littleEndian);
27235             },
27236             size: 8
27237         },
27238         // slong, 32 bit signed int:
27239         9: {
27240             getValue: function (dataView, dataOffset, littleEndian) {
27241                 return dataView.getInt32(dataOffset, littleEndian);
27242             },
27243             size: 4
27244         },
27245         // srational, two slongs, first is numerator, second is denominator:
27246         10: {
27247             getValue: function (dataView, dataOffset, littleEndian) {
27248                 return dataView.getInt32(dataOffset, littleEndian) /
27249                     dataView.getInt32(dataOffset + 4, littleEndian);
27250             },
27251             size: 8
27252         }
27253     },
27254     
27255     footer : {
27256         STANDARD : [
27257             {
27258                 tag : 'div',
27259                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27260                 action : 'rotate-left',
27261                 cn : [
27262                     {
27263                         tag : 'button',
27264                         cls : 'btn btn-default',
27265                         html : '<i class="fa fa-undo"></i>'
27266                     }
27267                 ]
27268             },
27269             {
27270                 tag : 'div',
27271                 cls : 'btn-group roo-upload-cropbox-picture',
27272                 action : 'picture',
27273                 cn : [
27274                     {
27275                         tag : 'button',
27276                         cls : 'btn btn-default',
27277                         html : '<i class="fa fa-picture-o"></i>'
27278                     }
27279                 ]
27280             },
27281             {
27282                 tag : 'div',
27283                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27284                 action : 'rotate-right',
27285                 cn : [
27286                     {
27287                         tag : 'button',
27288                         cls : 'btn btn-default',
27289                         html : '<i class="fa fa-repeat"></i>'
27290                     }
27291                 ]
27292             }
27293         ],
27294         DOCUMENT : [
27295             {
27296                 tag : 'div',
27297                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27298                 action : 'rotate-left',
27299                 cn : [
27300                     {
27301                         tag : 'button',
27302                         cls : 'btn btn-default',
27303                         html : '<i class="fa fa-undo"></i>'
27304                     }
27305                 ]
27306             },
27307             {
27308                 tag : 'div',
27309                 cls : 'btn-group roo-upload-cropbox-download',
27310                 action : 'download',
27311                 cn : [
27312                     {
27313                         tag : 'button',
27314                         cls : 'btn btn-default',
27315                         html : '<i class="fa fa-download"></i>'
27316                     }
27317                 ]
27318             },
27319             {
27320                 tag : 'div',
27321                 cls : 'btn-group roo-upload-cropbox-crop',
27322                 action : 'crop',
27323                 cn : [
27324                     {
27325                         tag : 'button',
27326                         cls : 'btn btn-default',
27327                         html : '<i class="fa fa-crop"></i>'
27328                     }
27329                 ]
27330             },
27331             {
27332                 tag : 'div',
27333                 cls : 'btn-group roo-upload-cropbox-trash',
27334                 action : 'trash',
27335                 cn : [
27336                     {
27337                         tag : 'button',
27338                         cls : 'btn btn-default',
27339                         html : '<i class="fa fa-trash"></i>'
27340                     }
27341                 ]
27342             },
27343             {
27344                 tag : 'div',
27345                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27346                 action : 'rotate-right',
27347                 cn : [
27348                     {
27349                         tag : 'button',
27350                         cls : 'btn btn-default',
27351                         html : '<i class="fa fa-repeat"></i>'
27352                     }
27353                 ]
27354             }
27355         ],
27356         ROTATOR : [
27357             {
27358                 tag : 'div',
27359                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27360                 action : 'rotate-left',
27361                 cn : [
27362                     {
27363                         tag : 'button',
27364                         cls : 'btn btn-default',
27365                         html : '<i class="fa fa-undo"></i>'
27366                     }
27367                 ]
27368             },
27369             {
27370                 tag : 'div',
27371                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27372                 action : 'rotate-right',
27373                 cn : [
27374                     {
27375                         tag : 'button',
27376                         cls : 'btn btn-default',
27377                         html : '<i class="fa fa-repeat"></i>'
27378                     }
27379                 ]
27380             }
27381         ]
27382     }
27383 });
27384
27385 /*
27386 * Licence: LGPL
27387 */
27388
27389 /**
27390  * @class Roo.bootstrap.DocumentManager
27391  * @extends Roo.bootstrap.Component
27392  * Bootstrap DocumentManager class
27393  * @cfg {String} paramName default 'imageUpload'
27394  * @cfg {String} method default POST
27395  * @cfg {String} url action url
27396  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
27397  * @cfg {Boolean} multiple multiple upload default true
27398  * @cfg {Number} thumbSize default 300
27399  * @cfg {String} fieldLabel
27400  * @cfg {Number} labelWidth default 4
27401  * @cfg {String} labelAlign (left|top) default left
27402  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
27403  * 
27404  * @constructor
27405  * Create a new DocumentManager
27406  * @param {Object} config The config object
27407  */
27408
27409 Roo.bootstrap.DocumentManager = function(config){
27410     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
27411     
27412     this.files = [];
27413     this.delegates = [];
27414     
27415     this.addEvents({
27416         /**
27417          * @event initial
27418          * Fire when initial the DocumentManager
27419          * @param {Roo.bootstrap.DocumentManager} this
27420          */
27421         "initial" : true,
27422         /**
27423          * @event inspect
27424          * inspect selected file
27425          * @param {Roo.bootstrap.DocumentManager} this
27426          * @param {File} file
27427          */
27428         "inspect" : true,
27429         /**
27430          * @event exception
27431          * Fire when xhr load exception
27432          * @param {Roo.bootstrap.DocumentManager} this
27433          * @param {XMLHttpRequest} xhr
27434          */
27435         "exception" : true,
27436         /**
27437          * @event afterupload
27438          * Fire when xhr load exception
27439          * @param {Roo.bootstrap.DocumentManager} this
27440          * @param {XMLHttpRequest} xhr
27441          */
27442         "afterupload" : true,
27443         /**
27444          * @event prepare
27445          * prepare the form data
27446          * @param {Roo.bootstrap.DocumentManager} this
27447          * @param {Object} formData
27448          */
27449         "prepare" : true,
27450         /**
27451          * @event remove
27452          * Fire when remove the file
27453          * @param {Roo.bootstrap.DocumentManager} this
27454          * @param {Object} file
27455          */
27456         "remove" : true,
27457         /**
27458          * @event refresh
27459          * Fire after refresh the file
27460          * @param {Roo.bootstrap.DocumentManager} this
27461          */
27462         "refresh" : true,
27463         /**
27464          * @event click
27465          * Fire after click the image
27466          * @param {Roo.bootstrap.DocumentManager} this
27467          * @param {Object} file
27468          */
27469         "click" : true,
27470         /**
27471          * @event edit
27472          * Fire when upload a image and editable set to true
27473          * @param {Roo.bootstrap.DocumentManager} this
27474          * @param {Object} file
27475          */
27476         "edit" : true,
27477         /**
27478          * @event beforeselectfile
27479          * Fire before select file
27480          * @param {Roo.bootstrap.DocumentManager} this
27481          */
27482         "beforeselectfile" : true,
27483         /**
27484          * @event process
27485          * Fire before process file
27486          * @param {Roo.bootstrap.DocumentManager} this
27487          * @param {Object} file
27488          */
27489         "process" : true
27490         
27491     });
27492 };
27493
27494 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
27495     
27496     boxes : 0,
27497     inputName : '',
27498     thumbSize : 300,
27499     multiple : true,
27500     files : false,
27501     method : 'POST',
27502     url : '',
27503     paramName : 'imageUpload',
27504     fieldLabel : '',
27505     labelWidth : 4,
27506     labelAlign : 'left',
27507     editable : true,
27508     delegates : false,
27509     
27510     
27511     xhr : false, 
27512     
27513     getAutoCreate : function()
27514     {   
27515         var managerWidget = {
27516             tag : 'div',
27517             cls : 'roo-document-manager',
27518             cn : [
27519                 {
27520                     tag : 'input',
27521                     cls : 'roo-document-manager-selector',
27522                     type : 'file'
27523                 },
27524                 {
27525                     tag : 'div',
27526                     cls : 'roo-document-manager-uploader',
27527                     cn : [
27528                         {
27529                             tag : 'div',
27530                             cls : 'roo-document-manager-upload-btn',
27531                             html : '<i class="fa fa-plus"></i>'
27532                         }
27533                     ]
27534                     
27535                 }
27536             ]
27537         };
27538         
27539         var content = [
27540             {
27541                 tag : 'div',
27542                 cls : 'column col-md-12',
27543                 cn : managerWidget
27544             }
27545         ];
27546         
27547         if(this.fieldLabel.length){
27548             
27549             content = [
27550                 {
27551                     tag : 'div',
27552                     cls : 'column col-md-12',
27553                     html : this.fieldLabel
27554                 },
27555                 {
27556                     tag : 'div',
27557                     cls : 'column col-md-12',
27558                     cn : managerWidget
27559                 }
27560             ];
27561
27562             if(this.labelAlign == 'left'){
27563                 content = [
27564                     {
27565                         tag : 'div',
27566                         cls : 'column col-md-' + this.labelWidth,
27567                         html : this.fieldLabel
27568                     },
27569                     {
27570                         tag : 'div',
27571                         cls : 'column col-md-' + (12 - this.labelWidth),
27572                         cn : managerWidget
27573                     }
27574                 ];
27575                 
27576             }
27577         }
27578         
27579         var cfg = {
27580             tag : 'div',
27581             cls : 'row clearfix',
27582             cn : content
27583         };
27584         
27585         return cfg;
27586         
27587     },
27588     
27589     initEvents : function()
27590     {
27591         this.managerEl = this.el.select('.roo-document-manager', true).first();
27592         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27593         
27594         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
27595         this.selectorEl.hide();
27596         
27597         if(this.multiple){
27598             this.selectorEl.attr('multiple', 'multiple');
27599         }
27600         
27601         this.selectorEl.on('change', this.onFileSelected, this);
27602         
27603         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
27604         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27605         
27606         this.uploader.on('click', this.onUploaderClick, this);
27607         
27608         this.renderProgressDialog();
27609         
27610         var _this = this;
27611         
27612         window.addEventListener("resize", function() { _this.refresh(); } );
27613         
27614         this.fireEvent('initial', this);
27615     },
27616     
27617     renderProgressDialog : function()
27618     {
27619         var _this = this;
27620         
27621         this.progressDialog = new Roo.bootstrap.Modal({
27622             cls : 'roo-document-manager-progress-dialog',
27623             allow_close : false,
27624             title : '',
27625             buttons : [
27626                 {
27627                     name  :'cancel',
27628                     weight : 'danger',
27629                     html : 'Cancel'
27630                 }
27631             ], 
27632             listeners : { 
27633                 btnclick : function() {
27634                     _this.uploadCancel();
27635                     this.hide();
27636                 }
27637             }
27638         });
27639          
27640         this.progressDialog.render(Roo.get(document.body));
27641          
27642         this.progress = new Roo.bootstrap.Progress({
27643             cls : 'roo-document-manager-progress',
27644             active : true,
27645             striped : true
27646         });
27647         
27648         this.progress.render(this.progressDialog.getChildContainer());
27649         
27650         this.progressBar = new Roo.bootstrap.ProgressBar({
27651             cls : 'roo-document-manager-progress-bar',
27652             aria_valuenow : 0,
27653             aria_valuemin : 0,
27654             aria_valuemax : 12,
27655             panel : 'success'
27656         });
27657         
27658         this.progressBar.render(this.progress.getChildContainer());
27659     },
27660     
27661     onUploaderClick : function(e)
27662     {
27663         e.preventDefault();
27664      
27665         if(this.fireEvent('beforeselectfile', this) != false){
27666             this.selectorEl.dom.click();
27667         }
27668         
27669     },
27670     
27671     onFileSelected : function(e)
27672     {
27673         e.preventDefault();
27674         
27675         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27676             return;
27677         }
27678         
27679         Roo.each(this.selectorEl.dom.files, function(file){
27680             if(this.fireEvent('inspect', this, file) != false){
27681                 this.files.push(file);
27682             }
27683         }, this);
27684         
27685         this.queue();
27686         
27687     },
27688     
27689     queue : function()
27690     {
27691         this.selectorEl.dom.value = '';
27692         
27693         if(!this.files.length){
27694             return;
27695         }
27696         
27697         if(this.boxes > 0 && this.files.length > this.boxes){
27698             this.files = this.files.slice(0, this.boxes);
27699         }
27700         
27701         this.uploader.show();
27702         
27703         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27704             this.uploader.hide();
27705         }
27706         
27707         var _this = this;
27708         
27709         var files = [];
27710         
27711         var docs = [];
27712         
27713         Roo.each(this.files, function(file){
27714             
27715             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27716                 var f = this.renderPreview(file);
27717                 files.push(f);
27718                 return;
27719             }
27720             
27721             if(file.type.indexOf('image') != -1){
27722                 this.delegates.push(
27723                     (function(){
27724                         _this.process(file);
27725                     }).createDelegate(this)
27726                 );
27727         
27728                 return;
27729             }
27730             
27731             docs.push(
27732                 (function(){
27733                     _this.process(file);
27734                 }).createDelegate(this)
27735             );
27736             
27737         }, this);
27738         
27739         this.files = files;
27740         
27741         this.delegates = this.delegates.concat(docs);
27742         
27743         if(!this.delegates.length){
27744             this.refresh();
27745             return;
27746         }
27747         
27748         this.progressBar.aria_valuemax = this.delegates.length;
27749         
27750         this.arrange();
27751         
27752         return;
27753     },
27754     
27755     arrange : function()
27756     {
27757         if(!this.delegates.length){
27758             this.progressDialog.hide();
27759             this.refresh();
27760             return;
27761         }
27762         
27763         var delegate = this.delegates.shift();
27764         
27765         this.progressDialog.show();
27766         
27767         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27768         
27769         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27770         
27771         delegate();
27772     },
27773     
27774     refresh : function()
27775     {
27776         this.uploader.show();
27777         
27778         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27779             this.uploader.hide();
27780         }
27781         
27782         Roo.isTouch ? this.closable(false) : this.closable(true);
27783         
27784         this.fireEvent('refresh', this);
27785     },
27786     
27787     onRemove : function(e, el, o)
27788     {
27789         e.preventDefault();
27790         
27791         this.fireEvent('remove', this, o);
27792         
27793     },
27794     
27795     remove : function(o)
27796     {
27797         var files = [];
27798         
27799         Roo.each(this.files, function(file){
27800             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27801                 files.push(file);
27802                 return;
27803             }
27804
27805             o.target.remove();
27806
27807         }, this);
27808         
27809         this.files = files;
27810         
27811         this.refresh();
27812     },
27813     
27814     clear : function()
27815     {
27816         Roo.each(this.files, function(file){
27817             if(!file.target){
27818                 return;
27819             }
27820             
27821             file.target.remove();
27822
27823         }, this);
27824         
27825         this.files = [];
27826         
27827         this.refresh();
27828     },
27829     
27830     onClick : function(e, el, o)
27831     {
27832         e.preventDefault();
27833         
27834         this.fireEvent('click', this, o);
27835         
27836     },
27837     
27838     closable : function(closable)
27839     {
27840         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27841             
27842             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27843             
27844             if(closable){
27845                 el.show();
27846                 return;
27847             }
27848             
27849             el.hide();
27850             
27851         }, this);
27852     },
27853     
27854     xhrOnLoad : function(xhr)
27855     {
27856         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27857             el.remove();
27858         }, this);
27859         
27860         if (xhr.readyState !== 4) {
27861             this.arrange();
27862             this.fireEvent('exception', this, xhr);
27863             return;
27864         }
27865
27866         var response = Roo.decode(xhr.responseText);
27867         
27868         if(!response.success){
27869             this.arrange();
27870             this.fireEvent('exception', this, xhr);
27871             return;
27872         }
27873         
27874         var file = this.renderPreview(response.data);
27875         
27876         this.files.push(file);
27877         
27878         this.arrange();
27879         
27880         this.fireEvent('afterupload', this, xhr);
27881         
27882     },
27883     
27884     xhrOnError : function(xhr)
27885     {
27886         Roo.log('xhr on error');
27887         
27888         var response = Roo.decode(xhr.responseText);
27889           
27890         Roo.log(response);
27891         
27892         this.arrange();
27893     },
27894     
27895     process : function(file)
27896     {
27897         if(this.fireEvent('process', this, file) !== false){
27898             if(this.editable && file.type.indexOf('image') != -1){
27899                 this.fireEvent('edit', this, file);
27900                 return;
27901             }
27902
27903             this.uploadStart(file, false);
27904
27905             return;
27906         }
27907         
27908     },
27909     
27910     uploadStart : function(file, crop)
27911     {
27912         this.xhr = new XMLHttpRequest();
27913         
27914         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27915             this.arrange();
27916             return;
27917         }
27918         
27919         file.xhr = this.xhr;
27920             
27921         this.managerEl.createChild({
27922             tag : 'div',
27923             cls : 'roo-document-manager-loading',
27924             cn : [
27925                 {
27926                     tag : 'div',
27927                     tooltip : file.name,
27928                     cls : 'roo-document-manager-thumb',
27929                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27930                 }
27931             ]
27932
27933         });
27934
27935         this.xhr.open(this.method, this.url, true);
27936         
27937         var headers = {
27938             "Accept": "application/json",
27939             "Cache-Control": "no-cache",
27940             "X-Requested-With": "XMLHttpRequest"
27941         };
27942         
27943         for (var headerName in headers) {
27944             var headerValue = headers[headerName];
27945             if (headerValue) {
27946                 this.xhr.setRequestHeader(headerName, headerValue);
27947             }
27948         }
27949         
27950         var _this = this;
27951         
27952         this.xhr.onload = function()
27953         {
27954             _this.xhrOnLoad(_this.xhr);
27955         }
27956         
27957         this.xhr.onerror = function()
27958         {
27959             _this.xhrOnError(_this.xhr);
27960         }
27961         
27962         var formData = new FormData();
27963
27964         formData.append('returnHTML', 'NO');
27965         
27966         if(crop){
27967             formData.append('crop', crop);
27968         }
27969         
27970         formData.append(this.paramName, file, file.name);
27971         
27972         if(this.fireEvent('prepare', this, formData) != false){
27973             this.xhr.send(formData);
27974         };
27975     },
27976     
27977     uploadCancel : function()
27978     {
27979         if (this.xhr) {
27980             this.xhr.abort();
27981         }
27982         
27983         
27984         this.delegates = [];
27985         
27986         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27987             el.remove();
27988         }, this);
27989         
27990         this.arrange();
27991     },
27992     
27993     renderPreview : function(file)
27994     {
27995         if(typeof(file.target) != 'undefined' && file.target){
27996             return file;
27997         }
27998         
27999         var previewEl = this.managerEl.createChild({
28000             tag : 'div',
28001             cls : 'roo-document-manager-preview',
28002             cn : [
28003                 {
28004                     tag : 'div',
28005                     tooltip : file.filename,
28006                     cls : 'roo-document-manager-thumb',
28007                     html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
28008                 },
28009                 {
28010                     tag : 'button',
28011                     cls : 'close',
28012                     html : '<i class="fa fa-times-circle"></i>'
28013                 }
28014             ]
28015         });
28016
28017         var close = previewEl.select('button.close', true).first();
28018
28019         close.on('click', this.onRemove, this, file);
28020
28021         file.target = previewEl;
28022
28023         var image = previewEl.select('img', true).first();
28024         
28025         var _this = this;
28026         
28027         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28028         
28029         image.on('click', this.onClick, this, file);
28030         
28031         return file;
28032         
28033     },
28034     
28035     onPreviewLoad : function(file, image)
28036     {
28037         if(typeof(file.target) == 'undefined' || !file.target){
28038             return;
28039         }
28040         
28041         var width = image.dom.naturalWidth || image.dom.width;
28042         var height = image.dom.naturalHeight || image.dom.height;
28043         
28044         if(width > height){
28045             file.target.addClass('wide');
28046             return;
28047         }
28048         
28049         file.target.addClass('tall');
28050         return;
28051         
28052     },
28053     
28054     uploadFromSource : function(file, crop)
28055     {
28056         this.xhr = new XMLHttpRequest();
28057         
28058         this.managerEl.createChild({
28059             tag : 'div',
28060             cls : 'roo-document-manager-loading',
28061             cn : [
28062                 {
28063                     tag : 'div',
28064                     tooltip : file.name,
28065                     cls : 'roo-document-manager-thumb',
28066                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28067                 }
28068             ]
28069
28070         });
28071
28072         this.xhr.open(this.method, this.url, true);
28073         
28074         var headers = {
28075             "Accept": "application/json",
28076             "Cache-Control": "no-cache",
28077             "X-Requested-With": "XMLHttpRequest"
28078         };
28079         
28080         for (var headerName in headers) {
28081             var headerValue = headers[headerName];
28082             if (headerValue) {
28083                 this.xhr.setRequestHeader(headerName, headerValue);
28084             }
28085         }
28086         
28087         var _this = this;
28088         
28089         this.xhr.onload = function()
28090         {
28091             _this.xhrOnLoad(_this.xhr);
28092         }
28093         
28094         this.xhr.onerror = function()
28095         {
28096             _this.xhrOnError(_this.xhr);
28097         }
28098         
28099         var formData = new FormData();
28100
28101         formData.append('returnHTML', 'NO');
28102         
28103         formData.append('crop', crop);
28104         
28105         if(typeof(file.filename) != 'undefined'){
28106             formData.append('filename', file.filename);
28107         }
28108         
28109         if(typeof(file.mimetype) != 'undefined'){
28110             formData.append('mimetype', file.mimetype);
28111         }
28112         
28113         if(this.fireEvent('prepare', this, formData) != false){
28114             this.xhr.send(formData);
28115         };
28116     }
28117 });
28118
28119 /*
28120 * Licence: LGPL
28121 */
28122
28123 /**
28124  * @class Roo.bootstrap.DocumentViewer
28125  * @extends Roo.bootstrap.Component
28126  * Bootstrap DocumentViewer class
28127  * @cfg {Boolean} showDownload (true|false) show download button (default true)
28128  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
28129  * 
28130  * @constructor
28131  * Create a new DocumentViewer
28132  * @param {Object} config The config object
28133  */
28134
28135 Roo.bootstrap.DocumentViewer = function(config){
28136     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
28137     
28138     this.addEvents({
28139         /**
28140          * @event initial
28141          * Fire after initEvent
28142          * @param {Roo.bootstrap.DocumentViewer} this
28143          */
28144         "initial" : true,
28145         /**
28146          * @event click
28147          * Fire after click
28148          * @param {Roo.bootstrap.DocumentViewer} this
28149          */
28150         "click" : true,
28151         /**
28152          * @event download
28153          * Fire after download button
28154          * @param {Roo.bootstrap.DocumentViewer} this
28155          */
28156         "download" : true,
28157         /**
28158          * @event trash
28159          * Fire after trash button
28160          * @param {Roo.bootstrap.DocumentViewer} this
28161          */
28162         "trash" : true
28163         
28164     });
28165 };
28166
28167 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
28168     
28169     showDownload : true,
28170     
28171     showTrash : true,
28172     
28173     getAutoCreate : function()
28174     {
28175         var cfg = {
28176             tag : 'div',
28177             cls : 'roo-document-viewer',
28178             cn : [
28179                 {
28180                     tag : 'div',
28181                     cls : 'roo-document-viewer-body',
28182                     cn : [
28183                         {
28184                             tag : 'div',
28185                             cls : 'roo-document-viewer-thumb',
28186                             cn : [
28187                                 {
28188                                     tag : 'img',
28189                                     cls : 'roo-document-viewer-image'
28190                                 }
28191                             ]
28192                         }
28193                     ]
28194                 },
28195                 {
28196                     tag : 'div',
28197                     cls : 'roo-document-viewer-footer',
28198                     cn : {
28199                         tag : 'div',
28200                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
28201                         cn : [
28202                             {
28203                                 tag : 'div',
28204                                 cls : 'btn-group roo-document-viewer-download',
28205                                 cn : [
28206                                     {
28207                                         tag : 'button',
28208                                         cls : 'btn btn-default',
28209                                         html : '<i class="fa fa-download"></i>'
28210                                     }
28211                                 ]
28212                             },
28213                             {
28214                                 tag : 'div',
28215                                 cls : 'btn-group roo-document-viewer-trash',
28216                                 cn : [
28217                                     {
28218                                         tag : 'button',
28219                                         cls : 'btn btn-default',
28220                                         html : '<i class="fa fa-trash"></i>'
28221                                     }
28222                                 ]
28223                             }
28224                         ]
28225                     }
28226                 }
28227             ]
28228         };
28229         
28230         return cfg;
28231     },
28232     
28233     initEvents : function()
28234     {
28235         
28236         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
28237         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
28238         
28239         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
28240         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
28241         
28242         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
28243         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
28244         
28245         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
28246         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
28247         
28248         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
28249         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
28250         
28251         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
28252         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
28253         
28254         this.bodyEl.on('click', this.onClick, this);
28255         this.downloadBtn.on('click', this.onDownload, this);
28256         this.trashBtn.on('click', this.onTrash, this);
28257         
28258         this.downloadBtn.hide();
28259         this.trashBtn.hide();
28260         
28261         if(this.showDownload){
28262             this.downloadBtn.show();
28263         }
28264         
28265         if(this.showTrash){
28266             this.trashBtn.show();
28267         }
28268         
28269         if(!this.showDownload && !this.showTrash) {
28270             this.footerEl.hide();
28271         }
28272         
28273     },
28274     
28275     initial : function()
28276     {
28277 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
28278         
28279         
28280         this.fireEvent('initial', this);
28281         
28282     },
28283     
28284     onClick : function(e)
28285     {
28286         e.preventDefault();
28287         
28288         this.fireEvent('click', this);
28289     },
28290     
28291     onDownload : function(e)
28292     {
28293         e.preventDefault();
28294         
28295         this.fireEvent('download', this);
28296     },
28297     
28298     onTrash : function(e)
28299     {
28300         e.preventDefault();
28301         
28302         this.fireEvent('trash', this);
28303     }
28304     
28305 });
28306 /*
28307  * - LGPL
28308  *
28309  * nav progress bar
28310  * 
28311  */
28312
28313 /**
28314  * @class Roo.bootstrap.NavProgressBar
28315  * @extends Roo.bootstrap.Component
28316  * Bootstrap NavProgressBar class
28317  * 
28318  * @constructor
28319  * Create a new nav progress bar
28320  * @param {Object} config The config object
28321  */
28322
28323 Roo.bootstrap.NavProgressBar = function(config){
28324     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
28325
28326     this.bullets = this.bullets || [];
28327    
28328 //    Roo.bootstrap.NavProgressBar.register(this);
28329      this.addEvents({
28330         /**
28331              * @event changed
28332              * Fires when the active item changes
28333              * @param {Roo.bootstrap.NavProgressBar} this
28334              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
28335              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
28336          */
28337         'changed': true
28338      });
28339     
28340 };
28341
28342 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
28343     
28344     bullets : [],
28345     barItems : [],
28346     
28347     getAutoCreate : function()
28348     {
28349         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
28350         
28351         cfg = {
28352             tag : 'div',
28353             cls : 'roo-navigation-bar-group',
28354             cn : [
28355                 {
28356                     tag : 'div',
28357                     cls : 'roo-navigation-top-bar'
28358                 },
28359                 {
28360                     tag : 'div',
28361                     cls : 'roo-navigation-bullets-bar',
28362                     cn : [
28363                         {
28364                             tag : 'ul',
28365                             cls : 'roo-navigation-bar'
28366                         }
28367                     ]
28368                 },
28369                 
28370                 {
28371                     tag : 'div',
28372                     cls : 'roo-navigation-bottom-bar'
28373                 }
28374             ]
28375             
28376         };
28377         
28378         return cfg;
28379         
28380     },
28381     
28382     initEvents: function() 
28383     {
28384         
28385     },
28386     
28387     onRender : function(ct, position) 
28388     {
28389         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28390         
28391         if(this.bullets.length){
28392             Roo.each(this.bullets, function(b){
28393                this.addItem(b);
28394             }, this);
28395         }
28396         
28397         this.format();
28398         
28399     },
28400     
28401     addItem : function(cfg)
28402     {
28403         var item = new Roo.bootstrap.NavProgressItem(cfg);
28404         
28405         item.parentId = this.id;
28406         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
28407         
28408         if(cfg.html){
28409             var top = new Roo.bootstrap.Element({
28410                 tag : 'div',
28411                 cls : 'roo-navigation-bar-text'
28412             });
28413             
28414             var bottom = new Roo.bootstrap.Element({
28415                 tag : 'div',
28416                 cls : 'roo-navigation-bar-text'
28417             });
28418             
28419             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
28420             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
28421             
28422             var topText = new Roo.bootstrap.Element({
28423                 tag : 'span',
28424                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
28425             });
28426             
28427             var bottomText = new Roo.bootstrap.Element({
28428                 tag : 'span',
28429                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
28430             });
28431             
28432             topText.onRender(top.el, null);
28433             bottomText.onRender(bottom.el, null);
28434             
28435             item.topEl = top;
28436             item.bottomEl = bottom;
28437         }
28438         
28439         this.barItems.push(item);
28440         
28441         return item;
28442     },
28443     
28444     getActive : function()
28445     {
28446         var active = false;
28447         
28448         Roo.each(this.barItems, function(v){
28449             
28450             if (!v.isActive()) {
28451                 return;
28452             }
28453             
28454             active = v;
28455             return false;
28456             
28457         });
28458         
28459         return active;
28460     },
28461     
28462     setActiveItem : function(item)
28463     {
28464         var prev = false;
28465         
28466         Roo.each(this.barItems, function(v){
28467             if (v.rid == item.rid) {
28468                 return ;
28469             }
28470             
28471             if (v.isActive()) {
28472                 v.setActive(false);
28473                 prev = v;
28474             }
28475         });
28476
28477         item.setActive(true);
28478         
28479         this.fireEvent('changed', this, item, prev);
28480     },
28481     
28482     getBarItem: function(rid)
28483     {
28484         var ret = false;
28485         
28486         Roo.each(this.barItems, function(e) {
28487             if (e.rid != rid) {
28488                 return;
28489             }
28490             
28491             ret =  e;
28492             return false;
28493         });
28494         
28495         return ret;
28496     },
28497     
28498     indexOfItem : function(item)
28499     {
28500         var index = false;
28501         
28502         Roo.each(this.barItems, function(v, i){
28503             
28504             if (v.rid != item.rid) {
28505                 return;
28506             }
28507             
28508             index = i;
28509             return false
28510         });
28511         
28512         return index;
28513     },
28514     
28515     setActiveNext : function()
28516     {
28517         var i = this.indexOfItem(this.getActive());
28518         
28519         if (i > this.barItems.length) {
28520             return;
28521         }
28522         
28523         this.setActiveItem(this.barItems[i+1]);
28524     },
28525     
28526     setActivePrev : function()
28527     {
28528         var i = this.indexOfItem(this.getActive());
28529         
28530         if (i  < 1) {
28531             return;
28532         }
28533         
28534         this.setActiveItem(this.barItems[i-1]);
28535     },
28536     
28537     format : function()
28538     {
28539         if(!this.barItems.length){
28540             return;
28541         }
28542      
28543         var width = 100 / this.barItems.length;
28544         
28545         Roo.each(this.barItems, function(i){
28546             i.el.setStyle('width', width + '%');
28547             i.topEl.el.setStyle('width', width + '%');
28548             i.bottomEl.el.setStyle('width', width + '%');
28549         }, this);
28550         
28551     }
28552     
28553 });
28554 /*
28555  * - LGPL
28556  *
28557  * Nav Progress Item
28558  * 
28559  */
28560
28561 /**
28562  * @class Roo.bootstrap.NavProgressItem
28563  * @extends Roo.bootstrap.Component
28564  * Bootstrap NavProgressItem class
28565  * @cfg {String} rid the reference id
28566  * @cfg {Boolean} active (true|false) Is item active default false
28567  * @cfg {Boolean} disabled (true|false) Is item active default false
28568  * @cfg {String} html
28569  * @cfg {String} position (top|bottom) text position default bottom
28570  * @cfg {String} icon show icon instead of number
28571  * 
28572  * @constructor
28573  * Create a new NavProgressItem
28574  * @param {Object} config The config object
28575  */
28576 Roo.bootstrap.NavProgressItem = function(config){
28577     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
28578     this.addEvents({
28579         // raw events
28580         /**
28581          * @event click
28582          * The raw click event for the entire grid.
28583          * @param {Roo.bootstrap.NavProgressItem} this
28584          * @param {Roo.EventObject} e
28585          */
28586         "click" : true
28587     });
28588    
28589 };
28590
28591 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
28592     
28593     rid : '',
28594     active : false,
28595     disabled : false,
28596     html : '',
28597     position : 'bottom',
28598     icon : false,
28599     
28600     getAutoCreate : function()
28601     {
28602         var iconCls = 'roo-navigation-bar-item-icon';
28603         
28604         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
28605         
28606         var cfg = {
28607             tag: 'li',
28608             cls: 'roo-navigation-bar-item',
28609             cn : [
28610                 {
28611                     tag : 'i',
28612                     cls : iconCls
28613                 }
28614             ]
28615         };
28616         
28617         if(this.active){
28618             cfg.cls += ' active';
28619         }
28620         if(this.disabled){
28621             cfg.cls += ' disabled';
28622         }
28623         
28624         return cfg;
28625     },
28626     
28627     disable : function()
28628     {
28629         this.setDisabled(true);
28630     },
28631     
28632     enable : function()
28633     {
28634         this.setDisabled(false);
28635     },
28636     
28637     initEvents: function() 
28638     {
28639         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
28640         
28641         this.iconEl.on('click', this.onClick, this);
28642     },
28643     
28644     onClick : function(e)
28645     {
28646         e.preventDefault();
28647         
28648         if(this.disabled){
28649             return;
28650         }
28651         
28652         if(this.fireEvent('click', this, e) === false){
28653             return;
28654         };
28655         
28656         this.parent().setActiveItem(this);
28657     },
28658     
28659     isActive: function () 
28660     {
28661         return this.active;
28662     },
28663     
28664     setActive : function(state)
28665     {
28666         if(this.active == state){
28667             return;
28668         }
28669         
28670         this.active = state;
28671         
28672         if (state) {
28673             this.el.addClass('active');
28674             return;
28675         }
28676         
28677         this.el.removeClass('active');
28678         
28679         return;
28680     },
28681     
28682     setDisabled : function(state)
28683     {
28684         if(this.disabled == state){
28685             return;
28686         }
28687         
28688         this.disabled = state;
28689         
28690         if (state) {
28691             this.el.addClass('disabled');
28692             return;
28693         }
28694         
28695         this.el.removeClass('disabled');
28696     },
28697     
28698     tooltipEl : function()
28699     {
28700         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
28701     }
28702 });
28703  
28704
28705  /*
28706  * - LGPL
28707  *
28708  * FieldLabel
28709  * 
28710  */
28711
28712 /**
28713  * @class Roo.bootstrap.FieldLabel
28714  * @extends Roo.bootstrap.Component
28715  * Bootstrap FieldLabel class
28716  * @cfg {String} html contents of the element
28717  * @cfg {String} tag tag of the element default label
28718  * @cfg {String} cls class of the element
28719  * @cfg {String} target label target 
28720  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
28721  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
28722  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
28723  * @cfg {String} iconTooltip default "This field is required"
28724  * 
28725  * @constructor
28726  * Create a new FieldLabel
28727  * @param {Object} config The config object
28728  */
28729
28730 Roo.bootstrap.FieldLabel = function(config){
28731     Roo.bootstrap.Element.superclass.constructor.call(this, config);
28732     
28733     this.addEvents({
28734             /**
28735              * @event invalid
28736              * Fires after the field has been marked as invalid.
28737              * @param {Roo.form.FieldLabel} this
28738              * @param {String} msg The validation message
28739              */
28740             invalid : true,
28741             /**
28742              * @event valid
28743              * Fires after the field has been validated with no errors.
28744              * @param {Roo.form.FieldLabel} this
28745              */
28746             valid : true
28747         });
28748 };
28749
28750 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
28751     
28752     tag: 'label',
28753     cls: '',
28754     html: '',
28755     target: '',
28756     allowBlank : true,
28757     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
28758     validClass : 'text-success fa fa-lg fa-check',
28759     iconTooltip : 'This field is required',
28760     
28761     getAutoCreate : function(){
28762         
28763         var cfg = {
28764             tag : this.tag,
28765             cls : 'roo-bootstrap-field-label ' + this.cls,
28766             for : this.target,
28767             cn : [
28768                 {
28769                     tag : 'i',
28770                     cls : '',
28771                     tooltip : this.iconTooltip
28772                 },
28773                 {
28774                     tag : 'span',
28775                     html : this.html
28776                 }
28777             ] 
28778         };
28779         
28780         return cfg;
28781     },
28782     
28783     initEvents: function() 
28784     {
28785         Roo.bootstrap.Element.superclass.initEvents.call(this);
28786         
28787         this.iconEl = this.el.select('i', true).first();
28788         
28789         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
28790         
28791         Roo.bootstrap.FieldLabel.register(this);
28792     },
28793     
28794     /**
28795      * Mark this field as valid
28796      */
28797     markValid : function()
28798     {
28799         this.iconEl.show();
28800         
28801         this.iconEl.removeClass(this.invalidClass);
28802         
28803         this.iconEl.addClass(this.validClass);
28804         
28805         this.fireEvent('valid', this);
28806     },
28807     
28808     /**
28809      * Mark this field as invalid
28810      * @param {String} msg The validation message
28811      */
28812     markInvalid : function(msg)
28813     {
28814         this.iconEl.show();
28815         
28816         this.iconEl.removeClass(this.validClass);
28817         
28818         this.iconEl.addClass(this.invalidClass);
28819         
28820         this.fireEvent('invalid', this, msg);
28821     }
28822     
28823    
28824 });
28825
28826 Roo.apply(Roo.bootstrap.FieldLabel, {
28827     
28828     groups: {},
28829     
28830      /**
28831     * register a FieldLabel Group
28832     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28833     */
28834     register : function(label)
28835     {
28836         if(this.groups.hasOwnProperty(label.target)){
28837             return;
28838         }
28839      
28840         this.groups[label.target] = label;
28841         
28842     },
28843     /**
28844     * fetch a FieldLabel Group based on the target
28845     * @param {string} target
28846     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28847     */
28848     get: function(target) {
28849         if (typeof(this.groups[target]) == 'undefined') {
28850             return false;
28851         }
28852         
28853         return this.groups[target] ;
28854     }
28855 });
28856
28857  
28858
28859  /*
28860  * - LGPL
28861  *
28862  * page DateSplitField.
28863  * 
28864  */
28865
28866
28867 /**
28868  * @class Roo.bootstrap.DateSplitField
28869  * @extends Roo.bootstrap.Component
28870  * Bootstrap DateSplitField class
28871  * @cfg {string} fieldLabel - the label associated
28872  * @cfg {Number} labelWidth set the width of label (0-12)
28873  * @cfg {String} labelAlign (top|left)
28874  * @cfg {Boolean} dayAllowBlank (true|false) default false
28875  * @cfg {Boolean} monthAllowBlank (true|false) default false
28876  * @cfg {Boolean} yearAllowBlank (true|false) default false
28877  * @cfg {string} dayPlaceholder 
28878  * @cfg {string} monthPlaceholder
28879  * @cfg {string} yearPlaceholder
28880  * @cfg {string} dayFormat default 'd'
28881  * @cfg {string} monthFormat default 'm'
28882  * @cfg {string} yearFormat default 'Y'
28883
28884  *     
28885  * @constructor
28886  * Create a new DateSplitField
28887  * @param {Object} config The config object
28888  */
28889
28890 Roo.bootstrap.DateSplitField = function(config){
28891     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28892     
28893     this.addEvents({
28894         // raw events
28895          /**
28896          * @event years
28897          * getting the data of years
28898          * @param {Roo.bootstrap.DateSplitField} this
28899          * @param {Object} years
28900          */
28901         "years" : true,
28902         /**
28903          * @event days
28904          * getting the data of days
28905          * @param {Roo.bootstrap.DateSplitField} this
28906          * @param {Object} days
28907          */
28908         "days" : true,
28909         /**
28910          * @event invalid
28911          * Fires after the field has been marked as invalid.
28912          * @param {Roo.form.Field} this
28913          * @param {String} msg The validation message
28914          */
28915         invalid : true,
28916        /**
28917          * @event valid
28918          * Fires after the field has been validated with no errors.
28919          * @param {Roo.form.Field} this
28920          */
28921         valid : true
28922     });
28923 };
28924
28925 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
28926     
28927     fieldLabel : '',
28928     labelAlign : 'top',
28929     labelWidth : 3,
28930     dayAllowBlank : false,
28931     monthAllowBlank : false,
28932     yearAllowBlank : false,
28933     dayPlaceholder : '',
28934     monthPlaceholder : '',
28935     yearPlaceholder : '',
28936     dayFormat : 'd',
28937     monthFormat : 'm',
28938     yearFormat : 'Y',
28939     isFormField : true,
28940     
28941     getAutoCreate : function()
28942     {
28943         var cfg = {
28944             tag : 'div',
28945             cls : 'row roo-date-split-field-group',
28946             cn : [
28947                 {
28948                     tag : 'input',
28949                     type : 'hidden',
28950                     cls : 'form-hidden-field roo-date-split-field-group-value',
28951                     name : this.name
28952                 }
28953             ]
28954         };
28955         
28956         if(this.fieldLabel){
28957             cfg.cn.push({
28958                 tag : 'div',
28959                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
28960                 cn : [
28961                     {
28962                         tag : 'label',
28963                         html : this.fieldLabel
28964                     }
28965                 ]
28966             });
28967         }
28968         
28969         Roo.each(['day', 'month', 'year'], function(t){
28970             cfg.cn.push({
28971                 tag : 'div',
28972                 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
28973             });
28974         }, this);
28975         
28976         return cfg;
28977     },
28978     
28979     inputEl: function ()
28980     {
28981         return this.el.select('.roo-date-split-field-group-value', true).first();
28982     },
28983     
28984     onRender : function(ct, position) 
28985     {
28986         var _this = this;
28987         
28988         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28989         
28990         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
28991         
28992         this.dayField = new Roo.bootstrap.ComboBox({
28993             allowBlank : this.dayAllowBlank,
28994             alwaysQuery : true,
28995             displayField : 'value',
28996             editable : false,
28997             fieldLabel : '',
28998             forceSelection : true,
28999             mode : 'local',
29000             placeholder : this.dayPlaceholder,
29001             selectOnFocus : true,
29002             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29003             triggerAction : 'all',
29004             typeAhead : true,
29005             valueField : 'value',
29006             store : new Roo.data.SimpleStore({
29007                 data : (function() {    
29008                     var days = [];
29009                     _this.fireEvent('days', _this, days);
29010                     return days;
29011                 })(),
29012                 fields : [ 'value' ]
29013             }),
29014             listeners : {
29015                 select : function (_self, record, index)
29016                 {
29017                     _this.setValue(_this.getValue());
29018                 }
29019             }
29020         });
29021
29022         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
29023         
29024         this.monthField = new Roo.bootstrap.MonthField({
29025             after : '<i class=\"fa fa-calendar\"></i>',
29026             allowBlank : this.monthAllowBlank,
29027             placeholder : this.monthPlaceholder,
29028             readOnly : true,
29029             listeners : {
29030                 render : function (_self)
29031                 {
29032                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
29033                         e.preventDefault();
29034                         _self.focus();
29035                     });
29036                 },
29037                 select : function (_self, oldvalue, newvalue)
29038                 {
29039                     _this.setValue(_this.getValue());
29040                 }
29041             }
29042         });
29043         
29044         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
29045         
29046         this.yearField = new Roo.bootstrap.ComboBox({
29047             allowBlank : this.yearAllowBlank,
29048             alwaysQuery : true,
29049             displayField : 'value',
29050             editable : false,
29051             fieldLabel : '',
29052             forceSelection : true,
29053             mode : 'local',
29054             placeholder : this.yearPlaceholder,
29055             selectOnFocus : true,
29056             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29057             triggerAction : 'all',
29058             typeAhead : true,
29059             valueField : 'value',
29060             store : new Roo.data.SimpleStore({
29061                 data : (function() {
29062                     var years = [];
29063                     _this.fireEvent('years', _this, years);
29064                     return years;
29065                 })(),
29066                 fields : [ 'value' ]
29067             }),
29068             listeners : {
29069                 select : function (_self, record, index)
29070                 {
29071                     _this.setValue(_this.getValue());
29072                 }
29073             }
29074         });
29075
29076         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
29077     },
29078     
29079     setValue : function(v, format)
29080     {
29081         this.inputEl.dom.value = v;
29082         
29083         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
29084         
29085         var d = Date.parseDate(v, f);
29086         
29087         if(!d){
29088             this.validate();
29089             return;
29090         }
29091         
29092         this.setDay(d.format(this.dayFormat));
29093         this.setMonth(d.format(this.monthFormat));
29094         this.setYear(d.format(this.yearFormat));
29095         
29096         this.validate();
29097         
29098         return;
29099     },
29100     
29101     setDay : function(v)
29102     {
29103         this.dayField.setValue(v);
29104         this.inputEl.dom.value = this.getValue();
29105         this.validate();
29106         return;
29107     },
29108     
29109     setMonth : function(v)
29110     {
29111         this.monthField.setValue(v, true);
29112         this.inputEl.dom.value = this.getValue();
29113         this.validate();
29114         return;
29115     },
29116     
29117     setYear : function(v)
29118     {
29119         this.yearField.setValue(v);
29120         this.inputEl.dom.value = this.getValue();
29121         this.validate();
29122         return;
29123     },
29124     
29125     getDay : function()
29126     {
29127         return this.dayField.getValue();
29128     },
29129     
29130     getMonth : function()
29131     {
29132         return this.monthField.getValue();
29133     },
29134     
29135     getYear : function()
29136     {
29137         return this.yearField.getValue();
29138     },
29139     
29140     getValue : function()
29141     {
29142         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
29143         
29144         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
29145         
29146         return date;
29147     },
29148     
29149     reset : function()
29150     {
29151         this.setDay('');
29152         this.setMonth('');
29153         this.setYear('');
29154         this.inputEl.dom.value = '';
29155         this.validate();
29156         return;
29157     },
29158     
29159     validate : function()
29160     {
29161         var d = this.dayField.validate();
29162         var m = this.monthField.validate();
29163         var y = this.yearField.validate();
29164         
29165         var valid = true;
29166         
29167         if(
29168                 (!this.dayAllowBlank && !d) ||
29169                 (!this.monthAllowBlank && !m) ||
29170                 (!this.yearAllowBlank && !y)
29171         ){
29172             valid = false;
29173         }
29174         
29175         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
29176             return valid;
29177         }
29178         
29179         if(valid){
29180             this.markValid();
29181             return valid;
29182         }
29183         
29184         this.markInvalid();
29185         
29186         return valid;
29187     },
29188     
29189     markValid : function()
29190     {
29191         
29192         var label = this.el.select('label', true).first();
29193         var icon = this.el.select('i.fa-star', true).first();
29194
29195         if(label && icon){
29196             icon.remove();
29197         }
29198         
29199         this.fireEvent('valid', this);
29200     },
29201     
29202      /**
29203      * Mark this field as invalid
29204      * @param {String} msg The validation message
29205      */
29206     markInvalid : function(msg)
29207     {
29208         
29209         var label = this.el.select('label', true).first();
29210         var icon = this.el.select('i.fa-star', true).first();
29211
29212         if(label && !icon){
29213             this.el.select('.roo-date-split-field-label', true).createChild({
29214                 tag : 'i',
29215                 cls : 'text-danger fa fa-lg fa-star',
29216                 tooltip : 'This field is required',
29217                 style : 'margin-right:5px;'
29218             }, label, true);
29219         }
29220         
29221         this.fireEvent('invalid', this, msg);
29222     },
29223     
29224     clearInvalid : function()
29225     {
29226         var label = this.el.select('label', true).first();
29227         var icon = this.el.select('i.fa-star', true).first();
29228
29229         if(label && icon){
29230             icon.remove();
29231         }
29232         
29233         this.fireEvent('valid', this);
29234     },
29235     
29236     getName: function()
29237     {
29238         return this.name;
29239     }
29240     
29241 });
29242
29243  /**
29244  *
29245  * This is based on 
29246  * http://masonry.desandro.com
29247  *
29248  * The idea is to render all the bricks based on vertical width...
29249  *
29250  * The original code extends 'outlayer' - we might need to use that....
29251  * 
29252  */
29253
29254
29255 /**
29256  * @class Roo.bootstrap.LayoutMasonry
29257  * @extends Roo.bootstrap.Component
29258  * Bootstrap Layout Masonry class
29259  * 
29260  * @constructor
29261  * Create a new Element
29262  * @param {Object} config The config object
29263  */
29264
29265 Roo.bootstrap.LayoutMasonry = function(config){
29266     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
29267     
29268     this.bricks = [];
29269     
29270 };
29271
29272 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
29273     
29274     /**
29275      * @cfg {Boolean} isLayoutInstant = no animation?
29276      */   
29277     isLayoutInstant : false, // needed?
29278    
29279     /**
29280      * @cfg {Number} boxWidth  width of the columns
29281      */   
29282     boxWidth : 450,
29283     
29284       /**
29285      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
29286      */   
29287     boxHeight : 0,
29288     
29289     /**
29290      * @cfg {Number} padWidth padding below box..
29291      */   
29292     padWidth : 10, 
29293     
29294     /**
29295      * @cfg {Number} gutter gutter width..
29296      */   
29297     gutter : 10,
29298     
29299      /**
29300      * @cfg {Number} maxCols maximum number of columns
29301      */   
29302     
29303     maxCols: 0,
29304     
29305     /**
29306      * @cfg {Boolean} isAutoInitial defalut true
29307      */   
29308     isAutoInitial : true, 
29309     
29310     containerWidth: 0,
29311     
29312     /**
29313      * @cfg {Boolean} isHorizontal defalut false
29314      */   
29315     isHorizontal : false, 
29316
29317     currentSize : null,
29318     
29319     tag: 'div',
29320     
29321     cls: '',
29322     
29323     bricks: null, //CompositeElement
29324     
29325     cols : 1,
29326     
29327     _isLayoutInited : false,
29328     
29329 //    isAlternative : false, // only use for vertical layout...
29330     
29331     /**
29332      * @cfg {Number} alternativePadWidth padding below box..
29333      */   
29334     alternativePadWidth : 50, 
29335     
29336     getAutoCreate : function(){
29337         
29338         var cfg = {
29339             tag: this.tag,
29340             cls: 'blog-masonary-wrapper ' + this.cls,
29341             cn : {
29342                 cls : 'mas-boxes masonary'
29343             }
29344         };
29345         
29346         return cfg;
29347     },
29348     
29349     getChildContainer: function( )
29350     {
29351         if (this.boxesEl) {
29352             return this.boxesEl;
29353         }
29354         
29355         this.boxesEl = this.el.select('.mas-boxes').first();
29356         
29357         return this.boxesEl;
29358     },
29359     
29360     
29361     initEvents : function()
29362     {
29363         var _this = this;
29364         
29365         if(this.isAutoInitial){
29366             Roo.log('hook children rendered');
29367             this.on('childrenrendered', function() {
29368                 Roo.log('children rendered');
29369                 _this.initial();
29370             } ,this);
29371         }
29372     },
29373     
29374     initial : function()
29375     {
29376         this.currentSize = this.el.getBox(true);
29377         
29378         Roo.EventManager.onWindowResize(this.resize, this); 
29379
29380         if(!this.isAutoInitial){
29381             this.layout();
29382             return;
29383         }
29384         
29385         this.layout();
29386         
29387         return;
29388         //this.layout.defer(500,this);
29389         
29390     },
29391     
29392     resize : function()
29393     {
29394         Roo.log('resize');
29395         
29396         var cs = this.el.getBox(true);
29397         
29398         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29399             Roo.log("no change in with or X");
29400             return;
29401         }
29402         
29403         this.currentSize = cs;
29404         
29405         this.layout();
29406         
29407     },
29408     
29409     layout : function()
29410     {   
29411         this._resetLayout();
29412         
29413         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29414         
29415         this.layoutItems( isInstant );
29416       
29417         this._isLayoutInited = true;
29418         
29419     },
29420     
29421     _resetLayout : function()
29422     {
29423         if(this.isHorizontal){
29424             this.horizontalMeasureColumns();
29425             return;
29426         }
29427         
29428         this.verticalMeasureColumns();
29429         
29430     },
29431     
29432     verticalMeasureColumns : function()
29433     {
29434         this.getContainerWidth();
29435         
29436 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29437 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
29438 //            return;
29439 //        }
29440         
29441         var boxWidth = this.boxWidth + this.padWidth;
29442         
29443         if(this.containerWidth < this.boxWidth){
29444             boxWidth = this.containerWidth
29445         }
29446         
29447         var containerWidth = this.containerWidth;
29448         
29449         var cols = Math.floor(containerWidth / boxWidth);
29450         
29451         this.cols = Math.max( cols, 1 );
29452         
29453         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
29454         
29455         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
29456         
29457         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
29458         
29459         this.colWidth = boxWidth + avail - this.padWidth;
29460         
29461         this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
29462         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
29463     },
29464     
29465     horizontalMeasureColumns : function()
29466     {
29467         this.getContainerWidth();
29468         
29469         var boxWidth = this.boxWidth;
29470         
29471         if(this.containerWidth < boxWidth){
29472             boxWidth = this.containerWidth;
29473         }
29474         
29475         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
29476         
29477         this.el.setHeight(boxWidth);
29478         
29479     },
29480     
29481     getContainerWidth : function()
29482     {
29483         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
29484     },
29485     
29486     layoutItems : function( isInstant )
29487     {
29488         var items = Roo.apply([], this.bricks);
29489         
29490         if(this.isHorizontal){
29491             this._horizontalLayoutItems( items , isInstant );
29492             return;
29493         }
29494         
29495 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29496 //            this._verticalAlternativeLayoutItems( items , isInstant );
29497 //            return;
29498 //        }
29499         
29500         this._verticalLayoutItems( items , isInstant );
29501         
29502     },
29503     
29504     _verticalLayoutItems : function ( items , isInstant)
29505     {
29506         if ( !items || !items.length ) {
29507             return;
29508         }
29509         
29510         var standard = [
29511             ['xs', 'xs', 'xs', 'tall'],
29512             ['xs', 'xs', 'tall'],
29513             ['xs', 'xs', 'sm'],
29514             ['xs', 'xs', 'xs'],
29515             ['xs', 'tall'],
29516             ['xs', 'sm'],
29517             ['xs', 'xs'],
29518             ['xs'],
29519             
29520             ['sm', 'xs', 'xs'],
29521             ['sm', 'xs'],
29522             ['sm'],
29523             
29524             ['tall', 'xs', 'xs', 'xs'],
29525             ['tall', 'xs', 'xs'],
29526             ['tall', 'xs'],
29527             ['tall']
29528             
29529         ];
29530         
29531         var queue = [];
29532         
29533         var boxes = [];
29534         
29535         var box = [];
29536         
29537         Roo.each(items, function(item, k){
29538             
29539             switch (item.size) {
29540                 // these layouts take up a full box,
29541                 case 'md' :
29542                 case 'md-left' :
29543                 case 'md-right' :
29544                 case 'wide' :
29545                     
29546                     if(box.length){
29547                         boxes.push(box);
29548                         box = [];
29549                     }
29550                     
29551                     boxes.push([item]);
29552                     
29553                     break;
29554                     
29555                 case 'xs' :
29556                 case 'sm' :
29557                 case 'tall' :
29558                     
29559                     box.push(item);
29560                     
29561                     break;
29562                 default :
29563                     break;
29564                     
29565             }
29566             
29567         }, this);
29568         
29569         if(box.length){
29570             boxes.push(box);
29571             box = [];
29572         }
29573         
29574         var filterPattern = function(box, length)
29575         {
29576             if(!box.length){
29577                 return;
29578             }
29579             
29580             var match = false;
29581             
29582             var pattern = box.slice(0, length);
29583             
29584             var format = [];
29585             
29586             Roo.each(pattern, function(i){
29587                 format.push(i.size);
29588             }, this);
29589             
29590             Roo.each(standard, function(s){
29591                 
29592                 if(String(s) != String(format)){
29593                     return;
29594                 }
29595                 
29596                 match = true;
29597                 return false;
29598                 
29599             }, this);
29600             
29601             if(!match && length == 1){
29602                 return;
29603             }
29604             
29605             if(!match){
29606                 filterPattern(box, length - 1);
29607                 return;
29608             }
29609                 
29610             queue.push(pattern);
29611
29612             box = box.slice(length, box.length);
29613
29614             filterPattern(box, 4);
29615
29616             return;
29617             
29618         }
29619         
29620         Roo.each(boxes, function(box, k){
29621             
29622             if(!box.length){
29623                 return;
29624             }
29625             
29626             if(box.length == 1){
29627                 queue.push(box);
29628                 return;
29629             }
29630             
29631             filterPattern(box, 4);
29632             
29633         }, this);
29634         
29635         this._processVerticalLayoutQueue( queue, isInstant );
29636         
29637     },
29638     
29639 //    _verticalAlternativeLayoutItems : function( items , isInstant )
29640 //    {
29641 //        if ( !items || !items.length ) {
29642 //            return;
29643 //        }
29644 //
29645 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
29646 //        
29647 //    },
29648     
29649     _horizontalLayoutItems : function ( items , isInstant)
29650     {
29651         if ( !items || !items.length || items.length < 3) {
29652             return;
29653         }
29654         
29655         items.reverse();
29656         
29657         var eItems = items.slice(0, 3);
29658         
29659         items = items.slice(3, items.length);
29660         
29661         var standard = [
29662             ['xs', 'xs', 'xs', 'wide'],
29663             ['xs', 'xs', 'wide'],
29664             ['xs', 'xs', 'sm'],
29665             ['xs', 'xs', 'xs'],
29666             ['xs', 'wide'],
29667             ['xs', 'sm'],
29668             ['xs', 'xs'],
29669             ['xs'],
29670             
29671             ['sm', 'xs', 'xs'],
29672             ['sm', 'xs'],
29673             ['sm'],
29674             
29675             ['wide', 'xs', 'xs', 'xs'],
29676             ['wide', 'xs', 'xs'],
29677             ['wide', 'xs'],
29678             ['wide'],
29679             
29680             ['wide-thin']
29681         ];
29682         
29683         var queue = [];
29684         
29685         var boxes = [];
29686         
29687         var box = [];
29688         
29689         Roo.each(items, function(item, k){
29690             
29691             switch (item.size) {
29692                 case 'md' :
29693                 case 'md-left' :
29694                 case 'md-right' :
29695                 case 'tall' :
29696                     
29697                     if(box.length){
29698                         boxes.push(box);
29699                         box = [];
29700                     }
29701                     
29702                     boxes.push([item]);
29703                     
29704                     break;
29705                     
29706                 case 'xs' :
29707                 case 'sm' :
29708                 case 'wide' :
29709                 case 'wide-thin' :
29710                     
29711                     box.push(item);
29712                     
29713                     break;
29714                 default :
29715                     break;
29716                     
29717             }
29718             
29719         }, this);
29720         
29721         if(box.length){
29722             boxes.push(box);
29723             box = [];
29724         }
29725         
29726         var filterPattern = function(box, length)
29727         {
29728             if(!box.length){
29729                 return;
29730             }
29731             
29732             var match = false;
29733             
29734             var pattern = box.slice(0, length);
29735             
29736             var format = [];
29737             
29738             Roo.each(pattern, function(i){
29739                 format.push(i.size);
29740             }, this);
29741             
29742             Roo.each(standard, function(s){
29743                 
29744                 if(String(s) != String(format)){
29745                     return;
29746                 }
29747                 
29748                 match = true;
29749                 return false;
29750                 
29751             }, this);
29752             
29753             if(!match && length == 1){
29754                 return;
29755             }
29756             
29757             if(!match){
29758                 filterPattern(box, length - 1);
29759                 return;
29760             }
29761                 
29762             queue.push(pattern);
29763
29764             box = box.slice(length, box.length);
29765
29766             filterPattern(box, 4);
29767
29768             return;
29769             
29770         }
29771         
29772         Roo.each(boxes, function(box, k){
29773             
29774             if(!box.length){
29775                 return;
29776             }
29777             
29778             if(box.length == 1){
29779                 queue.push(box);
29780                 return;
29781             }
29782             
29783             filterPattern(box, 4);
29784             
29785         }, this);
29786         
29787         
29788         var prune = [];
29789         
29790         var pos = this.el.getBox(true);
29791         
29792         var minX = pos.x;
29793         
29794         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29795         
29796         var hit_end = false;
29797         
29798         Roo.each(queue, function(box){
29799             
29800             if(hit_end){
29801                 
29802                 Roo.each(box, function(b){
29803                 
29804                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29805                     b.el.hide();
29806
29807                 }, this);
29808
29809                 return;
29810             }
29811             
29812             var mx = 0;
29813             
29814             Roo.each(box, function(b){
29815                 
29816                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29817                 b.el.show();
29818
29819                 mx = Math.max(mx, b.x);
29820                 
29821             }, this);
29822             
29823             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29824             
29825             if(maxX < minX){
29826                 
29827                 Roo.each(box, function(b){
29828                 
29829                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29830                     b.el.hide();
29831                     
29832                 }, this);
29833                 
29834                 hit_end = true;
29835                 
29836                 return;
29837             }
29838             
29839             prune.push(box);
29840             
29841         }, this);
29842         
29843         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29844     },
29845     
29846     /** Sets position of item in DOM
29847     * @param {Element} item
29848     * @param {Number} x - horizontal position
29849     * @param {Number} y - vertical position
29850     * @param {Boolean} isInstant - disables transitions
29851     */
29852     _processVerticalLayoutQueue : function( queue, isInstant )
29853     {
29854         var pos = this.el.getBox(true);
29855         var x = pos.x;
29856         var y = pos.y;
29857         var maxY = [];
29858         
29859         for (var i = 0; i < this.cols; i++){
29860             maxY[i] = pos.y;
29861         }
29862         
29863         Roo.each(queue, function(box, k){
29864             
29865             var col = k % this.cols;
29866             
29867             Roo.each(box, function(b,kk){
29868                 
29869                 b.el.position('absolute');
29870                 
29871                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29872                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29873                 
29874                 if(b.size == 'md-left' || b.size == 'md-right'){
29875                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29876                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29877                 }
29878                 
29879                 b.el.setWidth(width);
29880                 b.el.setHeight(height);
29881                 // iframe?
29882                 b.el.select('iframe',true).setSize(width,height);
29883                 
29884             }, this);
29885             
29886             for (var i = 0; i < this.cols; i++){
29887                 
29888                 if(maxY[i] < maxY[col]){
29889                     col = i;
29890                     continue;
29891                 }
29892                 
29893                 col = Math.min(col, i);
29894                 
29895             }
29896             
29897             x = pos.x + col * (this.colWidth + this.padWidth);
29898             
29899             y = maxY[col];
29900             
29901             var positions = [];
29902             
29903             switch (box.length){
29904                 case 1 :
29905                     positions = this.getVerticalOneBoxColPositions(x, y, box);
29906                     break;
29907                 case 2 :
29908                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
29909                     break;
29910                 case 3 :
29911                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
29912                     break;
29913                 case 4 :
29914                     positions = this.getVerticalFourBoxColPositions(x, y, box);
29915                     break;
29916                 default :
29917                     break;
29918             }
29919             
29920             Roo.each(box, function(b,kk){
29921                 
29922                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29923                 
29924                 var sz = b.el.getSize();
29925                 
29926                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29927                 
29928             }, this);
29929             
29930         }, this);
29931         
29932         var mY = 0;
29933         
29934         for (var i = 0; i < this.cols; i++){
29935             mY = Math.max(mY, maxY[i]);
29936         }
29937         
29938         this.el.setHeight(mY - pos.y);
29939         
29940     },
29941     
29942 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29943 //    {
29944 //        var pos = this.el.getBox(true);
29945 //        var x = pos.x;
29946 //        var y = pos.y;
29947 //        var maxX = pos.right;
29948 //        
29949 //        var maxHeight = 0;
29950 //        
29951 //        Roo.each(items, function(item, k){
29952 //            
29953 //            var c = k % 2;
29954 //            
29955 //            item.el.position('absolute');
29956 //                
29957 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29958 //
29959 //            item.el.setWidth(width);
29960 //
29961 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
29962 //
29963 //            item.el.setHeight(height);
29964 //            
29965 //            if(c == 0){
29966 //                item.el.setXY([x, y], isInstant ? false : true);
29967 //            } else {
29968 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
29969 //            }
29970 //            
29971 //            y = y + height + this.alternativePadWidth;
29972 //            
29973 //            maxHeight = maxHeight + height + this.alternativePadWidth;
29974 //            
29975 //        }, this);
29976 //        
29977 //        this.el.setHeight(maxHeight);
29978 //        
29979 //    },
29980     
29981     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
29982     {
29983         var pos = this.el.getBox(true);
29984         
29985         var minX = pos.x;
29986         var minY = pos.y;
29987         
29988         var maxX = pos.right;
29989         
29990         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
29991         
29992         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29993         
29994         Roo.each(queue, function(box, k){
29995             
29996             Roo.each(box, function(b, kk){
29997                 
29998                 b.el.position('absolute');
29999                 
30000                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30001                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30002                 
30003                 if(b.size == 'md-left' || b.size == 'md-right'){
30004                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30005                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30006                 }
30007                 
30008                 b.el.setWidth(width);
30009                 b.el.setHeight(height);
30010                 
30011             }, this);
30012             
30013             if(!box.length){
30014                 return;
30015             }
30016             
30017             var positions = [];
30018             
30019             switch (box.length){
30020                 case 1 :
30021                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
30022                     break;
30023                 case 2 :
30024                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
30025                     break;
30026                 case 3 :
30027                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
30028                     break;
30029                 case 4 :
30030                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
30031                     break;
30032                 default :
30033                     break;
30034             }
30035             
30036             Roo.each(box, function(b,kk){
30037                 
30038                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30039                 
30040                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
30041                 
30042             }, this);
30043             
30044         }, this);
30045         
30046     },
30047     
30048     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
30049     {
30050         Roo.each(eItems, function(b,k){
30051             
30052             b.size = (k == 0) ? 'sm' : 'xs';
30053             b.x = (k == 0) ? 2 : 1;
30054             b.y = (k == 0) ? 2 : 1;
30055             
30056             b.el.position('absolute');
30057             
30058             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30059                 
30060             b.el.setWidth(width);
30061             
30062             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30063             
30064             b.el.setHeight(height);
30065             
30066         }, this);
30067
30068         var positions = [];
30069         
30070         positions.push({
30071             x : maxX - this.unitWidth * 2 - this.gutter,
30072             y : minY
30073         });
30074         
30075         positions.push({
30076             x : maxX - this.unitWidth,
30077             y : minY + (this.unitWidth + this.gutter) * 2
30078         });
30079         
30080         positions.push({
30081             x : maxX - this.unitWidth * 3 - this.gutter * 2,
30082             y : minY
30083         });
30084         
30085         Roo.each(eItems, function(b,k){
30086             
30087             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
30088
30089         }, this);
30090         
30091     },
30092     
30093     getVerticalOneBoxColPositions : function(x, y, box)
30094     {
30095         var pos = [];
30096         
30097         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
30098         
30099         if(box[0].size == 'md-left'){
30100             rand = 0;
30101         }
30102         
30103         if(box[0].size == 'md-right'){
30104             rand = 1;
30105         }
30106         
30107         pos.push({
30108             x : x + (this.unitWidth + this.gutter) * rand,
30109             y : y
30110         });
30111         
30112         return pos;
30113     },
30114     
30115     getVerticalTwoBoxColPositions : function(x, y, box)
30116     {
30117         var pos = [];
30118         
30119         if(box[0].size == 'xs'){
30120             
30121             pos.push({
30122                 x : x,
30123                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
30124             });
30125
30126             pos.push({
30127                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
30128                 y : y
30129             });
30130             
30131             return pos;
30132             
30133         }
30134         
30135         pos.push({
30136             x : x,
30137             y : y
30138         });
30139
30140         pos.push({
30141             x : x + (this.unitWidth + this.gutter) * 2,
30142             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
30143         });
30144         
30145         return pos;
30146         
30147     },
30148     
30149     getVerticalThreeBoxColPositions : function(x, y, box)
30150     {
30151         var pos = [];
30152         
30153         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30154             
30155             pos.push({
30156                 x : x,
30157                 y : y
30158             });
30159
30160             pos.push({
30161                 x : x + (this.unitWidth + this.gutter) * 1,
30162                 y : y
30163             });
30164             
30165             pos.push({
30166                 x : x + (this.unitWidth + this.gutter) * 2,
30167                 y : y
30168             });
30169             
30170             return pos;
30171             
30172         }
30173         
30174         if(box[0].size == 'xs' && box[1].size == 'xs'){
30175             
30176             pos.push({
30177                 x : x,
30178                 y : y
30179             });
30180
30181             pos.push({
30182                 x : x,
30183                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
30184             });
30185             
30186             pos.push({
30187                 x : x + (this.unitWidth + this.gutter) * 1,
30188                 y : y
30189             });
30190             
30191             return pos;
30192             
30193         }
30194         
30195         pos.push({
30196             x : x,
30197             y : y
30198         });
30199
30200         pos.push({
30201             x : x + (this.unitWidth + this.gutter) * 2,
30202             y : y
30203         });
30204
30205         pos.push({
30206             x : x + (this.unitWidth + this.gutter) * 2,
30207             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
30208         });
30209             
30210         return pos;
30211         
30212     },
30213     
30214     getVerticalFourBoxColPositions : function(x, y, box)
30215     {
30216         var pos = [];
30217         
30218         if(box[0].size == 'xs'){
30219             
30220             pos.push({
30221                 x : x,
30222                 y : y
30223             });
30224
30225             pos.push({
30226                 x : x,
30227                 y : y + (this.unitHeight + this.gutter) * 1
30228             });
30229             
30230             pos.push({
30231                 x : x,
30232                 y : y + (this.unitHeight + this.gutter) * 2
30233             });
30234             
30235             pos.push({
30236                 x : x + (this.unitWidth + this.gutter) * 1,
30237                 y : y
30238             });
30239             
30240             return pos;
30241             
30242         }
30243         
30244         pos.push({
30245             x : x,
30246             y : y
30247         });
30248
30249         pos.push({
30250             x : x + (this.unitWidth + this.gutter) * 2,
30251             y : y
30252         });
30253
30254         pos.push({
30255             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
30256             y : y + (this.unitHeight + this.gutter) * 1
30257         });
30258
30259         pos.push({
30260             x : x + (this.unitWidth + this.gutter) * 2,
30261             y : y + (this.unitWidth + this.gutter) * 2
30262         });
30263
30264         return pos;
30265         
30266     },
30267     
30268     getHorizontalOneBoxColPositions : function(maxX, minY, box)
30269     {
30270         var pos = [];
30271         
30272         if(box[0].size == 'md-left'){
30273             pos.push({
30274                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30275                 y : minY
30276             });
30277             
30278             return pos;
30279         }
30280         
30281         if(box[0].size == 'md-right'){
30282             pos.push({
30283                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30284                 y : minY + (this.unitWidth + this.gutter) * 1
30285             });
30286             
30287             return pos;
30288         }
30289         
30290         var rand = Math.floor(Math.random() * (4 - box[0].y));
30291         
30292         pos.push({
30293             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30294             y : minY + (this.unitWidth + this.gutter) * rand
30295         });
30296         
30297         return pos;
30298         
30299     },
30300     
30301     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
30302     {
30303         var pos = [];
30304         
30305         if(box[0].size == 'xs'){
30306             
30307             pos.push({
30308                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30309                 y : minY
30310             });
30311
30312             pos.push({
30313                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30314                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
30315             });
30316             
30317             return pos;
30318             
30319         }
30320         
30321         pos.push({
30322             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30323             y : minY
30324         });
30325
30326         pos.push({
30327             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30328             y : minY + (this.unitWidth + this.gutter) * 2
30329         });
30330         
30331         return pos;
30332         
30333     },
30334     
30335     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
30336     {
30337         var pos = [];
30338         
30339         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30340             
30341             pos.push({
30342                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30343                 y : minY
30344             });
30345
30346             pos.push({
30347                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30348                 y : minY + (this.unitWidth + this.gutter) * 1
30349             });
30350             
30351             pos.push({
30352                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30353                 y : minY + (this.unitWidth + this.gutter) * 2
30354             });
30355             
30356             return pos;
30357             
30358         }
30359         
30360         if(box[0].size == 'xs' && box[1].size == 'xs'){
30361             
30362             pos.push({
30363                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30364                 y : minY
30365             });
30366
30367             pos.push({
30368                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30369                 y : minY
30370             });
30371             
30372             pos.push({
30373                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30374                 y : minY + (this.unitWidth + this.gutter) * 1
30375             });
30376             
30377             return pos;
30378             
30379         }
30380         
30381         pos.push({
30382             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30383             y : minY
30384         });
30385
30386         pos.push({
30387             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30388             y : minY + (this.unitWidth + this.gutter) * 2
30389         });
30390
30391         pos.push({
30392             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30393             y : minY + (this.unitWidth + this.gutter) * 2
30394         });
30395             
30396         return pos;
30397         
30398     },
30399     
30400     getHorizontalFourBoxColPositions : function(maxX, minY, box)
30401     {
30402         var pos = [];
30403         
30404         if(box[0].size == 'xs'){
30405             
30406             pos.push({
30407                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30408                 y : minY
30409             });
30410
30411             pos.push({
30412                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30413                 y : minY
30414             });
30415             
30416             pos.push({
30417                 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),
30418                 y : minY
30419             });
30420             
30421             pos.push({
30422                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
30423                 y : minY + (this.unitWidth + this.gutter) * 1
30424             });
30425             
30426             return pos;
30427             
30428         }
30429         
30430         pos.push({
30431             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30432             y : minY
30433         });
30434         
30435         pos.push({
30436             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30437             y : minY + (this.unitWidth + this.gutter) * 2
30438         });
30439         
30440         pos.push({
30441             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30442             y : minY + (this.unitWidth + this.gutter) * 2
30443         });
30444         
30445         pos.push({
30446             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),
30447             y : minY + (this.unitWidth + this.gutter) * 2
30448         });
30449
30450         return pos;
30451         
30452     }
30453     
30454 });
30455
30456  
30457
30458  /**
30459  *
30460  * This is based on 
30461  * http://masonry.desandro.com
30462  *
30463  * The idea is to render all the bricks based on vertical width...
30464  *
30465  * The original code extends 'outlayer' - we might need to use that....
30466  * 
30467  */
30468
30469
30470 /**
30471  * @class Roo.bootstrap.LayoutMasonryAuto
30472  * @extends Roo.bootstrap.Component
30473  * Bootstrap Layout Masonry class
30474  * 
30475  * @constructor
30476  * Create a new Element
30477  * @param {Object} config The config object
30478  */
30479
30480 Roo.bootstrap.LayoutMasonryAuto = function(config){
30481     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
30482 };
30483
30484 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
30485     
30486       /**
30487      * @cfg {Boolean} isFitWidth  - resize the width..
30488      */   
30489     isFitWidth : false,  // options..
30490     /**
30491      * @cfg {Boolean} isOriginLeft = left align?
30492      */   
30493     isOriginLeft : true,
30494     /**
30495      * @cfg {Boolean} isOriginTop = top align?
30496      */   
30497     isOriginTop : false,
30498     /**
30499      * @cfg {Boolean} isLayoutInstant = no animation?
30500      */   
30501     isLayoutInstant : false, // needed?
30502     /**
30503      * @cfg {Boolean} isResizingContainer = not sure if this is used..
30504      */   
30505     isResizingContainer : true,
30506     /**
30507      * @cfg {Number} columnWidth  width of the columns 
30508      */   
30509     
30510     columnWidth : 0,
30511     
30512     /**
30513      * @cfg {Number} maxCols maximum number of columns
30514      */   
30515     
30516     maxCols: 0,
30517     /**
30518      * @cfg {Number} padHeight padding below box..
30519      */   
30520     
30521     padHeight : 10, 
30522     
30523     /**
30524      * @cfg {Boolean} isAutoInitial defalut true
30525      */   
30526     
30527     isAutoInitial : true, 
30528     
30529     // private?
30530     gutter : 0,
30531     
30532     containerWidth: 0,
30533     initialColumnWidth : 0,
30534     currentSize : null,
30535     
30536     colYs : null, // array.
30537     maxY : 0,
30538     padWidth: 10,
30539     
30540     
30541     tag: 'div',
30542     cls: '',
30543     bricks: null, //CompositeElement
30544     cols : 0, // array?
30545     // element : null, // wrapped now this.el
30546     _isLayoutInited : null, 
30547     
30548     
30549     getAutoCreate : function(){
30550         
30551         var cfg = {
30552             tag: this.tag,
30553             cls: 'blog-masonary-wrapper ' + this.cls,
30554             cn : {
30555                 cls : 'mas-boxes masonary'
30556             }
30557         };
30558         
30559         return cfg;
30560     },
30561     
30562     getChildContainer: function( )
30563     {
30564         if (this.boxesEl) {
30565             return this.boxesEl;
30566         }
30567         
30568         this.boxesEl = this.el.select('.mas-boxes').first();
30569         
30570         return this.boxesEl;
30571     },
30572     
30573     
30574     initEvents : function()
30575     {
30576         var _this = this;
30577         
30578         if(this.isAutoInitial){
30579             Roo.log('hook children rendered');
30580             this.on('childrenrendered', function() {
30581                 Roo.log('children rendered');
30582                 _this.initial();
30583             } ,this);
30584         }
30585         
30586     },
30587     
30588     initial : function()
30589     {
30590         this.reloadItems();
30591
30592         this.currentSize = this.el.getBox(true);
30593
30594         /// was window resize... - let's see if this works..
30595         Roo.EventManager.onWindowResize(this.resize, this); 
30596
30597         if(!this.isAutoInitial){
30598             this.layout();
30599             return;
30600         }
30601         
30602         this.layout.defer(500,this);
30603     },
30604     
30605     reloadItems: function()
30606     {
30607         this.bricks = this.el.select('.masonry-brick', true);
30608         
30609         this.bricks.each(function(b) {
30610             //Roo.log(b.getSize());
30611             if (!b.attr('originalwidth')) {
30612                 b.attr('originalwidth',  b.getSize().width);
30613             }
30614             
30615         });
30616         
30617         Roo.log(this.bricks.elements.length);
30618     },
30619     
30620     resize : function()
30621     {
30622         Roo.log('resize');
30623         var cs = this.el.getBox(true);
30624         
30625         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
30626             Roo.log("no change in with or X");
30627             return;
30628         }
30629         this.currentSize = cs;
30630         this.layout();
30631     },
30632     
30633     layout : function()
30634     {
30635          Roo.log('layout');
30636         this._resetLayout();
30637         //this._manageStamps();
30638       
30639         // don't animate first layout
30640         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30641         this.layoutItems( isInstant );
30642       
30643         // flag for initalized
30644         this._isLayoutInited = true;
30645     },
30646     
30647     layoutItems : function( isInstant )
30648     {
30649         //var items = this._getItemsForLayout( this.items );
30650         // original code supports filtering layout items.. we just ignore it..
30651         
30652         this._layoutItems( this.bricks , isInstant );
30653       
30654         this._postLayout();
30655     },
30656     _layoutItems : function ( items , isInstant)
30657     {
30658        //this.fireEvent( 'layout', this, items );
30659     
30660
30661         if ( !items || !items.elements.length ) {
30662           // no items, emit event with empty array
30663             return;
30664         }
30665
30666         var queue = [];
30667         items.each(function(item) {
30668             Roo.log("layout item");
30669             Roo.log(item);
30670             // get x/y object from method
30671             var position = this._getItemLayoutPosition( item );
30672             // enqueue
30673             position.item = item;
30674             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
30675             queue.push( position );
30676         }, this);
30677       
30678         this._processLayoutQueue( queue );
30679     },
30680     /** Sets position of item in DOM
30681     * @param {Element} item
30682     * @param {Number} x - horizontal position
30683     * @param {Number} y - vertical position
30684     * @param {Boolean} isInstant - disables transitions
30685     */
30686     _processLayoutQueue : function( queue )
30687     {
30688         for ( var i=0, len = queue.length; i < len; i++ ) {
30689             var obj = queue[i];
30690             obj.item.position('absolute');
30691             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
30692         }
30693     },
30694       
30695     
30696     /**
30697     * Any logic you want to do after each layout,
30698     * i.e. size the container
30699     */
30700     _postLayout : function()
30701     {
30702         this.resizeContainer();
30703     },
30704     
30705     resizeContainer : function()
30706     {
30707         if ( !this.isResizingContainer ) {
30708             return;
30709         }
30710         var size = this._getContainerSize();
30711         if ( size ) {
30712             this.el.setSize(size.width,size.height);
30713             this.boxesEl.setSize(size.width,size.height);
30714         }
30715     },
30716     
30717     
30718     
30719     _resetLayout : function()
30720     {
30721         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
30722         this.colWidth = this.el.getWidth();
30723         //this.gutter = this.el.getWidth(); 
30724         
30725         this.measureColumns();
30726
30727         // reset column Y
30728         var i = this.cols;
30729         this.colYs = [];
30730         while (i--) {
30731             this.colYs.push( 0 );
30732         }
30733     
30734         this.maxY = 0;
30735     },
30736
30737     measureColumns : function()
30738     {
30739         this.getContainerWidth();
30740       // if columnWidth is 0, default to outerWidth of first item
30741         if ( !this.columnWidth ) {
30742             var firstItem = this.bricks.first();
30743             Roo.log(firstItem);
30744             this.columnWidth  = this.containerWidth;
30745             if (firstItem && firstItem.attr('originalwidth') ) {
30746                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
30747             }
30748             // columnWidth fall back to item of first element
30749             Roo.log("set column width?");
30750                         this.initialColumnWidth = this.columnWidth  ;
30751
30752             // if first elem has no width, default to size of container
30753             
30754         }
30755         
30756         
30757         if (this.initialColumnWidth) {
30758             this.columnWidth = this.initialColumnWidth;
30759         }
30760         
30761         
30762             
30763         // column width is fixed at the top - however if container width get's smaller we should
30764         // reduce it...
30765         
30766         // this bit calcs how man columns..
30767             
30768         var columnWidth = this.columnWidth += this.gutter;
30769       
30770         // calculate columns
30771         var containerWidth = this.containerWidth + this.gutter;
30772         
30773         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
30774         // fix rounding errors, typically with gutters
30775         var excess = columnWidth - containerWidth % columnWidth;
30776         
30777         
30778         // if overshoot is less than a pixel, round up, otherwise floor it
30779         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
30780         cols = Math[ mathMethod ]( cols );
30781         this.cols = Math.max( cols, 1 );
30782         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30783         
30784          // padding positioning..
30785         var totalColWidth = this.cols * this.columnWidth;
30786         var padavail = this.containerWidth - totalColWidth;
30787         // so for 2 columns - we need 3 'pads'
30788         
30789         var padNeeded = (1+this.cols) * this.padWidth;
30790         
30791         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
30792         
30793         this.columnWidth += padExtra
30794         //this.padWidth = Math.floor(padavail /  ( this.cols));
30795         
30796         // adjust colum width so that padding is fixed??
30797         
30798         // we have 3 columns ... total = width * 3
30799         // we have X left over... that should be used by 
30800         
30801         //if (this.expandC) {
30802             
30803         //}
30804         
30805         
30806         
30807     },
30808     
30809     getContainerWidth : function()
30810     {
30811        /* // container is parent if fit width
30812         var container = this.isFitWidth ? this.element.parentNode : this.element;
30813         // check that this.size and size are there
30814         // IE8 triggers resize on body size change, so they might not be
30815         
30816         var size = getSize( container );  //FIXME
30817         this.containerWidth = size && size.innerWidth; //FIXME
30818         */
30819          
30820         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30821         
30822     },
30823     
30824     _getItemLayoutPosition : function( item )  // what is item?
30825     {
30826         // we resize the item to our columnWidth..
30827       
30828         item.setWidth(this.columnWidth);
30829         item.autoBoxAdjust  = false;
30830         
30831         var sz = item.getSize();
30832  
30833         // how many columns does this brick span
30834         var remainder = this.containerWidth % this.columnWidth;
30835         
30836         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30837         // round if off by 1 pixel, otherwise use ceil
30838         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
30839         colSpan = Math.min( colSpan, this.cols );
30840         
30841         // normally this should be '1' as we dont' currently allow multi width columns..
30842         
30843         var colGroup = this._getColGroup( colSpan );
30844         // get the minimum Y value from the columns
30845         var minimumY = Math.min.apply( Math, colGroup );
30846         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30847         
30848         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
30849          
30850         // position the brick
30851         var position = {
30852             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30853             y: this.currentSize.y + minimumY + this.padHeight
30854         };
30855         
30856         Roo.log(position);
30857         // apply setHeight to necessary columns
30858         var setHeight = minimumY + sz.height + this.padHeight;
30859         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30860         
30861         var setSpan = this.cols + 1 - colGroup.length;
30862         for ( var i = 0; i < setSpan; i++ ) {
30863           this.colYs[ shortColIndex + i ] = setHeight ;
30864         }
30865       
30866         return position;
30867     },
30868     
30869     /**
30870      * @param {Number} colSpan - number of columns the element spans
30871      * @returns {Array} colGroup
30872      */
30873     _getColGroup : function( colSpan )
30874     {
30875         if ( colSpan < 2 ) {
30876           // if brick spans only one column, use all the column Ys
30877           return this.colYs;
30878         }
30879       
30880         var colGroup = [];
30881         // how many different places could this brick fit horizontally
30882         var groupCount = this.cols + 1 - colSpan;
30883         // for each group potential horizontal position
30884         for ( var i = 0; i < groupCount; i++ ) {
30885           // make an array of colY values for that one group
30886           var groupColYs = this.colYs.slice( i, i + colSpan );
30887           // and get the max value of the array
30888           colGroup[i] = Math.max.apply( Math, groupColYs );
30889         }
30890         return colGroup;
30891     },
30892     /*
30893     _manageStamp : function( stamp )
30894     {
30895         var stampSize =  stamp.getSize();
30896         var offset = stamp.getBox();
30897         // get the columns that this stamp affects
30898         var firstX = this.isOriginLeft ? offset.x : offset.right;
30899         var lastX = firstX + stampSize.width;
30900         var firstCol = Math.floor( firstX / this.columnWidth );
30901         firstCol = Math.max( 0, firstCol );
30902         
30903         var lastCol = Math.floor( lastX / this.columnWidth );
30904         // lastCol should not go over if multiple of columnWidth #425
30905         lastCol -= lastX % this.columnWidth ? 0 : 1;
30906         lastCol = Math.min( this.cols - 1, lastCol );
30907         
30908         // set colYs to bottom of the stamp
30909         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30910             stampSize.height;
30911             
30912         for ( var i = firstCol; i <= lastCol; i++ ) {
30913           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30914         }
30915     },
30916     */
30917     
30918     _getContainerSize : function()
30919     {
30920         this.maxY = Math.max.apply( Math, this.colYs );
30921         var size = {
30922             height: this.maxY
30923         };
30924       
30925         if ( this.isFitWidth ) {
30926             size.width = this._getContainerFitWidth();
30927         }
30928       
30929         return size;
30930     },
30931     
30932     _getContainerFitWidth : function()
30933     {
30934         var unusedCols = 0;
30935         // count unused columns
30936         var i = this.cols;
30937         while ( --i ) {
30938           if ( this.colYs[i] !== 0 ) {
30939             break;
30940           }
30941           unusedCols++;
30942         }
30943         // fit container to columns that have been used
30944         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30945     },
30946     
30947     needsResizeLayout : function()
30948     {
30949         var previousWidth = this.containerWidth;
30950         this.getContainerWidth();
30951         return previousWidth !== this.containerWidth;
30952     }
30953  
30954 });
30955
30956  
30957
30958  /*
30959  * - LGPL
30960  *
30961  * element
30962  * 
30963  */
30964
30965 /**
30966  * @class Roo.bootstrap.MasonryBrick
30967  * @extends Roo.bootstrap.Component
30968  * Bootstrap MasonryBrick class
30969  * 
30970  * @constructor
30971  * Create a new MasonryBrick
30972  * @param {Object} config The config object
30973  */
30974
30975 Roo.bootstrap.MasonryBrick = function(config){
30976     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
30977     
30978     this.addEvents({
30979         // raw events
30980         /**
30981          * @event click
30982          * When a MasonryBrick is clcik
30983          * @param {Roo.bootstrap.MasonryBrick} this
30984          * @param {Roo.EventObject} e
30985          */
30986         "click" : true
30987     });
30988 };
30989
30990 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
30991     
30992     /**
30993      * @cfg {String} title
30994      */   
30995     title : '',
30996     /**
30997      * @cfg {String} html
30998      */   
30999     html : '',
31000     /**
31001      * @cfg {String} bgimage
31002      */   
31003     bgimage : '',
31004     /**
31005      * @cfg {String} videourl
31006      */   
31007     videourl : '',
31008     /**
31009      * @cfg {String} cls
31010      */   
31011     cls : '',
31012     /**
31013      * @cfg {String} href
31014      */   
31015     href : '',
31016     /**
31017      * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
31018      */   
31019     size : 'xs',
31020     
31021     /**
31022      * @cfg {String} (center|bottom) placetitle
31023      */   
31024     placetitle : '',
31025     
31026     /**
31027      * @cfg {Boolean} isFitContainer defalut true
31028      */   
31029     isFitContainer : true, 
31030     
31031     /**
31032      * @cfg {Boolean} preventDefault defalut false
31033      */   
31034     preventDefault : false, 
31035     
31036     getAutoCreate : function()
31037     {
31038         if(!this.isFitContainer){
31039             return this.getSplitAutoCreate();
31040         }
31041         
31042         var cls = 'masonry-brick masonry-brick-full';
31043         
31044         if(this.href.length){
31045             cls += ' masonry-brick-link';
31046         }
31047         
31048         if(this.bgimage.length){
31049             cls += ' masonry-brick-image';
31050         }
31051         
31052         if(!this.html.length){
31053             cls += ' enable-mask';
31054         }
31055         
31056         if(this.size){
31057             cls += ' masonry-' + this.size + '-brick';
31058         }
31059         
31060         if(this.placetitle.length){
31061             
31062             switch (this.placetitle) {
31063                 case 'center' :
31064                     cls += ' masonry-center-title';
31065                     break;
31066                 case 'bottom' :
31067                     cls += ' masonry-bottom-title';
31068                     break;
31069                 default:
31070                     break;
31071             }
31072             
31073         } else {
31074             if(!this.html.length && !this.bgimage.length){
31075                 cls += ' masonry-center-title';
31076             }
31077
31078             if(!this.html.length && this.bgimage.length){
31079                 cls += ' masonry-bottom-title';
31080             }
31081         }
31082         
31083         if(this.cls){
31084             cls += ' ' + this.cls;
31085         }
31086         
31087         var cfg = {
31088             tag: (this.href.length) ? 'a' : 'div',
31089             cls: cls,
31090             cn: [
31091                 {
31092                     tag: 'div',
31093                     cls: 'masonry-brick-paragraph',
31094                     cn: []
31095                 }
31096             ]
31097         };
31098         
31099         if(this.href.length){
31100             cfg.href = this.href;
31101         }
31102         
31103         var cn = cfg.cn[0].cn;
31104         
31105         if(this.title.length){
31106             cn.push({
31107                 tag: 'h4',
31108                 cls: 'masonry-brick-title',
31109                 html: this.title
31110             });
31111         }
31112         
31113         if(this.html.length){
31114             cn.push({
31115                 tag: 'p',
31116                 cls: 'masonry-brick-text',
31117                 html: this.html
31118             });
31119         }  
31120         if (!this.title.length && !this.html.length) {
31121             cfg.cn[0].cls += ' hide';
31122         }
31123         
31124         if(this.bgimage.length){
31125             cfg.cn.push({
31126                 tag: 'img',
31127                 cls: 'masonry-brick-image-view',
31128                 src: this.bgimage
31129             });
31130         }
31131         
31132         if(this.videourl.length){
31133             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31134             // youtube support only?
31135             cfg.cn.push({
31136                 tag: 'iframe',
31137                 cls: 'masonry-brick-image-view',
31138                 src: vurl,
31139                 frameborder : 0,
31140                 allowfullscreen : true
31141             });
31142             
31143             
31144         }
31145         
31146         cfg.cn.push({
31147             tag: 'div',
31148             cls: 'masonry-brick-mask'
31149         });
31150         
31151         return cfg;
31152         
31153     },
31154     
31155     getSplitAutoCreate : function()
31156     {
31157         var cls = 'masonry-brick masonry-brick-split';
31158         
31159         if(this.href.length){
31160             cls += ' masonry-brick-link';
31161         }
31162         
31163         if(this.bgimage.length){
31164             cls += ' masonry-brick-image';
31165         }
31166         
31167         if(this.size){
31168             cls += ' masonry-' + this.size + '-brick';
31169         }
31170         
31171         switch (this.placetitle) {
31172             case 'center' :
31173                 cls += ' masonry-center-title';
31174                 break;
31175             case 'bottom' :
31176                 cls += ' masonry-bottom-title';
31177                 break;
31178             default:
31179                 if(!this.bgimage.length){
31180                     cls += ' masonry-center-title';
31181                 }
31182
31183                 if(this.bgimage.length){
31184                     cls += ' masonry-bottom-title';
31185                 }
31186                 break;
31187         }
31188         
31189         if(this.cls){
31190             cls += ' ' + this.cls;
31191         }
31192         
31193         var cfg = {
31194             tag: (this.href.length) ? 'a' : 'div',
31195             cls: cls,
31196             cn: [
31197                 {
31198                     tag: 'div',
31199                     cls: 'masonry-brick-split-head',
31200                     cn: [
31201                         {
31202                             tag: 'div',
31203                             cls: 'masonry-brick-paragraph',
31204                             cn: []
31205                         }
31206                     ]
31207                 },
31208                 {
31209                     tag: 'div',
31210                     cls: 'masonry-brick-split-body',
31211                     cn: []
31212                 }
31213             ]
31214         };
31215         
31216         if(this.href.length){
31217             cfg.href = this.href;
31218         }
31219         
31220         if(this.title.length){
31221             cfg.cn[0].cn[0].cn.push({
31222                 tag: 'h4',
31223                 cls: 'masonry-brick-title',
31224                 html: this.title
31225             });
31226         }
31227         
31228         if(this.html.length){
31229             cfg.cn[1].cn.push({
31230                 tag: 'p',
31231                 cls: 'masonry-brick-text',
31232                 html: this.html
31233             });
31234         }
31235
31236         if(this.bgimage.length){
31237             cfg.cn[0].cn.push({
31238                 tag: 'img',
31239                 cls: 'masonry-brick-image-view',
31240                 src: this.bgimage
31241             });
31242         }
31243         
31244         if(this.videourl.length){
31245             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31246             // youtube support only?
31247             cfg.cn[0].cn.cn.push({
31248                 tag: 'iframe',
31249                 cls: 'masonry-brick-image-view',
31250                 src: vurl,
31251                 frameborder : 0,
31252                 allowfullscreen : true
31253             });
31254         }
31255         
31256         return cfg;
31257     },
31258     
31259     initEvents: function() 
31260     {
31261         switch (this.size) {
31262             case 'xs' :
31263                 this.x = 1;
31264                 this.y = 1;
31265                 break;
31266             case 'sm' :
31267                 this.x = 2;
31268                 this.y = 2;
31269                 break;
31270             case 'md' :
31271             case 'md-left' :
31272             case 'md-right' :
31273                 this.x = 3;
31274                 this.y = 3;
31275                 break;
31276             case 'tall' :
31277                 this.x = 2;
31278                 this.y = 3;
31279                 break;
31280             case 'wide' :
31281                 this.x = 3;
31282                 this.y = 2;
31283                 break;
31284             case 'wide-thin' :
31285                 this.x = 3;
31286                 this.y = 1;
31287                 break;
31288                         
31289             default :
31290                 break;
31291         }
31292         
31293         if(Roo.isTouch){
31294             this.el.on('touchstart', this.onTouchStart, this);
31295             this.el.on('touchmove', this.onTouchMove, this);
31296             this.el.on('touchend', this.onTouchEnd, this);
31297             this.el.on('contextmenu', this.onContextMenu, this);
31298         } else {
31299             this.el.on('mouseenter'  ,this.enter, this);
31300             this.el.on('mouseleave', this.leave, this);
31301             this.el.on('click', this.onClick, this);
31302         }
31303         
31304         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
31305             this.parent().bricks.push(this);   
31306         }
31307         
31308     },
31309     
31310     onClick: function(e, el)
31311     {
31312         var time = this.endTimer - this.startTimer;
31313         
31314         if(Roo.isTouch){
31315             if(time > 1000){
31316                 e.preventDefault();
31317                 return;
31318             }
31319         }
31320         
31321         if(!this.preventDefault){
31322             return;
31323         }
31324         
31325         e.preventDefault();
31326         this.fireEvent('click', this);
31327     },
31328     
31329     enter: function(e, el)
31330     {
31331         e.preventDefault();
31332         
31333         if(!this.isFitContainer){
31334             return;
31335         }
31336         
31337         if(this.bgimage.length && this.html.length){
31338             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31339         }
31340     },
31341     
31342     leave: function(e, el)
31343     {
31344         e.preventDefault();
31345         
31346         if(!this.isFitContainer){
31347             return;
31348         }
31349         
31350         if(this.bgimage.length && this.html.length){
31351             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31352         }
31353     },
31354     
31355     onTouchStart: function(e, el)
31356     {
31357 //        e.preventDefault();
31358         
31359         this.touchmoved = false;
31360         
31361         if(!this.isFitContainer){
31362             return;
31363         }
31364         
31365         if(!this.bgimage.length || !this.html.length){
31366             return;
31367         }
31368         
31369         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31370         
31371         this.timer = new Date().getTime();
31372         
31373     },
31374     
31375     onTouchMove: function(e, el)
31376     {
31377         this.touchmoved = true;
31378     },
31379     
31380     onContextMenu : function(e,el)
31381     {
31382         e.preventDefault();
31383         e.stopPropagation();
31384         return false;
31385     },
31386     
31387     onTouchEnd: function(e, el)
31388     {
31389 //        e.preventDefault();
31390         
31391         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
31392         
31393             this.leave(e,el);
31394             
31395             return;
31396         }
31397         
31398         if(!this.bgimage.length || !this.html.length){
31399             
31400             if(this.href.length){
31401                 window.location.href = this.href;
31402             }
31403             
31404             return;
31405         }
31406         
31407         if(!this.isFitContainer){
31408             return;
31409         }
31410         
31411         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31412         
31413         window.location.href = this.href;
31414     }
31415     
31416 });
31417
31418  
31419
31420  /*
31421  * - LGPL
31422  *
31423  * element
31424  * 
31425  */
31426
31427 /**
31428  * @class Roo.bootstrap.Brick
31429  * @extends Roo.bootstrap.Component
31430  * Bootstrap Brick class
31431  * 
31432  * @constructor
31433  * Create a new Brick
31434  * @param {Object} config The config object
31435  */
31436
31437 Roo.bootstrap.Brick = function(config){
31438     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
31439     
31440     this.addEvents({
31441         // raw events
31442         /**
31443          * @event click
31444          * When a Brick is click
31445          * @param {Roo.bootstrap.Brick} this
31446          * @param {Roo.EventObject} e
31447          */
31448         "click" : true
31449     });
31450 };
31451
31452 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
31453     
31454     /**
31455      * @cfg {String} title
31456      */   
31457     title : '',
31458     /**
31459      * @cfg {String} html
31460      */   
31461     html : '',
31462     /**
31463      * @cfg {String} bgimage
31464      */   
31465     bgimage : '',
31466     /**
31467      * @cfg {String} cls
31468      */   
31469     cls : '',
31470     /**
31471      * @cfg {String} href
31472      */   
31473     href : '',
31474     /**
31475      * @cfg {String} video
31476      */   
31477     video : '',
31478     /**
31479      * @cfg {Boolean} square
31480      */   
31481     square : true,
31482     
31483     getAutoCreate : function()
31484     {
31485         var cls = 'roo-brick';
31486         
31487         if(this.href.length){
31488             cls += ' roo-brick-link';
31489         }
31490         
31491         if(this.bgimage.length){
31492             cls += ' roo-brick-image';
31493         }
31494         
31495         if(!this.html.length && !this.bgimage.length){
31496             cls += ' roo-brick-center-title';
31497         }
31498         
31499         if(!this.html.length && this.bgimage.length){
31500             cls += ' roo-brick-bottom-title';
31501         }
31502         
31503         if(this.cls){
31504             cls += ' ' + this.cls;
31505         }
31506         
31507         var cfg = {
31508             tag: (this.href.length) ? 'a' : 'div',
31509             cls: cls,
31510             cn: [
31511                 {
31512                     tag: 'div',
31513                     cls: 'roo-brick-paragraph',
31514                     cn: []
31515                 }
31516             ]
31517         };
31518         
31519         if(this.href.length){
31520             cfg.href = this.href;
31521         }
31522         
31523         var cn = cfg.cn[0].cn;
31524         
31525         if(this.title.length){
31526             cn.push({
31527                 tag: 'h4',
31528                 cls: 'roo-brick-title',
31529                 html: this.title
31530             });
31531         }
31532         
31533         if(this.html.length){
31534             cn.push({
31535                 tag: 'p',
31536                 cls: 'roo-brick-text',
31537                 html: this.html
31538             });
31539         } else {
31540             cn.cls += ' hide';
31541         }
31542         
31543         if(this.bgimage.length){
31544             cfg.cn.push({
31545                 tag: 'img',
31546                 cls: 'roo-brick-image-view',
31547                 src: this.bgimage
31548             });
31549         }
31550         
31551         return cfg;
31552     },
31553     
31554     initEvents: function() 
31555     {
31556         if(this.title.length || this.html.length){
31557             this.el.on('mouseenter'  ,this.enter, this);
31558             this.el.on('mouseleave', this.leave, this);
31559         }
31560         
31561         
31562         Roo.EventManager.onWindowResize(this.resize, this); 
31563         
31564         this.resize();
31565     },
31566     
31567     resize : function()
31568     {
31569         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
31570         
31571         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
31572         
31573         if(this.bgimage.length){
31574             var image = this.el.select('.roo-brick-image-view', true).first();
31575             image.setWidth(paragraph.getWidth());
31576             image.setHeight(paragraph.getWidth());
31577             
31578             this.el.setHeight(paragraph.getWidth());
31579             
31580         }
31581         
31582     },
31583     
31584     enter: function(e, el)
31585     {
31586         e.preventDefault();
31587         
31588         if(this.bgimage.length){
31589             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
31590             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
31591         }
31592     },
31593     
31594     leave: function(e, el)
31595     {
31596         e.preventDefault();
31597         
31598         if(this.bgimage.length){
31599             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
31600             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
31601         }
31602     }
31603     
31604 });
31605
31606  
31607
31608  /*
31609  * Based on:
31610  * Ext JS Library 1.1.1
31611  * Copyright(c) 2006-2007, Ext JS, LLC.
31612  *
31613  * Originally Released Under LGPL - original licence link has changed is not relivant.
31614  *
31615  * Fork - LGPL
31616  * <script type="text/javascript">
31617  */
31618
31619
31620 /**
31621  * @class Roo.bootstrap.SplitBar
31622  * @extends Roo.util.Observable
31623  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
31624  * <br><br>
31625  * Usage:
31626  * <pre><code>
31627 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
31628                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
31629 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
31630 split.minSize = 100;
31631 split.maxSize = 600;
31632 split.animate = true;
31633 split.on('moved', splitterMoved);
31634 </code></pre>
31635  * @constructor
31636  * Create a new SplitBar
31637  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
31638  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
31639  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31640  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
31641                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
31642                         position of the SplitBar).
31643  */
31644 Roo.bootstrap.SplitBar = function(cfg){
31645     
31646     /** @private */
31647     
31648     //{
31649     //  dragElement : elm
31650     //  resizingElement: el,
31651         // optional..
31652     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
31653     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
31654         // existingProxy ???
31655     //}
31656     
31657     this.el = Roo.get(cfg.dragElement, true);
31658     this.el.dom.unselectable = "on";
31659     /** @private */
31660     this.resizingEl = Roo.get(cfg.resizingElement, true);
31661
31662     /**
31663      * @private
31664      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31665      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
31666      * @type Number
31667      */
31668     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
31669     
31670     /**
31671      * The minimum size of the resizing element. (Defaults to 0)
31672      * @type Number
31673      */
31674     this.minSize = 0;
31675     
31676     /**
31677      * The maximum size of the resizing element. (Defaults to 2000)
31678      * @type Number
31679      */
31680     this.maxSize = 2000;
31681     
31682     /**
31683      * Whether to animate the transition to the new size
31684      * @type Boolean
31685      */
31686     this.animate = false;
31687     
31688     /**
31689      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
31690      * @type Boolean
31691      */
31692     this.useShim = false;
31693     
31694     /** @private */
31695     this.shim = null;
31696     
31697     if(!cfg.existingProxy){
31698         /** @private */
31699         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
31700     }else{
31701         this.proxy = Roo.get(cfg.existingProxy).dom;
31702     }
31703     /** @private */
31704     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
31705     
31706     /** @private */
31707     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
31708     
31709     /** @private */
31710     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
31711     
31712     /** @private */
31713     this.dragSpecs = {};
31714     
31715     /**
31716      * @private The adapter to use to positon and resize elements
31717      */
31718     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31719     this.adapter.init(this);
31720     
31721     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31722         /** @private */
31723         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
31724         this.el.addClass("roo-splitbar-h");
31725     }else{
31726         /** @private */
31727         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
31728         this.el.addClass("roo-splitbar-v");
31729     }
31730     
31731     this.addEvents({
31732         /**
31733          * @event resize
31734          * Fires when the splitter is moved (alias for {@link #event-moved})
31735          * @param {Roo.bootstrap.SplitBar} this
31736          * @param {Number} newSize the new width or height
31737          */
31738         "resize" : true,
31739         /**
31740          * @event moved
31741          * Fires when the splitter is moved
31742          * @param {Roo.bootstrap.SplitBar} this
31743          * @param {Number} newSize the new width or height
31744          */
31745         "moved" : true,
31746         /**
31747          * @event beforeresize
31748          * Fires before the splitter is dragged
31749          * @param {Roo.bootstrap.SplitBar} this
31750          */
31751         "beforeresize" : true,
31752
31753         "beforeapply" : true
31754     });
31755
31756     Roo.util.Observable.call(this);
31757 };
31758
31759 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
31760     onStartProxyDrag : function(x, y){
31761         this.fireEvent("beforeresize", this);
31762         if(!this.overlay){
31763             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
31764             o.unselectable();
31765             o.enableDisplayMode("block");
31766             // all splitbars share the same overlay
31767             Roo.bootstrap.SplitBar.prototype.overlay = o;
31768         }
31769         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
31770         this.overlay.show();
31771         Roo.get(this.proxy).setDisplayed("block");
31772         var size = this.adapter.getElementSize(this);
31773         this.activeMinSize = this.getMinimumSize();;
31774         this.activeMaxSize = this.getMaximumSize();;
31775         var c1 = size - this.activeMinSize;
31776         var c2 = Math.max(this.activeMaxSize - size, 0);
31777         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31778             this.dd.resetConstraints();
31779             this.dd.setXConstraint(
31780                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
31781                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
31782             );
31783             this.dd.setYConstraint(0, 0);
31784         }else{
31785             this.dd.resetConstraints();
31786             this.dd.setXConstraint(0, 0);
31787             this.dd.setYConstraint(
31788                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
31789                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
31790             );
31791          }
31792         this.dragSpecs.startSize = size;
31793         this.dragSpecs.startPoint = [x, y];
31794         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
31795     },
31796     
31797     /** 
31798      * @private Called after the drag operation by the DDProxy
31799      */
31800     onEndProxyDrag : function(e){
31801         Roo.get(this.proxy).setDisplayed(false);
31802         var endPoint = Roo.lib.Event.getXY(e);
31803         if(this.overlay){
31804             this.overlay.hide();
31805         }
31806         var newSize;
31807         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31808             newSize = this.dragSpecs.startSize + 
31809                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
31810                     endPoint[0] - this.dragSpecs.startPoint[0] :
31811                     this.dragSpecs.startPoint[0] - endPoint[0]
31812                 );
31813         }else{
31814             newSize = this.dragSpecs.startSize + 
31815                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
31816                     endPoint[1] - this.dragSpecs.startPoint[1] :
31817                     this.dragSpecs.startPoint[1] - endPoint[1]
31818                 );
31819         }
31820         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
31821         if(newSize != this.dragSpecs.startSize){
31822             if(this.fireEvent('beforeapply', this, newSize) !== false){
31823                 this.adapter.setElementSize(this, newSize);
31824                 this.fireEvent("moved", this, newSize);
31825                 this.fireEvent("resize", this, newSize);
31826             }
31827         }
31828     },
31829     
31830     /**
31831      * Get the adapter this SplitBar uses
31832      * @return The adapter object
31833      */
31834     getAdapter : function(){
31835         return this.adapter;
31836     },
31837     
31838     /**
31839      * Set the adapter this SplitBar uses
31840      * @param {Object} adapter A SplitBar adapter object
31841      */
31842     setAdapter : function(adapter){
31843         this.adapter = adapter;
31844         this.adapter.init(this);
31845     },
31846     
31847     /**
31848      * Gets the minimum size for the resizing element
31849      * @return {Number} The minimum size
31850      */
31851     getMinimumSize : function(){
31852         return this.minSize;
31853     },
31854     
31855     /**
31856      * Sets the minimum size for the resizing element
31857      * @param {Number} minSize The minimum size
31858      */
31859     setMinimumSize : function(minSize){
31860         this.minSize = minSize;
31861     },
31862     
31863     /**
31864      * Gets the maximum size for the resizing element
31865      * @return {Number} The maximum size
31866      */
31867     getMaximumSize : function(){
31868         return this.maxSize;
31869     },
31870     
31871     /**
31872      * Sets the maximum size for the resizing element
31873      * @param {Number} maxSize The maximum size
31874      */
31875     setMaximumSize : function(maxSize){
31876         this.maxSize = maxSize;
31877     },
31878     
31879     /**
31880      * Sets the initialize size for the resizing element
31881      * @param {Number} size The initial size
31882      */
31883     setCurrentSize : function(size){
31884         var oldAnimate = this.animate;
31885         this.animate = false;
31886         this.adapter.setElementSize(this, size);
31887         this.animate = oldAnimate;
31888     },
31889     
31890     /**
31891      * Destroy this splitbar. 
31892      * @param {Boolean} removeEl True to remove the element
31893      */
31894     destroy : function(removeEl){
31895         if(this.shim){
31896             this.shim.remove();
31897         }
31898         this.dd.unreg();
31899         this.proxy.parentNode.removeChild(this.proxy);
31900         if(removeEl){
31901             this.el.remove();
31902         }
31903     }
31904 });
31905
31906 /**
31907  * @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.
31908  */
31909 Roo.bootstrap.SplitBar.createProxy = function(dir){
31910     var proxy = new Roo.Element(document.createElement("div"));
31911     proxy.unselectable();
31912     var cls = 'roo-splitbar-proxy';
31913     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
31914     document.body.appendChild(proxy.dom);
31915     return proxy.dom;
31916 };
31917
31918 /** 
31919  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
31920  * Default Adapter. It assumes the splitter and resizing element are not positioned
31921  * elements and only gets/sets the width of the element. Generally used for table based layouts.
31922  */
31923 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
31924 };
31925
31926 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
31927     // do nothing for now
31928     init : function(s){
31929     
31930     },
31931     /**
31932      * Called before drag operations to get the current size of the resizing element. 
31933      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31934      */
31935      getElementSize : function(s){
31936         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31937             return s.resizingEl.getWidth();
31938         }else{
31939             return s.resizingEl.getHeight();
31940         }
31941     },
31942     
31943     /**
31944      * Called after drag operations to set the size of the resizing element.
31945      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31946      * @param {Number} newSize The new size to set
31947      * @param {Function} onComplete A function to be invoked when resizing is complete
31948      */
31949     setElementSize : function(s, newSize, onComplete){
31950         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31951             if(!s.animate){
31952                 s.resizingEl.setWidth(newSize);
31953                 if(onComplete){
31954                     onComplete(s, newSize);
31955                 }
31956             }else{
31957                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
31958             }
31959         }else{
31960             
31961             if(!s.animate){
31962                 s.resizingEl.setHeight(newSize);
31963                 if(onComplete){
31964                     onComplete(s, newSize);
31965                 }
31966             }else{
31967                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
31968             }
31969         }
31970     }
31971 };
31972
31973 /** 
31974  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
31975  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
31976  * Adapter that  moves the splitter element to align with the resized sizing element. 
31977  * Used with an absolute positioned SplitBar.
31978  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
31979  * document.body, make sure you assign an id to the body element.
31980  */
31981 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
31982     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31983     this.container = Roo.get(container);
31984 };
31985
31986 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
31987     init : function(s){
31988         this.basic.init(s);
31989     },
31990     
31991     getElementSize : function(s){
31992         return this.basic.getElementSize(s);
31993     },
31994     
31995     setElementSize : function(s, newSize, onComplete){
31996         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
31997     },
31998     
31999     moveSplitter : function(s){
32000         var yes = Roo.bootstrap.SplitBar;
32001         switch(s.placement){
32002             case yes.LEFT:
32003                 s.el.setX(s.resizingEl.getRight());
32004                 break;
32005             case yes.RIGHT:
32006                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
32007                 break;
32008             case yes.TOP:
32009                 s.el.setY(s.resizingEl.getBottom());
32010                 break;
32011             case yes.BOTTOM:
32012                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
32013                 break;
32014         }
32015     }
32016 };
32017
32018 /**
32019  * Orientation constant - Create a vertical SplitBar
32020  * @static
32021  * @type Number
32022  */
32023 Roo.bootstrap.SplitBar.VERTICAL = 1;
32024
32025 /**
32026  * Orientation constant - Create a horizontal SplitBar
32027  * @static
32028  * @type Number
32029  */
32030 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
32031
32032 /**
32033  * Placement constant - The resizing element is to the left of the splitter element
32034  * @static
32035  * @type Number
32036  */
32037 Roo.bootstrap.SplitBar.LEFT = 1;
32038
32039 /**
32040  * Placement constant - The resizing element is to the right of the splitter element
32041  * @static
32042  * @type Number
32043  */
32044 Roo.bootstrap.SplitBar.RIGHT = 2;
32045
32046 /**
32047  * Placement constant - The resizing element is positioned above the splitter element
32048  * @static
32049  * @type Number
32050  */
32051 Roo.bootstrap.SplitBar.TOP = 3;
32052
32053 /**
32054  * Placement constant - The resizing element is positioned under splitter element
32055  * @static
32056  * @type Number
32057  */
32058 Roo.bootstrap.SplitBar.BOTTOM = 4;
32059 Roo.namespace("Roo.bootstrap.layout");/*
32060  * Based on:
32061  * Ext JS Library 1.1.1
32062  * Copyright(c) 2006-2007, Ext JS, LLC.
32063  *
32064  * Originally Released Under LGPL - original licence link has changed is not relivant.
32065  *
32066  * Fork - LGPL
32067  * <script type="text/javascript">
32068  */
32069
32070 /**
32071  * @class Roo.bootstrap.layout.Manager
32072  * @extends Roo.bootstrap.Component
32073  * Base class for layout managers.
32074  */
32075 Roo.bootstrap.layout.Manager = function(config)
32076 {
32077     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
32078
32079
32080
32081
32082
32083     /** false to disable window resize monitoring @type Boolean */
32084     this.monitorWindowResize = true;
32085     this.regions = {};
32086     this.addEvents({
32087         /**
32088          * @event layout
32089          * Fires when a layout is performed.
32090          * @param {Roo.LayoutManager} this
32091          */
32092         "layout" : true,
32093         /**
32094          * @event regionresized
32095          * Fires when the user resizes a region.
32096          * @param {Roo.LayoutRegion} region The resized region
32097          * @param {Number} newSize The new size (width for east/west, height for north/south)
32098          */
32099         "regionresized" : true,
32100         /**
32101          * @event regioncollapsed
32102          * Fires when a region is collapsed.
32103          * @param {Roo.LayoutRegion} region The collapsed region
32104          */
32105         "regioncollapsed" : true,
32106         /**
32107          * @event regionexpanded
32108          * Fires when a region is expanded.
32109          * @param {Roo.LayoutRegion} region The expanded region
32110          */
32111         "regionexpanded" : true
32112     });
32113     this.updating = false;
32114
32115     if (config.el) {
32116         this.el = Roo.get(config.el);
32117         this.initEvents();
32118     }
32119
32120 };
32121
32122 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
32123
32124
32125     regions : null,
32126
32127     monitorWindowResize : true,
32128
32129
32130     updating : false,
32131
32132
32133     onRender : function(ct, position)
32134     {
32135         if(!this.el){
32136             this.el = Roo.get(ct);
32137             this.initEvents();
32138         }
32139         //this.fireEvent('render',this);
32140     },
32141
32142
32143     initEvents: function()
32144     {
32145
32146
32147         // ie scrollbar fix
32148         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32149             document.body.scroll = "no";
32150         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32151             this.el.position('relative');
32152         }
32153         this.id = this.el.id;
32154         this.el.addClass("roo-layout-container");
32155         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32156         if(this.el.dom != document.body ) {
32157             this.el.on('resize', this.layout,this);
32158             this.el.on('show', this.layout,this);
32159         }
32160
32161     },
32162
32163     /**
32164      * Returns true if this layout is currently being updated
32165      * @return {Boolean}
32166      */
32167     isUpdating : function(){
32168         return this.updating;
32169     },
32170
32171     /**
32172      * Suspend the LayoutManager from doing auto-layouts while
32173      * making multiple add or remove calls
32174      */
32175     beginUpdate : function(){
32176         this.updating = true;
32177     },
32178
32179     /**
32180      * Restore auto-layouts and optionally disable the manager from performing a layout
32181      * @param {Boolean} noLayout true to disable a layout update
32182      */
32183     endUpdate : function(noLayout){
32184         this.updating = false;
32185         if(!noLayout){
32186             this.layout();
32187         }
32188     },
32189
32190     layout: function(){
32191         // abstract...
32192     },
32193
32194     onRegionResized : function(region, newSize){
32195         this.fireEvent("regionresized", region, newSize);
32196         this.layout();
32197     },
32198
32199     onRegionCollapsed : function(region){
32200         this.fireEvent("regioncollapsed", region);
32201     },
32202
32203     onRegionExpanded : function(region){
32204         this.fireEvent("regionexpanded", region);
32205     },
32206
32207     /**
32208      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32209      * performs box-model adjustments.
32210      * @return {Object} The size as an object {width: (the width), height: (the height)}
32211      */
32212     getViewSize : function()
32213     {
32214         var size;
32215         if(this.el.dom != document.body){
32216             size = this.el.getSize();
32217         }else{
32218             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32219         }
32220         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32221         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32222         return size;
32223     },
32224
32225     /**
32226      * Returns the Element this layout is bound to.
32227      * @return {Roo.Element}
32228      */
32229     getEl : function(){
32230         return this.el;
32231     },
32232
32233     /**
32234      * Returns the specified region.
32235      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32236      * @return {Roo.LayoutRegion}
32237      */
32238     getRegion : function(target){
32239         return this.regions[target.toLowerCase()];
32240     },
32241
32242     onWindowResize : function(){
32243         if(this.monitorWindowResize){
32244             this.layout();
32245         }
32246     }
32247 });
32248 /*
32249  * Based on:
32250  * Ext JS Library 1.1.1
32251  * Copyright(c) 2006-2007, Ext JS, LLC.
32252  *
32253  * Originally Released Under LGPL - original licence link has changed is not relivant.
32254  *
32255  * Fork - LGPL
32256  * <script type="text/javascript">
32257  */
32258 /**
32259  * @class Roo.bootstrap.layout.Border
32260  * @extends Roo.bootstrap.layout.Manager
32261  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32262  * please see: examples/bootstrap/nested.html<br><br>
32263  
32264 <b>The container the layout is rendered into can be either the body element or any other element.
32265 If it is not the body element, the container needs to either be an absolute positioned element,
32266 or you will need to add "position:relative" to the css of the container.  You will also need to specify
32267 the container size if it is not the body element.</b>
32268
32269 * @constructor
32270 * Create a new Border
32271 * @param {Object} config Configuration options
32272  */
32273 Roo.bootstrap.layout.Border = function(config){
32274     config = config || {};
32275     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
32276     
32277     
32278     
32279     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
32280         if(config[region]){
32281             config[region].region = region;
32282             this.addRegion(config[region]);
32283         }
32284     },this);
32285     
32286 };
32287
32288 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
32289
32290 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
32291     /**
32292      * Creates and adds a new region if it doesn't already exist.
32293      * @param {String} target The target region key (north, south, east, west or center).
32294      * @param {Object} config The regions config object
32295      * @return {BorderLayoutRegion} The new region
32296      */
32297     addRegion : function(config)
32298     {
32299         if(!this.regions[config.region]){
32300             var r = this.factory(config);
32301             this.bindRegion(r);
32302         }
32303         return this.regions[config.region];
32304     },
32305
32306     // private (kinda)
32307     bindRegion : function(r){
32308         this.regions[r.config.region] = r;
32309         
32310         r.on("visibilitychange",    this.layout, this);
32311         r.on("paneladded",          this.layout, this);
32312         r.on("panelremoved",        this.layout, this);
32313         r.on("invalidated",         this.layout, this);
32314         r.on("resized",             this.onRegionResized, this);
32315         r.on("collapsed",           this.onRegionCollapsed, this);
32316         r.on("expanded",            this.onRegionExpanded, this);
32317     },
32318
32319     /**
32320      * Performs a layout update.
32321      */
32322     layout : function()
32323     {
32324         if(this.updating) {
32325             return;
32326         }
32327         
32328         // render all the rebions if they have not been done alreayd?
32329         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
32330             if(this.regions[region] && !this.regions[region].bodyEl){
32331                 this.regions[region].onRender(this.el)
32332             }
32333         },this);
32334         
32335         var size = this.getViewSize();
32336         var w = size.width;
32337         var h = size.height;
32338         var centerW = w;
32339         var centerH = h;
32340         var centerY = 0;
32341         var centerX = 0;
32342         //var x = 0, y = 0;
32343
32344         var rs = this.regions;
32345         var north = rs["north"];
32346         var south = rs["south"]; 
32347         var west = rs["west"];
32348         var east = rs["east"];
32349         var center = rs["center"];
32350         //if(this.hideOnLayout){ // not supported anymore
32351             //c.el.setStyle("display", "none");
32352         //}
32353         if(north && north.isVisible()){
32354             var b = north.getBox();
32355             var m = north.getMargins();
32356             b.width = w - (m.left+m.right);
32357             b.x = m.left;
32358             b.y = m.top;
32359             centerY = b.height + b.y + m.bottom;
32360             centerH -= centerY;
32361             north.updateBox(this.safeBox(b));
32362         }
32363         if(south && south.isVisible()){
32364             var b = south.getBox();
32365             var m = south.getMargins();
32366             b.width = w - (m.left+m.right);
32367             b.x = m.left;
32368             var totalHeight = (b.height + m.top + m.bottom);
32369             b.y = h - totalHeight + m.top;
32370             centerH -= totalHeight;
32371             south.updateBox(this.safeBox(b));
32372         }
32373         if(west && west.isVisible()){
32374             var b = west.getBox();
32375             var m = west.getMargins();
32376             b.height = centerH - (m.top+m.bottom);
32377             b.x = m.left;
32378             b.y = centerY + m.top;
32379             var totalWidth = (b.width + m.left + m.right);
32380             centerX += totalWidth;
32381             centerW -= totalWidth;
32382             west.updateBox(this.safeBox(b));
32383         }
32384         if(east && east.isVisible()){
32385             var b = east.getBox();
32386             var m = east.getMargins();
32387             b.height = centerH - (m.top+m.bottom);
32388             var totalWidth = (b.width + m.left + m.right);
32389             b.x = w - totalWidth + m.left;
32390             b.y = centerY + m.top;
32391             centerW -= totalWidth;
32392             east.updateBox(this.safeBox(b));
32393         }
32394         if(center){
32395             var m = center.getMargins();
32396             var centerBox = {
32397                 x: centerX + m.left,
32398                 y: centerY + m.top,
32399                 width: centerW - (m.left+m.right),
32400                 height: centerH - (m.top+m.bottom)
32401             };
32402             //if(this.hideOnLayout){
32403                 //center.el.setStyle("display", "block");
32404             //}
32405             center.updateBox(this.safeBox(centerBox));
32406         }
32407         this.el.repaint();
32408         this.fireEvent("layout", this);
32409     },
32410
32411     // private
32412     safeBox : function(box){
32413         box.width = Math.max(0, box.width);
32414         box.height = Math.max(0, box.height);
32415         return box;
32416     },
32417
32418     /**
32419      * Adds a ContentPanel (or subclass) to this layout.
32420      * @param {String} target The target region key (north, south, east, west or center).
32421      * @param {Roo.ContentPanel} panel The panel to add
32422      * @return {Roo.ContentPanel} The added panel
32423      */
32424     add : function(target, panel){
32425          
32426         target = target.toLowerCase();
32427         return this.regions[target].add(panel);
32428     },
32429
32430     /**
32431      * Remove a ContentPanel (or subclass) to this layout.
32432      * @param {String} target The target region key (north, south, east, west or center).
32433      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
32434      * @return {Roo.ContentPanel} The removed panel
32435      */
32436     remove : function(target, panel){
32437         target = target.toLowerCase();
32438         return this.regions[target].remove(panel);
32439     },
32440
32441     /**
32442      * Searches all regions for a panel with the specified id
32443      * @param {String} panelId
32444      * @return {Roo.ContentPanel} The panel or null if it wasn't found
32445      */
32446     findPanel : function(panelId){
32447         var rs = this.regions;
32448         for(var target in rs){
32449             if(typeof rs[target] != "function"){
32450                 var p = rs[target].getPanel(panelId);
32451                 if(p){
32452                     return p;
32453                 }
32454             }
32455         }
32456         return null;
32457     },
32458
32459     /**
32460      * Searches all regions for a panel with the specified id and activates (shows) it.
32461      * @param {String/ContentPanel} panelId The panels id or the panel itself
32462      * @return {Roo.ContentPanel} The shown panel or null
32463      */
32464     showPanel : function(panelId) {
32465       var rs = this.regions;
32466       for(var target in rs){
32467          var r = rs[target];
32468          if(typeof r != "function"){
32469             if(r.hasPanel(panelId)){
32470                return r.showPanel(panelId);
32471             }
32472          }
32473       }
32474       return null;
32475    },
32476
32477    /**
32478      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
32479      * @param {Roo.state.Provider} provider (optional) An alternate state provider
32480      */
32481    /*
32482     restoreState : function(provider){
32483         if(!provider){
32484             provider = Roo.state.Manager;
32485         }
32486         var sm = new Roo.LayoutStateManager();
32487         sm.init(this, provider);
32488     },
32489 */
32490  
32491  
32492     /**
32493      * Adds a xtype elements to the layout.
32494      * <pre><code>
32495
32496 layout.addxtype({
32497        xtype : 'ContentPanel',
32498        region: 'west',
32499        items: [ .... ]
32500    }
32501 );
32502
32503 layout.addxtype({
32504         xtype : 'NestedLayoutPanel',
32505         region: 'west',
32506         layout: {
32507            center: { },
32508            west: { }   
32509         },
32510         items : [ ... list of content panels or nested layout panels.. ]
32511    }
32512 );
32513 </code></pre>
32514      * @param {Object} cfg Xtype definition of item to add.
32515      */
32516     addxtype : function(cfg)
32517     {
32518         // basically accepts a pannel...
32519         // can accept a layout region..!?!?
32520         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
32521         
32522         
32523         // theory?  children can only be panels??
32524         
32525         //if (!cfg.xtype.match(/Panel$/)) {
32526         //    return false;
32527         //}
32528         var ret = false;
32529         
32530         if (typeof(cfg.region) == 'undefined') {
32531             Roo.log("Failed to add Panel, region was not set");
32532             Roo.log(cfg);
32533             return false;
32534         }
32535         var region = cfg.region;
32536         delete cfg.region;
32537         
32538           
32539         var xitems = [];
32540         if (cfg.items) {
32541             xitems = cfg.items;
32542             delete cfg.items;
32543         }
32544         var nb = false;
32545         
32546         switch(cfg.xtype) 
32547         {
32548             case 'Content':  // ContentPanel (el, cfg)
32549             case 'Scroll':  // ContentPanel (el, cfg)
32550             case 'View': 
32551                 cfg.autoCreate = true;
32552                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32553                 //} else {
32554                 //    var el = this.el.createChild();
32555                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
32556                 //}
32557                 
32558                 this.add(region, ret);
32559                 break;
32560             
32561             /*
32562             case 'TreePanel': // our new panel!
32563                 cfg.el = this.el.createChild();
32564                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32565                 this.add(region, ret);
32566                 break;
32567             */
32568             
32569             case 'Nest': 
32570                 // create a new Layout (which is  a Border Layout...
32571                 
32572                 var clayout = cfg.layout;
32573                 clayout.el  = this.el.createChild();
32574                 clayout.items   = clayout.items  || [];
32575                 
32576                 delete cfg.layout;
32577                 
32578                 // replace this exitems with the clayout ones..
32579                 xitems = clayout.items;
32580                  
32581                 // force background off if it's in center...
32582                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
32583                     cfg.background = false;
32584                 }
32585                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
32586                 
32587                 
32588                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32589                 //console.log('adding nested layout panel '  + cfg.toSource());
32590                 this.add(region, ret);
32591                 nb = {}; /// find first...
32592                 break;
32593             
32594             case 'Grid':
32595                 
32596                 // needs grid and region
32597                 
32598                 //var el = this.getRegion(region).el.createChild();
32599                 /*
32600                  *var el = this.el.createChild();
32601                 // create the grid first...
32602                 cfg.grid.container = el;
32603                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
32604                 */
32605                 
32606                 if (region == 'center' && this.active ) {
32607                     cfg.background = false;
32608                 }
32609                 
32610                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32611                 
32612                 this.add(region, ret);
32613                 /*
32614                 if (cfg.background) {
32615                     // render grid on panel activation (if panel background)
32616                     ret.on('activate', function(gp) {
32617                         if (!gp.grid.rendered) {
32618                     //        gp.grid.render(el);
32619                         }
32620                     });
32621                 } else {
32622                   //  cfg.grid.render(el);
32623                 }
32624                 */
32625                 break;
32626            
32627            
32628             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
32629                 // it was the old xcomponent building that caused this before.
32630                 // espeically if border is the top element in the tree.
32631                 ret = this;
32632                 break; 
32633                 
32634                     
32635                 
32636                 
32637                 
32638             default:
32639                 /*
32640                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
32641                     
32642                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32643                     this.add(region, ret);
32644                 } else {
32645                 */
32646                     Roo.log(cfg);
32647                     throw "Can not add '" + cfg.xtype + "' to Border";
32648                     return null;
32649              
32650                                 
32651              
32652         }
32653         this.beginUpdate();
32654         // add children..
32655         var region = '';
32656         var abn = {};
32657         Roo.each(xitems, function(i)  {
32658             region = nb && i.region ? i.region : false;
32659             
32660             var add = ret.addxtype(i);
32661            
32662             if (region) {
32663                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
32664                 if (!i.background) {
32665                     abn[region] = nb[region] ;
32666                 }
32667             }
32668             
32669         });
32670         this.endUpdate();
32671
32672         // make the last non-background panel active..
32673         //if (nb) { Roo.log(abn); }
32674         if (nb) {
32675             
32676             for(var r in abn) {
32677                 region = this.getRegion(r);
32678                 if (region) {
32679                     // tried using nb[r], but it does not work..
32680                      
32681                     region.showPanel(abn[r]);
32682                    
32683                 }
32684             }
32685         }
32686         return ret;
32687         
32688     },
32689     
32690     
32691 // private
32692     factory : function(cfg)
32693     {
32694         
32695         var validRegions = Roo.bootstrap.layout.Border.regions;
32696
32697         var target = cfg.region;
32698         cfg.mgr = this;
32699         
32700         var r = Roo.bootstrap.layout;
32701         Roo.log(target);
32702         switch(target){
32703             case "north":
32704                 return new r.North(cfg);
32705             case "south":
32706                 return new r.South(cfg);
32707             case "east":
32708                 return new r.East(cfg);
32709             case "west":
32710                 return new r.West(cfg);
32711             case "center":
32712                 return new r.Center(cfg);
32713         }
32714         throw 'Layout region "'+target+'" not supported.';
32715     }
32716     
32717     
32718 });
32719  /*
32720  * Based on:
32721  * Ext JS Library 1.1.1
32722  * Copyright(c) 2006-2007, Ext JS, LLC.
32723  *
32724  * Originally Released Under LGPL - original licence link has changed is not relivant.
32725  *
32726  * Fork - LGPL
32727  * <script type="text/javascript">
32728  */
32729  
32730 /**
32731  * @class Roo.bootstrap.layout.Basic
32732  * @extends Roo.util.Observable
32733  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
32734  * and does not have a titlebar, tabs or any other features. All it does is size and position 
32735  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
32736  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
32737  * @cfg {string}   region  the region that it inhabits..
32738  * @cfg {bool}   skipConfig skip config?
32739  * 
32740
32741  */
32742 Roo.bootstrap.layout.Basic = function(config){
32743     
32744     this.mgr = config.mgr;
32745     
32746     this.position = config.region;
32747     
32748     var skipConfig = config.skipConfig;
32749     
32750     this.events = {
32751         /**
32752          * @scope Roo.BasicLayoutRegion
32753          */
32754         
32755         /**
32756          * @event beforeremove
32757          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
32758          * @param {Roo.LayoutRegion} this
32759          * @param {Roo.ContentPanel} panel The panel
32760          * @param {Object} e The cancel event object
32761          */
32762         "beforeremove" : true,
32763         /**
32764          * @event invalidated
32765          * Fires when the layout for this region is changed.
32766          * @param {Roo.LayoutRegion} this
32767          */
32768         "invalidated" : true,
32769         /**
32770          * @event visibilitychange
32771          * Fires when this region is shown or hidden 
32772          * @param {Roo.LayoutRegion} this
32773          * @param {Boolean} visibility true or false
32774          */
32775         "visibilitychange" : true,
32776         /**
32777          * @event paneladded
32778          * Fires when a panel is added. 
32779          * @param {Roo.LayoutRegion} this
32780          * @param {Roo.ContentPanel} panel The panel
32781          */
32782         "paneladded" : true,
32783         /**
32784          * @event panelremoved
32785          * Fires when a panel is removed. 
32786          * @param {Roo.LayoutRegion} this
32787          * @param {Roo.ContentPanel} panel The panel
32788          */
32789         "panelremoved" : true,
32790         /**
32791          * @event beforecollapse
32792          * Fires when this region before collapse.
32793          * @param {Roo.LayoutRegion} this
32794          */
32795         "beforecollapse" : true,
32796         /**
32797          * @event collapsed
32798          * Fires when this region is collapsed.
32799          * @param {Roo.LayoutRegion} this
32800          */
32801         "collapsed" : true,
32802         /**
32803          * @event expanded
32804          * Fires when this region is expanded.
32805          * @param {Roo.LayoutRegion} this
32806          */
32807         "expanded" : true,
32808         /**
32809          * @event slideshow
32810          * Fires when this region is slid into view.
32811          * @param {Roo.LayoutRegion} this
32812          */
32813         "slideshow" : true,
32814         /**
32815          * @event slidehide
32816          * Fires when this region slides out of view. 
32817          * @param {Roo.LayoutRegion} this
32818          */
32819         "slidehide" : true,
32820         /**
32821          * @event panelactivated
32822          * Fires when a panel is activated. 
32823          * @param {Roo.LayoutRegion} this
32824          * @param {Roo.ContentPanel} panel The activated panel
32825          */
32826         "panelactivated" : true,
32827         /**
32828          * @event resized
32829          * Fires when the user resizes this region. 
32830          * @param {Roo.LayoutRegion} this
32831          * @param {Number} newSize The new size (width for east/west, height for north/south)
32832          */
32833         "resized" : true
32834     };
32835     /** A collection of panels in this region. @type Roo.util.MixedCollection */
32836     this.panels = new Roo.util.MixedCollection();
32837     this.panels.getKey = this.getPanelId.createDelegate(this);
32838     this.box = null;
32839     this.activePanel = null;
32840     // ensure listeners are added...
32841     
32842     if (config.listeners || config.events) {
32843         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
32844             listeners : config.listeners || {},
32845             events : config.events || {}
32846         });
32847     }
32848     
32849     if(skipConfig !== true){
32850         this.applyConfig(config);
32851     }
32852 };
32853
32854 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
32855 {
32856     getPanelId : function(p){
32857         return p.getId();
32858     },
32859     
32860     applyConfig : function(config){
32861         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32862         this.config = config;
32863         
32864     },
32865     
32866     /**
32867      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
32868      * the width, for horizontal (north, south) the height.
32869      * @param {Number} newSize The new width or height
32870      */
32871     resizeTo : function(newSize){
32872         var el = this.el ? this.el :
32873                  (this.activePanel ? this.activePanel.getEl() : null);
32874         if(el){
32875             switch(this.position){
32876                 case "east":
32877                 case "west":
32878                     el.setWidth(newSize);
32879                     this.fireEvent("resized", this, newSize);
32880                 break;
32881                 case "north":
32882                 case "south":
32883                     el.setHeight(newSize);
32884                     this.fireEvent("resized", this, newSize);
32885                 break;                
32886             }
32887         }
32888     },
32889     
32890     getBox : function(){
32891         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
32892     },
32893     
32894     getMargins : function(){
32895         return this.margins;
32896     },
32897     
32898     updateBox : function(box){
32899         this.box = box;
32900         var el = this.activePanel.getEl();
32901         el.dom.style.left = box.x + "px";
32902         el.dom.style.top = box.y + "px";
32903         this.activePanel.setSize(box.width, box.height);
32904     },
32905     
32906     /**
32907      * Returns the container element for this region.
32908      * @return {Roo.Element}
32909      */
32910     getEl : function(){
32911         return this.activePanel;
32912     },
32913     
32914     /**
32915      * Returns true if this region is currently visible.
32916      * @return {Boolean}
32917      */
32918     isVisible : function(){
32919         return this.activePanel ? true : false;
32920     },
32921     
32922     setActivePanel : function(panel){
32923         panel = this.getPanel(panel);
32924         if(this.activePanel && this.activePanel != panel){
32925             this.activePanel.setActiveState(false);
32926             this.activePanel.getEl().setLeftTop(-10000,-10000);
32927         }
32928         this.activePanel = panel;
32929         panel.setActiveState(true);
32930         if(this.box){
32931             panel.setSize(this.box.width, this.box.height);
32932         }
32933         this.fireEvent("panelactivated", this, panel);
32934         this.fireEvent("invalidated");
32935     },
32936     
32937     /**
32938      * Show the specified panel.
32939      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
32940      * @return {Roo.ContentPanel} The shown panel or null
32941      */
32942     showPanel : function(panel){
32943         panel = this.getPanel(panel);
32944         if(panel){
32945             this.setActivePanel(panel);
32946         }
32947         return panel;
32948     },
32949     
32950     /**
32951      * Get the active panel for this region.
32952      * @return {Roo.ContentPanel} The active panel or null
32953      */
32954     getActivePanel : function(){
32955         return this.activePanel;
32956     },
32957     
32958     /**
32959      * Add the passed ContentPanel(s)
32960      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32961      * @return {Roo.ContentPanel} The panel added (if only one was added)
32962      */
32963     add : function(panel){
32964         if(arguments.length > 1){
32965             for(var i = 0, len = arguments.length; i < len; i++) {
32966                 this.add(arguments[i]);
32967             }
32968             return null;
32969         }
32970         if(this.hasPanel(panel)){
32971             this.showPanel(panel);
32972             return panel;
32973         }
32974         var el = panel.getEl();
32975         if(el.dom.parentNode != this.mgr.el.dom){
32976             this.mgr.el.dom.appendChild(el.dom);
32977         }
32978         if(panel.setRegion){
32979             panel.setRegion(this);
32980         }
32981         this.panels.add(panel);
32982         el.setStyle("position", "absolute");
32983         if(!panel.background){
32984             this.setActivePanel(panel);
32985             if(this.config.initialSize && this.panels.getCount()==1){
32986                 this.resizeTo(this.config.initialSize);
32987             }
32988         }
32989         this.fireEvent("paneladded", this, panel);
32990         return panel;
32991     },
32992     
32993     /**
32994      * Returns true if the panel is in this region.
32995      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32996      * @return {Boolean}
32997      */
32998     hasPanel : function(panel){
32999         if(typeof panel == "object"){ // must be panel obj
33000             panel = panel.getId();
33001         }
33002         return this.getPanel(panel) ? true : false;
33003     },
33004     
33005     /**
33006      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33007      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33008      * @param {Boolean} preservePanel Overrides the config preservePanel option
33009      * @return {Roo.ContentPanel} The panel that was removed
33010      */
33011     remove : function(panel, preservePanel){
33012         panel = this.getPanel(panel);
33013         if(!panel){
33014             return null;
33015         }
33016         var e = {};
33017         this.fireEvent("beforeremove", this, panel, e);
33018         if(e.cancel === true){
33019             return null;
33020         }
33021         var panelId = panel.getId();
33022         this.panels.removeKey(panelId);
33023         return panel;
33024     },
33025     
33026     /**
33027      * Returns the panel specified or null if it's not in this region.
33028      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33029      * @return {Roo.ContentPanel}
33030      */
33031     getPanel : function(id){
33032         if(typeof id == "object"){ // must be panel obj
33033             return id;
33034         }
33035         return this.panels.get(id);
33036     },
33037     
33038     /**
33039      * Returns this regions position (north/south/east/west/center).
33040      * @return {String} 
33041      */
33042     getPosition: function(){
33043         return this.position;    
33044     }
33045 });/*
33046  * Based on:
33047  * Ext JS Library 1.1.1
33048  * Copyright(c) 2006-2007, Ext JS, LLC.
33049  *
33050  * Originally Released Under LGPL - original licence link has changed is not relivant.
33051  *
33052  * Fork - LGPL
33053  * <script type="text/javascript">
33054  */
33055  
33056 /**
33057  * @class Roo.bootstrap.layout.Region
33058  * @extends Roo.bootstrap.layout.Basic
33059  * This class represents a region in a layout manager.
33060  
33061  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33062  * @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})
33063  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
33064  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
33065  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
33066  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
33067  * @cfg {String}    title           The title for the region (overrides panel titles)
33068  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
33069  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33070  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
33071  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33072  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
33073  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33074  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
33075  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
33076  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
33077  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
33078
33079  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
33080  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
33081  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
33082  * @cfg {Number}    width           For East/West panels
33083  * @cfg {Number}    height          For North/South panels
33084  * @cfg {Boolean}   split           To show the splitter
33085  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
33086  * 
33087  * @cfg {string}   cls             Extra CSS classes to add to region
33088  * 
33089  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
33090  * @cfg {string}   region  the region that it inhabits..
33091  *
33092
33093  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
33094  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
33095
33096  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
33097  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
33098  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
33099  */
33100 Roo.bootstrap.layout.Region = function(config)
33101 {
33102     this.applyConfig(config);
33103
33104     var mgr = config.mgr;
33105     var pos = config.region;
33106     config.skipConfig = true;
33107     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
33108     
33109     if (mgr.el) {
33110         this.onRender(mgr.el);   
33111     }
33112      
33113     this.visible = true;
33114     this.collapsed = false;
33115     this.unrendered_panels = [];
33116 };
33117
33118 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
33119
33120     position: '', // set by wrapper (eg. north/south etc..)
33121     unrendered_panels : null,  // unrendered panels.
33122     createBody : function(){
33123         /** This region's body element 
33124         * @type Roo.Element */
33125         this.bodyEl = this.el.createChild({
33126                 tag: "div",
33127                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
33128         });
33129     },
33130
33131     onRender: function(ctr, pos)
33132     {
33133         var dh = Roo.DomHelper;
33134         /** This region's container element 
33135         * @type Roo.Element */
33136         this.el = dh.append(ctr.dom, {
33137                 tag: "div",
33138                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
33139             }, true);
33140         /** This region's title element 
33141         * @type Roo.Element */
33142     
33143         this.titleEl = dh.append(this.el.dom,
33144             {
33145                     tag: "div",
33146                     unselectable: "on",
33147                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
33148                     children:[
33149                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
33150                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
33151                     ]}, true);
33152         
33153         this.titleEl.enableDisplayMode();
33154         /** This region's title text element 
33155         * @type HTMLElement */
33156         this.titleTextEl = this.titleEl.dom.firstChild;
33157         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33158         /*
33159         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
33160         this.closeBtn.enableDisplayMode();
33161         this.closeBtn.on("click", this.closeClicked, this);
33162         this.closeBtn.hide();
33163     */
33164         this.createBody(this.config);
33165         if(this.config.hideWhenEmpty){
33166             this.hide();
33167             this.on("paneladded", this.validateVisibility, this);
33168             this.on("panelremoved", this.validateVisibility, this);
33169         }
33170         if(this.autoScroll){
33171             this.bodyEl.setStyle("overflow", "auto");
33172         }else{
33173             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
33174         }
33175         //if(c.titlebar !== false){
33176             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
33177                 this.titleEl.hide();
33178             }else{
33179                 this.titleEl.show();
33180                 if(this.config.title){
33181                     this.titleTextEl.innerHTML = this.config.title;
33182                 }
33183             }
33184         //}
33185         if(this.config.collapsed){
33186             this.collapse(true);
33187         }
33188         if(this.config.hidden){
33189             this.hide();
33190         }
33191         
33192         if (this.unrendered_panels && this.unrendered_panels.length) {
33193             for (var i =0;i< this.unrendered_panels.length; i++) {
33194                 this.add(this.unrendered_panels[i]);
33195             }
33196             this.unrendered_panels = null;
33197             
33198         }
33199         
33200     },
33201     
33202     applyConfig : function(c)
33203     {
33204         /*
33205          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
33206             var dh = Roo.DomHelper;
33207             if(c.titlebar !== false){
33208                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
33209                 this.collapseBtn.on("click", this.collapse, this);
33210                 this.collapseBtn.enableDisplayMode();
33211                 /*
33212                 if(c.showPin === true || this.showPin){
33213                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
33214                     this.stickBtn.enableDisplayMode();
33215                     this.stickBtn.on("click", this.expand, this);
33216                     this.stickBtn.hide();
33217                 }
33218                 
33219             }
33220             */
33221             /** This region's collapsed element
33222             * @type Roo.Element */
33223             /*
33224              *
33225             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33226                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33227             ]}, true);
33228             
33229             if(c.floatable !== false){
33230                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33231                this.collapsedEl.on("click", this.collapseClick, this);
33232             }
33233
33234             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33235                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33236                    id: "message", unselectable: "on", style:{"float":"left"}});
33237                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33238              }
33239             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33240             this.expandBtn.on("click", this.expand, this);
33241             
33242         }
33243         
33244         if(this.collapseBtn){
33245             this.collapseBtn.setVisible(c.collapsible == true);
33246         }
33247         
33248         this.cmargins = c.cmargins || this.cmargins ||
33249                          (this.position == "west" || this.position == "east" ?
33250                              {top: 0, left: 2, right:2, bottom: 0} :
33251                              {top: 2, left: 0, right:0, bottom: 2});
33252         */
33253         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33254         
33255         
33256         this.bottomTabs = c.tabPosition != "top";
33257         
33258         this.autoScroll = c.autoScroll || false;
33259         
33260         
33261        
33262         
33263         this.duration = c.duration || .30;
33264         this.slideDuration = c.slideDuration || .45;
33265         this.config = c;
33266        
33267     },
33268     /**
33269      * Returns true if this region is currently visible.
33270      * @return {Boolean}
33271      */
33272     isVisible : function(){
33273         return this.visible;
33274     },
33275
33276     /**
33277      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33278      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
33279      */
33280     //setCollapsedTitle : function(title){
33281     //    title = title || "&#160;";
33282      //   if(this.collapsedTitleTextEl){
33283       //      this.collapsedTitleTextEl.innerHTML = title;
33284        // }
33285     //},
33286
33287     getBox : function(){
33288         var b;
33289       //  if(!this.collapsed){
33290             b = this.el.getBox(false, true);
33291        // }else{
33292           //  b = this.collapsedEl.getBox(false, true);
33293         //}
33294         return b;
33295     },
33296
33297     getMargins : function(){
33298         return this.margins;
33299         //return this.collapsed ? this.cmargins : this.margins;
33300     },
33301 /*
33302     highlight : function(){
33303         this.el.addClass("x-layout-panel-dragover");
33304     },
33305
33306     unhighlight : function(){
33307         this.el.removeClass("x-layout-panel-dragover");
33308     },
33309 */
33310     updateBox : function(box)
33311     {
33312         if (!this.bodyEl) {
33313             return; // not rendered yet..
33314         }
33315         
33316         this.box = box;
33317         if(!this.collapsed){
33318             this.el.dom.style.left = box.x + "px";
33319             this.el.dom.style.top = box.y + "px";
33320             this.updateBody(box.width, box.height);
33321         }else{
33322             this.collapsedEl.dom.style.left = box.x + "px";
33323             this.collapsedEl.dom.style.top = box.y + "px";
33324             this.collapsedEl.setSize(box.width, box.height);
33325         }
33326         if(this.tabs){
33327             this.tabs.autoSizeTabs();
33328         }
33329     },
33330
33331     updateBody : function(w, h)
33332     {
33333         if(w !== null){
33334             this.el.setWidth(w);
33335             w -= this.el.getBorderWidth("rl");
33336             if(this.config.adjustments){
33337                 w += this.config.adjustments[0];
33338             }
33339         }
33340         if(h !== null && h > 0){
33341             this.el.setHeight(h);
33342             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
33343             h -= this.el.getBorderWidth("tb");
33344             if(this.config.adjustments){
33345                 h += this.config.adjustments[1];
33346             }
33347             this.bodyEl.setHeight(h);
33348             if(this.tabs){
33349                 h = this.tabs.syncHeight(h);
33350             }
33351         }
33352         if(this.panelSize){
33353             w = w !== null ? w : this.panelSize.width;
33354             h = h !== null ? h : this.panelSize.height;
33355         }
33356         if(this.activePanel){
33357             var el = this.activePanel.getEl();
33358             w = w !== null ? w : el.getWidth();
33359             h = h !== null ? h : el.getHeight();
33360             this.panelSize = {width: w, height: h};
33361             this.activePanel.setSize(w, h);
33362         }
33363         if(Roo.isIE && this.tabs){
33364             this.tabs.el.repaint();
33365         }
33366     },
33367
33368     /**
33369      * Returns the container element for this region.
33370      * @return {Roo.Element}
33371      */
33372     getEl : function(){
33373         return this.el;
33374     },
33375
33376     /**
33377      * Hides this region.
33378      */
33379     hide : function(){
33380         //if(!this.collapsed){
33381             this.el.dom.style.left = "-2000px";
33382             this.el.hide();
33383         //}else{
33384          //   this.collapsedEl.dom.style.left = "-2000px";
33385          //   this.collapsedEl.hide();
33386        // }
33387         this.visible = false;
33388         this.fireEvent("visibilitychange", this, false);
33389     },
33390
33391     /**
33392      * Shows this region if it was previously hidden.
33393      */
33394     show : function(){
33395         //if(!this.collapsed){
33396             this.el.show();
33397         //}else{
33398         //    this.collapsedEl.show();
33399        // }
33400         this.visible = true;
33401         this.fireEvent("visibilitychange", this, true);
33402     },
33403 /*
33404     closeClicked : function(){
33405         if(this.activePanel){
33406             this.remove(this.activePanel);
33407         }
33408     },
33409
33410     collapseClick : function(e){
33411         if(this.isSlid){
33412            e.stopPropagation();
33413            this.slideIn();
33414         }else{
33415            e.stopPropagation();
33416            this.slideOut();
33417         }
33418     },
33419 */
33420     /**
33421      * Collapses this region.
33422      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
33423      */
33424     /*
33425     collapse : function(skipAnim, skipCheck = false){
33426         if(this.collapsed) {
33427             return;
33428         }
33429         
33430         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
33431             
33432             this.collapsed = true;
33433             if(this.split){
33434                 this.split.el.hide();
33435             }
33436             if(this.config.animate && skipAnim !== true){
33437                 this.fireEvent("invalidated", this);
33438                 this.animateCollapse();
33439             }else{
33440                 this.el.setLocation(-20000,-20000);
33441                 this.el.hide();
33442                 this.collapsedEl.show();
33443                 this.fireEvent("collapsed", this);
33444                 this.fireEvent("invalidated", this);
33445             }
33446         }
33447         
33448     },
33449 */
33450     animateCollapse : function(){
33451         // overridden
33452     },
33453
33454     /**
33455      * Expands this region if it was previously collapsed.
33456      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
33457      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
33458      */
33459     /*
33460     expand : function(e, skipAnim){
33461         if(e) {
33462             e.stopPropagation();
33463         }
33464         if(!this.collapsed || this.el.hasActiveFx()) {
33465             return;
33466         }
33467         if(this.isSlid){
33468             this.afterSlideIn();
33469             skipAnim = true;
33470         }
33471         this.collapsed = false;
33472         if(this.config.animate && skipAnim !== true){
33473             this.animateExpand();
33474         }else{
33475             this.el.show();
33476             if(this.split){
33477                 this.split.el.show();
33478             }
33479             this.collapsedEl.setLocation(-2000,-2000);
33480             this.collapsedEl.hide();
33481             this.fireEvent("invalidated", this);
33482             this.fireEvent("expanded", this);
33483         }
33484     },
33485 */
33486     animateExpand : function(){
33487         // overridden
33488     },
33489
33490     initTabs : function()
33491     {
33492         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
33493         
33494         var ts = new Roo.bootstrap.panel.Tabs({
33495                 el: this.bodyEl.dom,
33496                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
33497                 disableTooltips: this.config.disableTabTips,
33498                 toolbar : this.config.toolbar
33499             });
33500         
33501         if(this.config.hideTabs){
33502             ts.stripWrap.setDisplayed(false);
33503         }
33504         this.tabs = ts;
33505         ts.resizeTabs = this.config.resizeTabs === true;
33506         ts.minTabWidth = this.config.minTabWidth || 40;
33507         ts.maxTabWidth = this.config.maxTabWidth || 250;
33508         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
33509         ts.monitorResize = false;
33510         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
33511         ts.bodyEl.addClass('roo-layout-tabs-body');
33512         this.panels.each(this.initPanelAsTab, this);
33513     },
33514
33515     initPanelAsTab : function(panel){
33516         var ti = this.tabs.addTab(
33517             panel.getEl().id,
33518             panel.getTitle(),
33519             null,
33520             this.config.closeOnTab && panel.isClosable(),
33521             panel.tpl
33522         );
33523         if(panel.tabTip !== undefined){
33524             ti.setTooltip(panel.tabTip);
33525         }
33526         ti.on("activate", function(){
33527               this.setActivePanel(panel);
33528         }, this);
33529         
33530         if(this.config.closeOnTab){
33531             ti.on("beforeclose", function(t, e){
33532                 e.cancel = true;
33533                 this.remove(panel);
33534             }, this);
33535         }
33536         
33537         panel.tabItem = ti;
33538         
33539         return ti;
33540     },
33541
33542     updatePanelTitle : function(panel, title)
33543     {
33544         if(this.activePanel == panel){
33545             this.updateTitle(title);
33546         }
33547         if(this.tabs){
33548             var ti = this.tabs.getTab(panel.getEl().id);
33549             ti.setText(title);
33550             if(panel.tabTip !== undefined){
33551                 ti.setTooltip(panel.tabTip);
33552             }
33553         }
33554     },
33555
33556     updateTitle : function(title){
33557         if(this.titleTextEl && !this.config.title){
33558             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
33559         }
33560     },
33561
33562     setActivePanel : function(panel)
33563     {
33564         panel = this.getPanel(panel);
33565         if(this.activePanel && this.activePanel != panel){
33566             this.activePanel.setActiveState(false);
33567         }
33568         this.activePanel = panel;
33569         panel.setActiveState(true);
33570         if(this.panelSize){
33571             panel.setSize(this.panelSize.width, this.panelSize.height);
33572         }
33573         if(this.closeBtn){
33574             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
33575         }
33576         this.updateTitle(panel.getTitle());
33577         if(this.tabs){
33578             this.fireEvent("invalidated", this);
33579         }
33580         this.fireEvent("panelactivated", this, panel);
33581     },
33582
33583     /**
33584      * Shows the specified panel.
33585      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
33586      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
33587      */
33588     showPanel : function(panel)
33589     {
33590         panel = this.getPanel(panel);
33591         if(panel){
33592             if(this.tabs){
33593                 var tab = this.tabs.getTab(panel.getEl().id);
33594                 if(tab.isHidden()){
33595                     this.tabs.unhideTab(tab.id);
33596                 }
33597                 tab.activate();
33598             }else{
33599                 this.setActivePanel(panel);
33600             }
33601         }
33602         return panel;
33603     },
33604
33605     /**
33606      * Get the active panel for this region.
33607      * @return {Roo.ContentPanel} The active panel or null
33608      */
33609     getActivePanel : function(){
33610         return this.activePanel;
33611     },
33612
33613     validateVisibility : function(){
33614         if(this.panels.getCount() < 1){
33615             this.updateTitle("&#160;");
33616             this.closeBtn.hide();
33617             this.hide();
33618         }else{
33619             if(!this.isVisible()){
33620                 this.show();
33621             }
33622         }
33623     },
33624
33625     /**
33626      * Adds the passed ContentPanel(s) to this region.
33627      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33628      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
33629      */
33630     add : function(panel)
33631     {
33632         if(arguments.length > 1){
33633             for(var i = 0, len = arguments.length; i < len; i++) {
33634                 this.add(arguments[i]);
33635             }
33636             return null;
33637         }
33638         
33639         // if we have not been rendered yet, then we can not really do much of this..
33640         if (!this.bodyEl) {
33641             this.unrendered_panels.push(panel);
33642             return panel;
33643         }
33644         
33645         
33646         
33647         
33648         if(this.hasPanel(panel)){
33649             this.showPanel(panel);
33650             return panel;
33651         }
33652         panel.setRegion(this);
33653         this.panels.add(panel);
33654        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
33655             // sinle panel - no tab...?? would it not be better to render it with the tabs,
33656             // and hide them... ???
33657             this.bodyEl.dom.appendChild(panel.getEl().dom);
33658             if(panel.background !== true){
33659                 this.setActivePanel(panel);
33660             }
33661             this.fireEvent("paneladded", this, panel);
33662             return panel;
33663         }
33664         */
33665         if(!this.tabs){
33666             this.initTabs();
33667         }else{
33668             this.initPanelAsTab(panel);
33669         }
33670         
33671         
33672         if(panel.background !== true){
33673             this.tabs.activate(panel.getEl().id);
33674         }
33675         this.fireEvent("paneladded", this, panel);
33676         return panel;
33677     },
33678
33679     /**
33680      * Hides the tab for the specified panel.
33681      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33682      */
33683     hidePanel : function(panel){
33684         if(this.tabs && (panel = this.getPanel(panel))){
33685             this.tabs.hideTab(panel.getEl().id);
33686         }
33687     },
33688
33689     /**
33690      * Unhides the tab for a previously hidden panel.
33691      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33692      */
33693     unhidePanel : function(panel){
33694         if(this.tabs && (panel = this.getPanel(panel))){
33695             this.tabs.unhideTab(panel.getEl().id);
33696         }
33697     },
33698
33699     clearPanels : function(){
33700         while(this.panels.getCount() > 0){
33701              this.remove(this.panels.first());
33702         }
33703     },
33704
33705     /**
33706      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33707      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33708      * @param {Boolean} preservePanel Overrides the config preservePanel option
33709      * @return {Roo.ContentPanel} The panel that was removed
33710      */
33711     remove : function(panel, preservePanel)
33712     {
33713         panel = this.getPanel(panel);
33714         if(!panel){
33715             return null;
33716         }
33717         var e = {};
33718         this.fireEvent("beforeremove", this, panel, e);
33719         if(e.cancel === true){
33720             return null;
33721         }
33722         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
33723         var panelId = panel.getId();
33724         this.panels.removeKey(panelId);
33725         if(preservePanel){
33726             document.body.appendChild(panel.getEl().dom);
33727         }
33728         if(this.tabs){
33729             this.tabs.removeTab(panel.getEl().id);
33730         }else if (!preservePanel){
33731             this.bodyEl.dom.removeChild(panel.getEl().dom);
33732         }
33733         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
33734             var p = this.panels.first();
33735             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
33736             tempEl.appendChild(p.getEl().dom);
33737             this.bodyEl.update("");
33738             this.bodyEl.dom.appendChild(p.getEl().dom);
33739             tempEl = null;
33740             this.updateTitle(p.getTitle());
33741             this.tabs = null;
33742             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33743             this.setActivePanel(p);
33744         }
33745         panel.setRegion(null);
33746         if(this.activePanel == panel){
33747             this.activePanel = null;
33748         }
33749         if(this.config.autoDestroy !== false && preservePanel !== true){
33750             try{panel.destroy();}catch(e){}
33751         }
33752         this.fireEvent("panelremoved", this, panel);
33753         return panel;
33754     },
33755
33756     /**
33757      * Returns the TabPanel component used by this region
33758      * @return {Roo.TabPanel}
33759      */
33760     getTabs : function(){
33761         return this.tabs;
33762     },
33763
33764     createTool : function(parentEl, className){
33765         var btn = Roo.DomHelper.append(parentEl, {
33766             tag: "div",
33767             cls: "x-layout-tools-button",
33768             children: [ {
33769                 tag: "div",
33770                 cls: "roo-layout-tools-button-inner " + className,
33771                 html: "&#160;"
33772             }]
33773         }, true);
33774         btn.addClassOnOver("roo-layout-tools-button-over");
33775         return btn;
33776     }
33777 });/*
33778  * Based on:
33779  * Ext JS Library 1.1.1
33780  * Copyright(c) 2006-2007, Ext JS, LLC.
33781  *
33782  * Originally Released Under LGPL - original licence link has changed is not relivant.
33783  *
33784  * Fork - LGPL
33785  * <script type="text/javascript">
33786  */
33787  
33788
33789
33790 /**
33791  * @class Roo.SplitLayoutRegion
33792  * @extends Roo.LayoutRegion
33793  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
33794  */
33795 Roo.bootstrap.layout.Split = function(config){
33796     this.cursor = config.cursor;
33797     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
33798 };
33799
33800 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
33801 {
33802     splitTip : "Drag to resize.",
33803     collapsibleSplitTip : "Drag to resize. Double click to hide.",
33804     useSplitTips : false,
33805
33806     applyConfig : function(config){
33807         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
33808     },
33809     
33810     onRender : function(ctr,pos) {
33811         
33812         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
33813         if(!this.config.split){
33814             return;
33815         }
33816         if(!this.split){
33817             
33818             var splitEl = Roo.DomHelper.append(ctr.dom,  {
33819                             tag: "div",
33820                             id: this.el.id + "-split",
33821                             cls: "roo-layout-split roo-layout-split-"+this.position,
33822                             html: "&#160;"
33823             });
33824             /** The SplitBar for this region 
33825             * @type Roo.SplitBar */
33826             // does not exist yet...
33827             Roo.log([this.position, this.orientation]);
33828             
33829             this.split = new Roo.bootstrap.SplitBar({
33830                 dragElement : splitEl,
33831                 resizingElement: this.el,
33832                 orientation : this.orientation
33833             });
33834             
33835             this.split.on("moved", this.onSplitMove, this);
33836             this.split.useShim = this.config.useShim === true;
33837             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
33838             if(this.useSplitTips){
33839                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
33840             }
33841             //if(config.collapsible){
33842             //    this.split.el.on("dblclick", this.collapse,  this);
33843             //}
33844         }
33845         if(typeof this.config.minSize != "undefined"){
33846             this.split.minSize = this.config.minSize;
33847         }
33848         if(typeof this.config.maxSize != "undefined"){
33849             this.split.maxSize = this.config.maxSize;
33850         }
33851         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
33852             this.hideSplitter();
33853         }
33854         
33855     },
33856
33857     getHMaxSize : function(){
33858          var cmax = this.config.maxSize || 10000;
33859          var center = this.mgr.getRegion("center");
33860          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
33861     },
33862
33863     getVMaxSize : function(){
33864          var cmax = this.config.maxSize || 10000;
33865          var center = this.mgr.getRegion("center");
33866          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
33867     },
33868
33869     onSplitMove : function(split, newSize){
33870         this.fireEvent("resized", this, newSize);
33871     },
33872     
33873     /** 
33874      * Returns the {@link Roo.SplitBar} for this region.
33875      * @return {Roo.SplitBar}
33876      */
33877     getSplitBar : function(){
33878         return this.split;
33879     },
33880     
33881     hide : function(){
33882         this.hideSplitter();
33883         Roo.bootstrap.layout.Split.superclass.hide.call(this);
33884     },
33885
33886     hideSplitter : function(){
33887         if(this.split){
33888             this.split.el.setLocation(-2000,-2000);
33889             this.split.el.hide();
33890         }
33891     },
33892
33893     show : function(){
33894         if(this.split){
33895             this.split.el.show();
33896         }
33897         Roo.bootstrap.layout.Split.superclass.show.call(this);
33898     },
33899     
33900     beforeSlide: function(){
33901         if(Roo.isGecko){// firefox overflow auto bug workaround
33902             this.bodyEl.clip();
33903             if(this.tabs) {
33904                 this.tabs.bodyEl.clip();
33905             }
33906             if(this.activePanel){
33907                 this.activePanel.getEl().clip();
33908                 
33909                 if(this.activePanel.beforeSlide){
33910                     this.activePanel.beforeSlide();
33911                 }
33912             }
33913         }
33914     },
33915     
33916     afterSlide : function(){
33917         if(Roo.isGecko){// firefox overflow auto bug workaround
33918             this.bodyEl.unclip();
33919             if(this.tabs) {
33920                 this.tabs.bodyEl.unclip();
33921             }
33922             if(this.activePanel){
33923                 this.activePanel.getEl().unclip();
33924                 if(this.activePanel.afterSlide){
33925                     this.activePanel.afterSlide();
33926                 }
33927             }
33928         }
33929     },
33930
33931     initAutoHide : function(){
33932         if(this.autoHide !== false){
33933             if(!this.autoHideHd){
33934                 var st = new Roo.util.DelayedTask(this.slideIn, this);
33935                 this.autoHideHd = {
33936                     "mouseout": function(e){
33937                         if(!e.within(this.el, true)){
33938                             st.delay(500);
33939                         }
33940                     },
33941                     "mouseover" : function(e){
33942                         st.cancel();
33943                     },
33944                     scope : this
33945                 };
33946             }
33947             this.el.on(this.autoHideHd);
33948         }
33949     },
33950
33951     clearAutoHide : function(){
33952         if(this.autoHide !== false){
33953             this.el.un("mouseout", this.autoHideHd.mouseout);
33954             this.el.un("mouseover", this.autoHideHd.mouseover);
33955         }
33956     },
33957
33958     clearMonitor : function(){
33959         Roo.get(document).un("click", this.slideInIf, this);
33960     },
33961
33962     // these names are backwards but not changed for compat
33963     slideOut : function(){
33964         if(this.isSlid || this.el.hasActiveFx()){
33965             return;
33966         }
33967         this.isSlid = true;
33968         if(this.collapseBtn){
33969             this.collapseBtn.hide();
33970         }
33971         this.closeBtnState = this.closeBtn.getStyle('display');
33972         this.closeBtn.hide();
33973         if(this.stickBtn){
33974             this.stickBtn.show();
33975         }
33976         this.el.show();
33977         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
33978         this.beforeSlide();
33979         this.el.setStyle("z-index", 10001);
33980         this.el.slideIn(this.getSlideAnchor(), {
33981             callback: function(){
33982                 this.afterSlide();
33983                 this.initAutoHide();
33984                 Roo.get(document).on("click", this.slideInIf, this);
33985                 this.fireEvent("slideshow", this);
33986             },
33987             scope: this,
33988             block: true
33989         });
33990     },
33991
33992     afterSlideIn : function(){
33993         this.clearAutoHide();
33994         this.isSlid = false;
33995         this.clearMonitor();
33996         this.el.setStyle("z-index", "");
33997         if(this.collapseBtn){
33998             this.collapseBtn.show();
33999         }
34000         this.closeBtn.setStyle('display', this.closeBtnState);
34001         if(this.stickBtn){
34002             this.stickBtn.hide();
34003         }
34004         this.fireEvent("slidehide", this);
34005     },
34006
34007     slideIn : function(cb){
34008         if(!this.isSlid || this.el.hasActiveFx()){
34009             Roo.callback(cb);
34010             return;
34011         }
34012         this.isSlid = false;
34013         this.beforeSlide();
34014         this.el.slideOut(this.getSlideAnchor(), {
34015             callback: function(){
34016                 this.el.setLeftTop(-10000, -10000);
34017                 this.afterSlide();
34018                 this.afterSlideIn();
34019                 Roo.callback(cb);
34020             },
34021             scope: this,
34022             block: true
34023         });
34024     },
34025     
34026     slideInIf : function(e){
34027         if(!e.within(this.el)){
34028             this.slideIn();
34029         }
34030     },
34031
34032     animateCollapse : function(){
34033         this.beforeSlide();
34034         this.el.setStyle("z-index", 20000);
34035         var anchor = this.getSlideAnchor();
34036         this.el.slideOut(anchor, {
34037             callback : function(){
34038                 this.el.setStyle("z-index", "");
34039                 this.collapsedEl.slideIn(anchor, {duration:.3});
34040                 this.afterSlide();
34041                 this.el.setLocation(-10000,-10000);
34042                 this.el.hide();
34043                 this.fireEvent("collapsed", this);
34044             },
34045             scope: this,
34046             block: true
34047         });
34048     },
34049
34050     animateExpand : function(){
34051         this.beforeSlide();
34052         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34053         this.el.setStyle("z-index", 20000);
34054         this.collapsedEl.hide({
34055             duration:.1
34056         });
34057         this.el.slideIn(this.getSlideAnchor(), {
34058             callback : function(){
34059                 this.el.setStyle("z-index", "");
34060                 this.afterSlide();
34061                 if(this.split){
34062                     this.split.el.show();
34063                 }
34064                 this.fireEvent("invalidated", this);
34065                 this.fireEvent("expanded", this);
34066             },
34067             scope: this,
34068             block: true
34069         });
34070     },
34071
34072     anchors : {
34073         "west" : "left",
34074         "east" : "right",
34075         "north" : "top",
34076         "south" : "bottom"
34077     },
34078
34079     sanchors : {
34080         "west" : "l",
34081         "east" : "r",
34082         "north" : "t",
34083         "south" : "b"
34084     },
34085
34086     canchors : {
34087         "west" : "tl-tr",
34088         "east" : "tr-tl",
34089         "north" : "tl-bl",
34090         "south" : "bl-tl"
34091     },
34092
34093     getAnchor : function(){
34094         return this.anchors[this.position];
34095     },
34096
34097     getCollapseAnchor : function(){
34098         return this.canchors[this.position];
34099     },
34100
34101     getSlideAnchor : function(){
34102         return this.sanchors[this.position];
34103     },
34104
34105     getAlignAdj : function(){
34106         var cm = this.cmargins;
34107         switch(this.position){
34108             case "west":
34109                 return [0, 0];
34110             break;
34111             case "east":
34112                 return [0, 0];
34113             break;
34114             case "north":
34115                 return [0, 0];
34116             break;
34117             case "south":
34118                 return [0, 0];
34119             break;
34120         }
34121     },
34122
34123     getExpandAdj : function(){
34124         var c = this.collapsedEl, cm = this.cmargins;
34125         switch(this.position){
34126             case "west":
34127                 return [-(cm.right+c.getWidth()+cm.left), 0];
34128             break;
34129             case "east":
34130                 return [cm.right+c.getWidth()+cm.left, 0];
34131             break;
34132             case "north":
34133                 return [0, -(cm.top+cm.bottom+c.getHeight())];
34134             break;
34135             case "south":
34136                 return [0, cm.top+cm.bottom+c.getHeight()];
34137             break;
34138         }
34139     }
34140 });/*
34141  * Based on:
34142  * Ext JS Library 1.1.1
34143  * Copyright(c) 2006-2007, Ext JS, LLC.
34144  *
34145  * Originally Released Under LGPL - original licence link has changed is not relivant.
34146  *
34147  * Fork - LGPL
34148  * <script type="text/javascript">
34149  */
34150 /*
34151  * These classes are private internal classes
34152  */
34153 Roo.bootstrap.layout.Center = function(config){
34154     config.region = "center";
34155     Roo.bootstrap.layout.Region.call(this, config);
34156     this.visible = true;
34157     this.minWidth = config.minWidth || 20;
34158     this.minHeight = config.minHeight || 20;
34159 };
34160
34161 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
34162     hide : function(){
34163         // center panel can't be hidden
34164     },
34165     
34166     show : function(){
34167         // center panel can't be hidden
34168     },
34169     
34170     getMinWidth: function(){
34171         return this.minWidth;
34172     },
34173     
34174     getMinHeight: function(){
34175         return this.minHeight;
34176     }
34177 });
34178
34179
34180
34181
34182  
34183
34184
34185
34186
34187
34188 Roo.bootstrap.layout.North = function(config)
34189 {
34190     config.region = 'north';
34191     config.cursor = 'n-resize';
34192     
34193     Roo.bootstrap.layout.Split.call(this, config);
34194     
34195     
34196     if(this.split){
34197         this.split.placement = Roo.bootstrap.SplitBar.TOP;
34198         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
34199         this.split.el.addClass("roo-layout-split-v");
34200     }
34201     var size = config.initialSize || config.height;
34202     if(typeof size != "undefined"){
34203         this.el.setHeight(size);
34204     }
34205 };
34206 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
34207 {
34208     orientation: Roo.bootstrap.SplitBar.VERTICAL,
34209     
34210     
34211     
34212     getBox : function(){
34213         if(this.collapsed){
34214             return this.collapsedEl.getBox();
34215         }
34216         var box = this.el.getBox();
34217         if(this.split){
34218             box.height += this.split.el.getHeight();
34219         }
34220         return box;
34221     },
34222     
34223     updateBox : function(box){
34224         if(this.split && !this.collapsed){
34225             box.height -= this.split.el.getHeight();
34226             this.split.el.setLeft(box.x);
34227             this.split.el.setTop(box.y+box.height);
34228             this.split.el.setWidth(box.width);
34229         }
34230         if(this.collapsed){
34231             this.updateBody(box.width, null);
34232         }
34233         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34234     }
34235 });
34236
34237
34238
34239
34240
34241 Roo.bootstrap.layout.South = function(config){
34242     config.region = 'south';
34243     config.cursor = 's-resize';
34244     Roo.bootstrap.layout.Split.call(this, config);
34245     if(this.split){
34246         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
34247         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
34248         this.split.el.addClass("roo-layout-split-v");
34249     }
34250     var size = config.initialSize || config.height;
34251     if(typeof size != "undefined"){
34252         this.el.setHeight(size);
34253     }
34254 };
34255
34256 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
34257     orientation: Roo.bootstrap.SplitBar.VERTICAL,
34258     getBox : function(){
34259         if(this.collapsed){
34260             return this.collapsedEl.getBox();
34261         }
34262         var box = this.el.getBox();
34263         if(this.split){
34264             var sh = this.split.el.getHeight();
34265             box.height += sh;
34266             box.y -= sh;
34267         }
34268         return box;
34269     },
34270     
34271     updateBox : function(box){
34272         if(this.split && !this.collapsed){
34273             var sh = this.split.el.getHeight();
34274             box.height -= sh;
34275             box.y += sh;
34276             this.split.el.setLeft(box.x);
34277             this.split.el.setTop(box.y-sh);
34278             this.split.el.setWidth(box.width);
34279         }
34280         if(this.collapsed){
34281             this.updateBody(box.width, null);
34282         }
34283         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34284     }
34285 });
34286
34287 Roo.bootstrap.layout.East = function(config){
34288     config.region = "east";
34289     config.cursor = "e-resize";
34290     Roo.bootstrap.layout.Split.call(this, config);
34291     if(this.split){
34292         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
34293         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
34294         this.split.el.addClass("roo-layout-split-h");
34295     }
34296     var size = config.initialSize || config.width;
34297     if(typeof size != "undefined"){
34298         this.el.setWidth(size);
34299     }
34300 };
34301 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
34302     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
34303     getBox : function(){
34304         if(this.collapsed){
34305             return this.collapsedEl.getBox();
34306         }
34307         var box = this.el.getBox();
34308         if(this.split){
34309             var sw = this.split.el.getWidth();
34310             box.width += sw;
34311             box.x -= sw;
34312         }
34313         return box;
34314     },
34315
34316     updateBox : function(box){
34317         if(this.split && !this.collapsed){
34318             var sw = this.split.el.getWidth();
34319             box.width -= sw;
34320             this.split.el.setLeft(box.x);
34321             this.split.el.setTop(box.y);
34322             this.split.el.setHeight(box.height);
34323             box.x += sw;
34324         }
34325         if(this.collapsed){
34326             this.updateBody(null, box.height);
34327         }
34328         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34329     }
34330 });
34331
34332 Roo.bootstrap.layout.West = function(config){
34333     config.region = "west";
34334     config.cursor = "w-resize";
34335     
34336     Roo.bootstrap.layout.Split.call(this, config);
34337     if(this.split){
34338         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
34339         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
34340         this.split.el.addClass("roo-layout-split-h");
34341     }
34342     
34343 };
34344 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
34345     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
34346     
34347     onRender: function(ctr, pos)
34348     {
34349         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
34350         var size = this.config.initialSize || this.config.width;
34351         if(typeof size != "undefined"){
34352             this.el.setWidth(size);
34353         }
34354     },
34355     
34356     getBox : function(){
34357         if(this.collapsed){
34358             return this.collapsedEl.getBox();
34359         }
34360         var box = this.el.getBox();
34361         if(this.split){
34362             box.width += this.split.el.getWidth();
34363         }
34364         return box;
34365     },
34366     
34367     updateBox : function(box){
34368         if(this.split && !this.collapsed){
34369             var sw = this.split.el.getWidth();
34370             box.width -= sw;
34371             this.split.el.setLeft(box.x+box.width);
34372             this.split.el.setTop(box.y);
34373             this.split.el.setHeight(box.height);
34374         }
34375         if(this.collapsed){
34376             this.updateBody(null, box.height);
34377         }
34378         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34379     }
34380 });
34381 Roo.namespace("Roo.bootstrap.panel");/*
34382  * Based on:
34383  * Ext JS Library 1.1.1
34384  * Copyright(c) 2006-2007, Ext JS, LLC.
34385  *
34386  * Originally Released Under LGPL - original licence link has changed is not relivant.
34387  *
34388  * Fork - LGPL
34389  * <script type="text/javascript">
34390  */
34391 /**
34392  * @class Roo.ContentPanel
34393  * @extends Roo.util.Observable
34394  * A basic ContentPanel element.
34395  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
34396  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
34397  * @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
34398  * @cfg {Boolean}   closable      True if the panel can be closed/removed
34399  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
34400  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
34401  * @cfg {Toolbar}   toolbar       A toolbar for this panel
34402  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
34403  * @cfg {String} title          The title for this panel
34404  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
34405  * @cfg {String} url            Calls {@link #setUrl} with this value
34406  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
34407  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
34408  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
34409  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
34410  * @cfg {Boolean} badges render the badges
34411
34412  * @constructor
34413  * Create a new ContentPanel.
34414  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
34415  * @param {String/Object} config A string to set only the title or a config object
34416  * @param {String} content (optional) Set the HTML content for this panel
34417  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
34418  */
34419 Roo.bootstrap.panel.Content = function( config){
34420     
34421     this.tpl = config.tpl || false;
34422     
34423     var el = config.el;
34424     var content = config.content;
34425
34426     if(config.autoCreate){ // xtype is available if this is called from factory
34427         el = Roo.id();
34428     }
34429     this.el = Roo.get(el);
34430     if(!this.el && config && config.autoCreate){
34431         if(typeof config.autoCreate == "object"){
34432             if(!config.autoCreate.id){
34433                 config.autoCreate.id = config.id||el;
34434             }
34435             this.el = Roo.DomHelper.append(document.body,
34436                         config.autoCreate, true);
34437         }else{
34438             var elcfg =  {   tag: "div",
34439                             cls: "roo-layout-inactive-content",
34440                             id: config.id||el
34441                             };
34442             if (config.html) {
34443                 elcfg.html = config.html;
34444                 
34445             }
34446                         
34447             this.el = Roo.DomHelper.append(document.body, elcfg , true);
34448         }
34449     } 
34450     this.closable = false;
34451     this.loaded = false;
34452     this.active = false;
34453    
34454       
34455     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
34456         
34457         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
34458         
34459         this.wrapEl = this.el; //this.el.wrap();
34460         var ti = [];
34461         if (config.toolbar.items) {
34462             ti = config.toolbar.items ;
34463             delete config.toolbar.items ;
34464         }
34465         
34466         var nitems = [];
34467         this.toolbar.render(this.wrapEl, 'before');
34468         for(var i =0;i < ti.length;i++) {
34469           //  Roo.log(['add child', items[i]]);
34470             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34471         }
34472         this.toolbar.items = nitems;
34473         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
34474         delete config.toolbar;
34475         
34476     }
34477     /*
34478     // xtype created footer. - not sure if will work as we normally have to render first..
34479     if (this.footer && !this.footer.el && this.footer.xtype) {
34480         if (!this.wrapEl) {
34481             this.wrapEl = this.el.wrap();
34482         }
34483     
34484         this.footer.container = this.wrapEl.createChild();
34485          
34486         this.footer = Roo.factory(this.footer, Roo);
34487         
34488     }
34489     */
34490     
34491      if(typeof config == "string"){
34492         this.title = config;
34493     }else{
34494         Roo.apply(this, config);
34495     }
34496     
34497     if(this.resizeEl){
34498         this.resizeEl = Roo.get(this.resizeEl, true);
34499     }else{
34500         this.resizeEl = this.el;
34501     }
34502     // handle view.xtype
34503     
34504  
34505     
34506     
34507     this.addEvents({
34508         /**
34509          * @event activate
34510          * Fires when this panel is activated. 
34511          * @param {Roo.ContentPanel} this
34512          */
34513         "activate" : true,
34514         /**
34515          * @event deactivate
34516          * Fires when this panel is activated. 
34517          * @param {Roo.ContentPanel} this
34518          */
34519         "deactivate" : true,
34520
34521         /**
34522          * @event resize
34523          * Fires when this panel is resized if fitToFrame is true.
34524          * @param {Roo.ContentPanel} this
34525          * @param {Number} width The width after any component adjustments
34526          * @param {Number} height The height after any component adjustments
34527          */
34528         "resize" : true,
34529         
34530          /**
34531          * @event render
34532          * Fires when this tab is created
34533          * @param {Roo.ContentPanel} this
34534          */
34535         "render" : true
34536         
34537         
34538         
34539     });
34540     
34541
34542     
34543     
34544     if(this.autoScroll){
34545         this.resizeEl.setStyle("overflow", "auto");
34546     } else {
34547         // fix randome scrolling
34548         //this.el.on('scroll', function() {
34549         //    Roo.log('fix random scolling');
34550         //    this.scrollTo('top',0); 
34551         //});
34552     }
34553     content = content || this.content;
34554     if(content){
34555         this.setContent(content);
34556     }
34557     if(config && config.url){
34558         this.setUrl(this.url, this.params, this.loadOnce);
34559     }
34560     
34561     
34562     
34563     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
34564     
34565     if (this.view && typeof(this.view.xtype) != 'undefined') {
34566         this.view.el = this.el.appendChild(document.createElement("div"));
34567         this.view = Roo.factory(this.view); 
34568         this.view.render  &&  this.view.render(false, '');  
34569     }
34570     
34571     
34572     this.fireEvent('render', this);
34573 };
34574
34575 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
34576     
34577     tabTip : '',
34578     
34579     setRegion : function(region){
34580         this.region = region;
34581         this.setActiveClass(region && !this.background);
34582     },
34583     
34584     
34585     setActiveClass: function(state)
34586     {
34587         if(state){
34588            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
34589            this.el.setStyle('position','relative');
34590         }else{
34591            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
34592            this.el.setStyle('position', 'absolute');
34593         } 
34594     },
34595     
34596     /**
34597      * Returns the toolbar for this Panel if one was configured. 
34598      * @return {Roo.Toolbar} 
34599      */
34600     getToolbar : function(){
34601         return this.toolbar;
34602     },
34603     
34604     setActiveState : function(active)
34605     {
34606         this.active = active;
34607         this.setActiveClass(active);
34608         if(!active){
34609             this.fireEvent("deactivate", this);
34610         }else{
34611             this.fireEvent("activate", this);
34612         }
34613     },
34614     /**
34615      * Updates this panel's element
34616      * @param {String} content The new content
34617      * @param {Boolean} loadScripts (optional) true to look for and process scripts
34618     */
34619     setContent : function(content, loadScripts){
34620         this.el.update(content, loadScripts);
34621     },
34622
34623     ignoreResize : function(w, h){
34624         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
34625             return true;
34626         }else{
34627             this.lastSize = {width: w, height: h};
34628             return false;
34629         }
34630     },
34631     /**
34632      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
34633      * @return {Roo.UpdateManager} The UpdateManager
34634      */
34635     getUpdateManager : function(){
34636         return this.el.getUpdateManager();
34637     },
34638      /**
34639      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
34640      * @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:
34641 <pre><code>
34642 panel.load({
34643     url: "your-url.php",
34644     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
34645     callback: yourFunction,
34646     scope: yourObject, //(optional scope)
34647     discardUrl: false,
34648     nocache: false,
34649     text: "Loading...",
34650     timeout: 30,
34651     scripts: false
34652 });
34653 </code></pre>
34654      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
34655      * 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.
34656      * @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}
34657      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
34658      * @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.
34659      * @return {Roo.ContentPanel} this
34660      */
34661     load : function(){
34662         var um = this.el.getUpdateManager();
34663         um.update.apply(um, arguments);
34664         return this;
34665     },
34666
34667
34668     /**
34669      * 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.
34670      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
34671      * @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)
34672      * @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)
34673      * @return {Roo.UpdateManager} The UpdateManager
34674      */
34675     setUrl : function(url, params, loadOnce){
34676         if(this.refreshDelegate){
34677             this.removeListener("activate", this.refreshDelegate);
34678         }
34679         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34680         this.on("activate", this.refreshDelegate);
34681         return this.el.getUpdateManager();
34682     },
34683     
34684     _handleRefresh : function(url, params, loadOnce){
34685         if(!loadOnce || !this.loaded){
34686             var updater = this.el.getUpdateManager();
34687             updater.update(url, params, this._setLoaded.createDelegate(this));
34688         }
34689     },
34690     
34691     _setLoaded : function(){
34692         this.loaded = true;
34693     }, 
34694     
34695     /**
34696      * Returns this panel's id
34697      * @return {String} 
34698      */
34699     getId : function(){
34700         return this.el.id;
34701     },
34702     
34703     /** 
34704      * Returns this panel's element - used by regiosn to add.
34705      * @return {Roo.Element} 
34706      */
34707     getEl : function(){
34708         return this.wrapEl || this.el;
34709     },
34710     
34711    
34712     
34713     adjustForComponents : function(width, height)
34714     {
34715         //Roo.log('adjustForComponents ');
34716         if(this.resizeEl != this.el){
34717             width -= this.el.getFrameWidth('lr');
34718             height -= this.el.getFrameWidth('tb');
34719         }
34720         if(this.toolbar){
34721             var te = this.toolbar.getEl();
34722             height -= te.getHeight();
34723             te.setWidth(width);
34724         }
34725         if(this.footer){
34726             var te = this.footer.getEl();
34727             Roo.log("footer:" + te.getHeight());
34728             
34729             height -= te.getHeight();
34730             te.setWidth(width);
34731         }
34732         
34733         
34734         if(this.adjustments){
34735             width += this.adjustments[0];
34736             height += this.adjustments[1];
34737         }
34738         return {"width": width, "height": height};
34739     },
34740     
34741     setSize : function(width, height){
34742         if(this.fitToFrame && !this.ignoreResize(width, height)){
34743             if(this.fitContainer && this.resizeEl != this.el){
34744                 this.el.setSize(width, height);
34745             }
34746             var size = this.adjustForComponents(width, height);
34747             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
34748             this.fireEvent('resize', this, size.width, size.height);
34749         }
34750     },
34751     
34752     /**
34753      * Returns this panel's title
34754      * @return {String} 
34755      */
34756     getTitle : function(){
34757         return this.title;
34758     },
34759     
34760     /**
34761      * Set this panel's title
34762      * @param {String} title
34763      */
34764     setTitle : function(title){
34765         this.title = title;
34766         if(this.region){
34767             this.region.updatePanelTitle(this, title);
34768         }
34769     },
34770     
34771     /**
34772      * Returns true is this panel was configured to be closable
34773      * @return {Boolean} 
34774      */
34775     isClosable : function(){
34776         return this.closable;
34777     },
34778     
34779     beforeSlide : function(){
34780         this.el.clip();
34781         this.resizeEl.clip();
34782     },
34783     
34784     afterSlide : function(){
34785         this.el.unclip();
34786         this.resizeEl.unclip();
34787     },
34788     
34789     /**
34790      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
34791      *   Will fail silently if the {@link #setUrl} method has not been called.
34792      *   This does not activate the panel, just updates its content.
34793      */
34794     refresh : function(){
34795         if(this.refreshDelegate){
34796            this.loaded = false;
34797            this.refreshDelegate();
34798         }
34799     },
34800     
34801     /**
34802      * Destroys this panel
34803      */
34804     destroy : function(){
34805         this.el.removeAllListeners();
34806         var tempEl = document.createElement("span");
34807         tempEl.appendChild(this.el.dom);
34808         tempEl.innerHTML = "";
34809         this.el.remove();
34810         this.el = null;
34811     },
34812     
34813     /**
34814      * form - if the content panel contains a form - this is a reference to it.
34815      * @type {Roo.form.Form}
34816      */
34817     form : false,
34818     /**
34819      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
34820      *    This contains a reference to it.
34821      * @type {Roo.View}
34822      */
34823     view : false,
34824     
34825       /**
34826      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
34827      * <pre><code>
34828
34829 layout.addxtype({
34830        xtype : 'Form',
34831        items: [ .... ]
34832    }
34833 );
34834
34835 </code></pre>
34836      * @param {Object} cfg Xtype definition of item to add.
34837      */
34838     
34839     
34840     getChildContainer: function () {
34841         return this.getEl();
34842     }
34843     
34844     
34845     /*
34846         var  ret = new Roo.factory(cfg);
34847         return ret;
34848         
34849         
34850         // add form..
34851         if (cfg.xtype.match(/^Form$/)) {
34852             
34853             var el;
34854             //if (this.footer) {
34855             //    el = this.footer.container.insertSibling(false, 'before');
34856             //} else {
34857                 el = this.el.createChild();
34858             //}
34859
34860             this.form = new  Roo.form.Form(cfg);
34861             
34862             
34863             if ( this.form.allItems.length) {
34864                 this.form.render(el.dom);
34865             }
34866             return this.form;
34867         }
34868         // should only have one of theses..
34869         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
34870             // views.. should not be just added - used named prop 'view''
34871             
34872             cfg.el = this.el.appendChild(document.createElement("div"));
34873             // factory?
34874             
34875             var ret = new Roo.factory(cfg);
34876              
34877              ret.render && ret.render(false, ''); // render blank..
34878             this.view = ret;
34879             return ret;
34880         }
34881         return false;
34882     }
34883     \*/
34884 });
34885  
34886 /**
34887  * @class Roo.bootstrap.panel.Grid
34888  * @extends Roo.bootstrap.panel.Content
34889  * @constructor
34890  * Create a new GridPanel.
34891  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
34892  * @param {Object} config A the config object
34893   
34894  */
34895
34896
34897
34898 Roo.bootstrap.panel.Grid = function(config)
34899 {
34900     
34901       
34902     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
34903         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
34904
34905     config.el = this.wrapper;
34906     //this.el = this.wrapper;
34907     
34908       if (config.container) {
34909         // ctor'ed from a Border/panel.grid
34910         
34911         
34912         this.wrapper.setStyle("overflow", "hidden");
34913         this.wrapper.addClass('roo-grid-container');
34914
34915     }
34916     
34917     
34918     if(config.toolbar){
34919         var tool_el = this.wrapper.createChild();    
34920         this.toolbar = Roo.factory(config.toolbar);
34921         var ti = [];
34922         if (config.toolbar.items) {
34923             ti = config.toolbar.items ;
34924             delete config.toolbar.items ;
34925         }
34926         
34927         var nitems = [];
34928         this.toolbar.render(tool_el);
34929         for(var i =0;i < ti.length;i++) {
34930           //  Roo.log(['add child', items[i]]);
34931             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34932         }
34933         this.toolbar.items = nitems;
34934         
34935         delete config.toolbar;
34936     }
34937     
34938     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
34939     config.grid.scrollBody = true;;
34940     config.grid.monitorWindowResize = false; // turn off autosizing
34941     config.grid.autoHeight = false;
34942     config.grid.autoWidth = false;
34943     
34944     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
34945     
34946     if (config.background) {
34947         // render grid on panel activation (if panel background)
34948         this.on('activate', function(gp) {
34949             if (!gp.grid.rendered) {
34950                 gp.grid.render(this.wrapper);
34951                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
34952             }
34953         });
34954             
34955     } else {
34956         this.grid.render(this.wrapper);
34957         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
34958
34959     }
34960     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
34961     // ??? needed ??? config.el = this.wrapper;
34962     
34963     
34964     
34965   
34966     // xtype created footer. - not sure if will work as we normally have to render first..
34967     if (this.footer && !this.footer.el && this.footer.xtype) {
34968         
34969         var ctr = this.grid.getView().getFooterPanel(true);
34970         this.footer.dataSource = this.grid.dataSource;
34971         this.footer = Roo.factory(this.footer, Roo);
34972         this.footer.render(ctr);
34973         
34974     }
34975     
34976     
34977     
34978     
34979      
34980 };
34981
34982 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
34983     getId : function(){
34984         return this.grid.id;
34985     },
34986     
34987     /**
34988      * Returns the grid for this panel
34989      * @return {Roo.bootstrap.Table} 
34990      */
34991     getGrid : function(){
34992         return this.grid;    
34993     },
34994     
34995     setSize : function(width, height){
34996         if(!this.ignoreResize(width, height)){
34997             var grid = this.grid;
34998             var size = this.adjustForComponents(width, height);
34999             var gridel = grid.getGridEl();
35000             gridel.setSize(size.width, size.height);
35001             /*
35002             var thd = grid.getGridEl().select('thead',true).first();
35003             var tbd = grid.getGridEl().select('tbody', true).first();
35004             if (tbd) {
35005                 tbd.setSize(width, height - thd.getHeight());
35006             }
35007             */
35008             grid.autoSize();
35009         }
35010     },
35011      
35012     
35013     
35014     beforeSlide : function(){
35015         this.grid.getView().scroller.clip();
35016     },
35017     
35018     afterSlide : function(){
35019         this.grid.getView().scroller.unclip();
35020     },
35021     
35022     destroy : function(){
35023         this.grid.destroy();
35024         delete this.grid;
35025         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
35026     }
35027 });
35028
35029 /**
35030  * @class Roo.bootstrap.panel.Nest
35031  * @extends Roo.bootstrap.panel.Content
35032  * @constructor
35033  * Create a new Panel, that can contain a layout.Border.
35034  * 
35035  * 
35036  * @param {Roo.BorderLayout} layout The layout for this panel
35037  * @param {String/Object} config A string to set only the title or a config object
35038  */
35039 Roo.bootstrap.panel.Nest = function(config)
35040 {
35041     // construct with only one argument..
35042     /* FIXME - implement nicer consturctors
35043     if (layout.layout) {
35044         config = layout;
35045         layout = config.layout;
35046         delete config.layout;
35047     }
35048     if (layout.xtype && !layout.getEl) {
35049         // then layout needs constructing..
35050         layout = Roo.factory(layout, Roo);
35051     }
35052     */
35053     
35054     config.el =  config.layout.getEl();
35055     
35056     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
35057     
35058     config.layout.monitorWindowResize = false; // turn off autosizing
35059     this.layout = config.layout;
35060     this.layout.getEl().addClass("roo-layout-nested-layout");
35061     
35062     
35063     
35064     
35065 };
35066
35067 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
35068
35069     setSize : function(width, height){
35070         if(!this.ignoreResize(width, height)){
35071             var size = this.adjustForComponents(width, height);
35072             var el = this.layout.getEl();
35073             if (size.height < 1) {
35074                 el.setWidth(size.width);   
35075             } else {
35076                 el.setSize(size.width, size.height);
35077             }
35078             var touch = el.dom.offsetWidth;
35079             this.layout.layout();
35080             // ie requires a double layout on the first pass
35081             if(Roo.isIE && !this.initialized){
35082                 this.initialized = true;
35083                 this.layout.layout();
35084             }
35085         }
35086     },
35087     
35088     // activate all subpanels if not currently active..
35089     
35090     setActiveState : function(active){
35091         this.active = active;
35092         this.setActiveClass(active);
35093         
35094         if(!active){
35095             this.fireEvent("deactivate", this);
35096             return;
35097         }
35098         
35099         this.fireEvent("activate", this);
35100         // not sure if this should happen before or after..
35101         if (!this.layout) {
35102             return; // should not happen..
35103         }
35104         var reg = false;
35105         for (var r in this.layout.regions) {
35106             reg = this.layout.getRegion(r);
35107             if (reg.getActivePanel()) {
35108                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
35109                 reg.setActivePanel(reg.getActivePanel());
35110                 continue;
35111             }
35112             if (!reg.panels.length) {
35113                 continue;
35114             }
35115             reg.showPanel(reg.getPanel(0));
35116         }
35117         
35118         
35119         
35120         
35121     },
35122     
35123     /**
35124      * Returns the nested BorderLayout for this panel
35125      * @return {Roo.BorderLayout} 
35126      */
35127     getLayout : function(){
35128         return this.layout;
35129     },
35130     
35131      /**
35132      * Adds a xtype elements to the layout of the nested panel
35133      * <pre><code>
35134
35135 panel.addxtype({
35136        xtype : 'ContentPanel',
35137        region: 'west',
35138        items: [ .... ]
35139    }
35140 );
35141
35142 panel.addxtype({
35143         xtype : 'NestedLayoutPanel',
35144         region: 'west',
35145         layout: {
35146            center: { },
35147            west: { }   
35148         },
35149         items : [ ... list of content panels or nested layout panels.. ]
35150    }
35151 );
35152 </code></pre>
35153      * @param {Object} cfg Xtype definition of item to add.
35154      */
35155     addxtype : function(cfg) {
35156         return this.layout.addxtype(cfg);
35157     
35158     }
35159 });        /*
35160  * Based on:
35161  * Ext JS Library 1.1.1
35162  * Copyright(c) 2006-2007, Ext JS, LLC.
35163  *
35164  * Originally Released Under LGPL - original licence link has changed is not relivant.
35165  *
35166  * Fork - LGPL
35167  * <script type="text/javascript">
35168  */
35169 /**
35170  * @class Roo.TabPanel
35171  * @extends Roo.util.Observable
35172  * A lightweight tab container.
35173  * <br><br>
35174  * Usage:
35175  * <pre><code>
35176 // basic tabs 1, built from existing content
35177 var tabs = new Roo.TabPanel("tabs1");
35178 tabs.addTab("script", "View Script");
35179 tabs.addTab("markup", "View Markup");
35180 tabs.activate("script");
35181
35182 // more advanced tabs, built from javascript
35183 var jtabs = new Roo.TabPanel("jtabs");
35184 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
35185
35186 // set up the UpdateManager
35187 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
35188 var updater = tab2.getUpdateManager();
35189 updater.setDefaultUrl("ajax1.htm");
35190 tab2.on('activate', updater.refresh, updater, true);
35191
35192 // Use setUrl for Ajax loading
35193 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
35194 tab3.setUrl("ajax2.htm", null, true);
35195
35196 // Disabled tab
35197 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
35198 tab4.disable();
35199
35200 jtabs.activate("jtabs-1");
35201  * </code></pre>
35202  * @constructor
35203  * Create a new TabPanel.
35204  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
35205  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
35206  */
35207 Roo.bootstrap.panel.Tabs = function(config){
35208     /**
35209     * The container element for this TabPanel.
35210     * @type Roo.Element
35211     */
35212     this.el = Roo.get(config.el);
35213     delete config.el;
35214     if(config){
35215         if(typeof config == "boolean"){
35216             this.tabPosition = config ? "bottom" : "top";
35217         }else{
35218             Roo.apply(this, config);
35219         }
35220     }
35221     
35222     if(this.tabPosition == "bottom"){
35223         this.bodyEl = Roo.get(this.createBody(this.el.dom));
35224         this.el.addClass("roo-tabs-bottom");
35225     }
35226     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
35227     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
35228     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
35229     if(Roo.isIE){
35230         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
35231     }
35232     if(this.tabPosition != "bottom"){
35233         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
35234          * @type Roo.Element
35235          */
35236         this.bodyEl = Roo.get(this.createBody(this.el.dom));
35237         this.el.addClass("roo-tabs-top");
35238     }
35239     this.items = [];
35240
35241     this.bodyEl.setStyle("position", "relative");
35242
35243     this.active = null;
35244     this.activateDelegate = this.activate.createDelegate(this);
35245
35246     this.addEvents({
35247         /**
35248          * @event tabchange
35249          * Fires when the active tab changes
35250          * @param {Roo.TabPanel} this
35251          * @param {Roo.TabPanelItem} activePanel The new active tab
35252          */
35253         "tabchange": true,
35254         /**
35255          * @event beforetabchange
35256          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
35257          * @param {Roo.TabPanel} this
35258          * @param {Object} e Set cancel to true on this object to cancel the tab change
35259          * @param {Roo.TabPanelItem} tab The tab being changed to
35260          */
35261         "beforetabchange" : true
35262     });
35263
35264     Roo.EventManager.onWindowResize(this.onResize, this);
35265     this.cpad = this.el.getPadding("lr");
35266     this.hiddenCount = 0;
35267
35268
35269     // toolbar on the tabbar support...
35270     if (this.toolbar) {
35271         alert("no toolbar support yet");
35272         this.toolbar  = false;
35273         /*
35274         var tcfg = this.toolbar;
35275         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
35276         this.toolbar = new Roo.Toolbar(tcfg);
35277         if (Roo.isSafari) {
35278             var tbl = tcfg.container.child('table', true);
35279             tbl.setAttribute('width', '100%');
35280         }
35281         */
35282         
35283     }
35284    
35285
35286
35287     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
35288 };
35289
35290 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
35291     /*
35292      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
35293      */
35294     tabPosition : "top",
35295     /*
35296      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
35297      */
35298     currentTabWidth : 0,
35299     /*
35300      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
35301      */
35302     minTabWidth : 40,
35303     /*
35304      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
35305      */
35306     maxTabWidth : 250,
35307     /*
35308      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
35309      */
35310     preferredTabWidth : 175,
35311     /*
35312      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
35313      */
35314     resizeTabs : false,
35315     /*
35316      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
35317      */
35318     monitorResize : true,
35319     /*
35320      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
35321      */
35322     toolbar : false,
35323
35324     /**
35325      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
35326      * @param {String} id The id of the div to use <b>or create</b>
35327      * @param {String} text The text for the tab
35328      * @param {String} content (optional) Content to put in the TabPanelItem body
35329      * @param {Boolean} closable (optional) True to create a close icon on the tab
35330      * @return {Roo.TabPanelItem} The created TabPanelItem
35331      */
35332     addTab : function(id, text, content, closable, tpl)
35333     {
35334         var item = new Roo.bootstrap.panel.TabItem({
35335             panel: this,
35336             id : id,
35337             text : text,
35338             closable : closable,
35339             tpl : tpl
35340         });
35341         this.addTabItem(item);
35342         if(content){
35343             item.setContent(content);
35344         }
35345         return item;
35346     },
35347
35348     /**
35349      * Returns the {@link Roo.TabPanelItem} with the specified id/index
35350      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
35351      * @return {Roo.TabPanelItem}
35352      */
35353     getTab : function(id){
35354         return this.items[id];
35355     },
35356
35357     /**
35358      * Hides the {@link Roo.TabPanelItem} with the specified id/index
35359      * @param {String/Number} id The id or index of the TabPanelItem to hide.
35360      */
35361     hideTab : function(id){
35362         var t = this.items[id];
35363         if(!t.isHidden()){
35364            t.setHidden(true);
35365            this.hiddenCount++;
35366            this.autoSizeTabs();
35367         }
35368     },
35369
35370     /**
35371      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
35372      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
35373      */
35374     unhideTab : function(id){
35375         var t = this.items[id];
35376         if(t.isHidden()){
35377            t.setHidden(false);
35378            this.hiddenCount--;
35379            this.autoSizeTabs();
35380         }
35381     },
35382
35383     /**
35384      * Adds an existing {@link Roo.TabPanelItem}.
35385      * @param {Roo.TabPanelItem} item The TabPanelItem to add
35386      */
35387     addTabItem : function(item){
35388         this.items[item.id] = item;
35389         this.items.push(item);
35390       //  if(this.resizeTabs){
35391     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
35392   //         this.autoSizeTabs();
35393 //        }else{
35394 //            item.autoSize();
35395        // }
35396     },
35397
35398     /**
35399      * Removes a {@link Roo.TabPanelItem}.
35400      * @param {String/Number} id The id or index of the TabPanelItem to remove.
35401      */
35402     removeTab : function(id){
35403         var items = this.items;
35404         var tab = items[id];
35405         if(!tab) { return; }
35406         var index = items.indexOf(tab);
35407         if(this.active == tab && items.length > 1){
35408             var newTab = this.getNextAvailable(index);
35409             if(newTab) {
35410                 newTab.activate();
35411             }
35412         }
35413         this.stripEl.dom.removeChild(tab.pnode.dom);
35414         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
35415             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
35416         }
35417         items.splice(index, 1);
35418         delete this.items[tab.id];
35419         tab.fireEvent("close", tab);
35420         tab.purgeListeners();
35421         this.autoSizeTabs();
35422     },
35423
35424     getNextAvailable : function(start){
35425         var items = this.items;
35426         var index = start;
35427         // look for a next tab that will slide over to
35428         // replace the one being removed
35429         while(index < items.length){
35430             var item = items[++index];
35431             if(item && !item.isHidden()){
35432                 return item;
35433             }
35434         }
35435         // if one isn't found select the previous tab (on the left)
35436         index = start;
35437         while(index >= 0){
35438             var item = items[--index];
35439             if(item && !item.isHidden()){
35440                 return item;
35441             }
35442         }
35443         return null;
35444     },
35445
35446     /**
35447      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
35448      * @param {String/Number} id The id or index of the TabPanelItem to disable.
35449      */
35450     disableTab : function(id){
35451         var tab = this.items[id];
35452         if(tab && this.active != tab){
35453             tab.disable();
35454         }
35455     },
35456
35457     /**
35458      * Enables a {@link Roo.TabPanelItem} that is disabled.
35459      * @param {String/Number} id The id or index of the TabPanelItem to enable.
35460      */
35461     enableTab : function(id){
35462         var tab = this.items[id];
35463         tab.enable();
35464     },
35465
35466     /**
35467      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
35468      * @param {String/Number} id The id or index of the TabPanelItem to activate.
35469      * @return {Roo.TabPanelItem} The TabPanelItem.
35470      */
35471     activate : function(id){
35472         var tab = this.items[id];
35473         if(!tab){
35474             return null;
35475         }
35476         if(tab == this.active || tab.disabled){
35477             return tab;
35478         }
35479         var e = {};
35480         this.fireEvent("beforetabchange", this, e, tab);
35481         if(e.cancel !== true && !tab.disabled){
35482             if(this.active){
35483                 this.active.hide();
35484             }
35485             this.active = this.items[id];
35486             this.active.show();
35487             this.fireEvent("tabchange", this, this.active);
35488         }
35489         return tab;
35490     },
35491
35492     /**
35493      * Gets the active {@link Roo.TabPanelItem}.
35494      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
35495      */
35496     getActiveTab : function(){
35497         return this.active;
35498     },
35499
35500     /**
35501      * Updates the tab body element to fit the height of the container element
35502      * for overflow scrolling
35503      * @param {Number} targetHeight (optional) Override the starting height from the elements height
35504      */
35505     syncHeight : function(targetHeight){
35506         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35507         var bm = this.bodyEl.getMargins();
35508         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
35509         this.bodyEl.setHeight(newHeight);
35510         return newHeight;
35511     },
35512
35513     onResize : function(){
35514         if(this.monitorResize){
35515             this.autoSizeTabs();
35516         }
35517     },
35518
35519     /**
35520      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
35521      */
35522     beginUpdate : function(){
35523         this.updating = true;
35524     },
35525
35526     /**
35527      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
35528      */
35529     endUpdate : function(){
35530         this.updating = false;
35531         this.autoSizeTabs();
35532     },
35533
35534     /**
35535      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
35536      */
35537     autoSizeTabs : function(){
35538         var count = this.items.length;
35539         var vcount = count - this.hiddenCount;
35540         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
35541             return;
35542         }
35543         var w = Math.max(this.el.getWidth() - this.cpad, 10);
35544         var availWidth = Math.floor(w / vcount);
35545         var b = this.stripBody;
35546         if(b.getWidth() > w){
35547             var tabs = this.items;
35548             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
35549             if(availWidth < this.minTabWidth){
35550                 /*if(!this.sleft){    // incomplete scrolling code
35551                     this.createScrollButtons();
35552                 }
35553                 this.showScroll();
35554                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
35555             }
35556         }else{
35557             if(this.currentTabWidth < this.preferredTabWidth){
35558                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
35559             }
35560         }
35561     },
35562
35563     /**
35564      * Returns the number of tabs in this TabPanel.
35565      * @return {Number}
35566      */
35567      getCount : function(){
35568          return this.items.length;
35569      },
35570
35571     /**
35572      * Resizes all the tabs to the passed width
35573      * @param {Number} The new width
35574      */
35575     setTabWidth : function(width){
35576         this.currentTabWidth = width;
35577         for(var i = 0, len = this.items.length; i < len; i++) {
35578                 if(!this.items[i].isHidden()) {
35579                 this.items[i].setWidth(width);
35580             }
35581         }
35582     },
35583
35584     /**
35585      * Destroys this TabPanel
35586      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
35587      */
35588     destroy : function(removeEl){
35589         Roo.EventManager.removeResizeListener(this.onResize, this);
35590         for(var i = 0, len = this.items.length; i < len; i++){
35591             this.items[i].purgeListeners();
35592         }
35593         if(removeEl === true){
35594             this.el.update("");
35595             this.el.remove();
35596         }
35597     },
35598     
35599     createStrip : function(container)
35600     {
35601         var strip = document.createElement("nav");
35602         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
35603         container.appendChild(strip);
35604         return strip;
35605     },
35606     
35607     createStripList : function(strip)
35608     {
35609         // div wrapper for retard IE
35610         // returns the "tr" element.
35611         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
35612         //'<div class="x-tabs-strip-wrap">'+
35613           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
35614           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
35615         return strip.firstChild; //.firstChild.firstChild.firstChild;
35616     },
35617     createBody : function(container)
35618     {
35619         var body = document.createElement("div");
35620         Roo.id(body, "tab-body");
35621         //Roo.fly(body).addClass("x-tabs-body");
35622         Roo.fly(body).addClass("tab-content");
35623         container.appendChild(body);
35624         return body;
35625     },
35626     createItemBody :function(bodyEl, id){
35627         var body = Roo.getDom(id);
35628         if(!body){
35629             body = document.createElement("div");
35630             body.id = id;
35631         }
35632         //Roo.fly(body).addClass("x-tabs-item-body");
35633         Roo.fly(body).addClass("tab-pane");
35634          bodyEl.insertBefore(body, bodyEl.firstChild);
35635         return body;
35636     },
35637     /** @private */
35638     createStripElements :  function(stripEl, text, closable, tpl)
35639     {
35640         var td = document.createElement("li"); // was td..
35641         
35642         
35643         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
35644         
35645         
35646         stripEl.appendChild(td);
35647         /*if(closable){
35648             td.className = "x-tabs-closable";
35649             if(!this.closeTpl){
35650                 this.closeTpl = new Roo.Template(
35651                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
35652                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
35653                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
35654                 );
35655             }
35656             var el = this.closeTpl.overwrite(td, {"text": text});
35657             var close = el.getElementsByTagName("div")[0];
35658             var inner = el.getElementsByTagName("em")[0];
35659             return {"el": el, "close": close, "inner": inner};
35660         } else {
35661         */
35662         // not sure what this is..
35663 //            if(!this.tabTpl){
35664                 //this.tabTpl = new Roo.Template(
35665                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
35666                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
35667                 //);
35668 //                this.tabTpl = new Roo.Template(
35669 //                   '<a href="#">' +
35670 //                   '<span unselectable="on"' +
35671 //                            (this.disableTooltips ? '' : ' title="{text}"') +
35672 //                            ' >{text}</span></a>'
35673 //                );
35674 //                
35675 //            }
35676
35677
35678             var template = tpl || this.tabTpl || false;
35679             
35680             if(!template){
35681                 
35682                 template = new Roo.Template(
35683                    '<a href="#">' +
35684                    '<span unselectable="on"' +
35685                             (this.disableTooltips ? '' : ' title="{text}"') +
35686                             ' >{text}</span></a>'
35687                 );
35688             }
35689             
35690             switch (typeof(template)) {
35691                 case 'object' :
35692                     break;
35693                 case 'string' :
35694                     template = new Roo.Template(template);
35695                     break;
35696                 default :
35697                     break;
35698             }
35699             
35700             var el = template.overwrite(td, {"text": text});
35701             
35702             var inner = el.getElementsByTagName("span")[0];
35703             
35704             return {"el": el, "inner": inner};
35705             
35706     }
35707         
35708     
35709 });
35710
35711 /**
35712  * @class Roo.TabPanelItem
35713  * @extends Roo.util.Observable
35714  * Represents an individual item (tab plus body) in a TabPanel.
35715  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
35716  * @param {String} id The id of this TabPanelItem
35717  * @param {String} text The text for the tab of this TabPanelItem
35718  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
35719  */
35720 Roo.bootstrap.panel.TabItem = function(config){
35721     /**
35722      * The {@link Roo.TabPanel} this TabPanelItem belongs to
35723      * @type Roo.TabPanel
35724      */
35725     this.tabPanel = config.panel;
35726     /**
35727      * The id for this TabPanelItem
35728      * @type String
35729      */
35730     this.id = config.id;
35731     /** @private */
35732     this.disabled = false;
35733     /** @private */
35734     this.text = config.text;
35735     /** @private */
35736     this.loaded = false;
35737     this.closable = config.closable;
35738
35739     /**
35740      * The body element for this TabPanelItem.
35741      * @type Roo.Element
35742      */
35743     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
35744     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
35745     this.bodyEl.setStyle("display", "block");
35746     this.bodyEl.setStyle("zoom", "1");
35747     //this.hideAction();
35748
35749     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
35750     /** @private */
35751     this.el = Roo.get(els.el);
35752     this.inner = Roo.get(els.inner, true);
35753     this.textEl = Roo.get(this.el.dom.firstChild, true);
35754     this.pnode = Roo.get(els.el.parentNode, true);
35755     this.el.on("mousedown", this.onTabMouseDown, this);
35756     this.el.on("click", this.onTabClick, this);
35757     /** @private */
35758     if(config.closable){
35759         var c = Roo.get(els.close, true);
35760         c.dom.title = this.closeText;
35761         c.addClassOnOver("close-over");
35762         c.on("click", this.closeClick, this);
35763      }
35764
35765     this.addEvents({
35766          /**
35767          * @event activate
35768          * Fires when this tab becomes the active tab.
35769          * @param {Roo.TabPanel} tabPanel The parent TabPanel
35770          * @param {Roo.TabPanelItem} this
35771          */
35772         "activate": true,
35773         /**
35774          * @event beforeclose
35775          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
35776          * @param {Roo.TabPanelItem} this
35777          * @param {Object} e Set cancel to true on this object to cancel the close.
35778          */
35779         "beforeclose": true,
35780         /**
35781          * @event close
35782          * Fires when this tab is closed.
35783          * @param {Roo.TabPanelItem} this
35784          */
35785          "close": true,
35786         /**
35787          * @event deactivate
35788          * Fires when this tab is no longer the active tab.
35789          * @param {Roo.TabPanel} tabPanel The parent TabPanel
35790          * @param {Roo.TabPanelItem} this
35791          */
35792          "deactivate" : true
35793     });
35794     this.hidden = false;
35795
35796     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
35797 };
35798
35799 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
35800            {
35801     purgeListeners : function(){
35802        Roo.util.Observable.prototype.purgeListeners.call(this);
35803        this.el.removeAllListeners();
35804     },
35805     /**
35806      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
35807      */
35808     show : function(){
35809         this.pnode.addClass("active");
35810         this.showAction();
35811         if(Roo.isOpera){
35812             this.tabPanel.stripWrap.repaint();
35813         }
35814         this.fireEvent("activate", this.tabPanel, this);
35815     },
35816
35817     /**
35818      * Returns true if this tab is the active tab.
35819      * @return {Boolean}
35820      */
35821     isActive : function(){
35822         return this.tabPanel.getActiveTab() == this;
35823     },
35824
35825     /**
35826      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
35827      */
35828     hide : function(){
35829         this.pnode.removeClass("active");
35830         this.hideAction();
35831         this.fireEvent("deactivate", this.tabPanel, this);
35832     },
35833
35834     hideAction : function(){
35835         this.bodyEl.hide();
35836         this.bodyEl.setStyle("position", "absolute");
35837         this.bodyEl.setLeft("-20000px");
35838         this.bodyEl.setTop("-20000px");
35839     },
35840
35841     showAction : function(){
35842         this.bodyEl.setStyle("position", "relative");
35843         this.bodyEl.setTop("");
35844         this.bodyEl.setLeft("");
35845         this.bodyEl.show();
35846     },
35847
35848     /**
35849      * Set the tooltip for the tab.
35850      * @param {String} tooltip The tab's tooltip
35851      */
35852     setTooltip : function(text){
35853         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
35854             this.textEl.dom.qtip = text;
35855             this.textEl.dom.removeAttribute('title');
35856         }else{
35857             this.textEl.dom.title = text;
35858         }
35859     },
35860
35861     onTabClick : function(e){
35862         e.preventDefault();
35863         this.tabPanel.activate(this.id);
35864     },
35865
35866     onTabMouseDown : function(e){
35867         e.preventDefault();
35868         this.tabPanel.activate(this.id);
35869     },
35870 /*
35871     getWidth : function(){
35872         return this.inner.getWidth();
35873     },
35874
35875     setWidth : function(width){
35876         var iwidth = width - this.pnode.getPadding("lr");
35877         this.inner.setWidth(iwidth);
35878         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
35879         this.pnode.setWidth(width);
35880     },
35881 */
35882     /**
35883      * Show or hide the tab
35884      * @param {Boolean} hidden True to hide or false to show.
35885      */
35886     setHidden : function(hidden){
35887         this.hidden = hidden;
35888         this.pnode.setStyle("display", hidden ? "none" : "");
35889     },
35890
35891     /**
35892      * Returns true if this tab is "hidden"
35893      * @return {Boolean}
35894      */
35895     isHidden : function(){
35896         return this.hidden;
35897     },
35898
35899     /**
35900      * Returns the text for this tab
35901      * @return {String}
35902      */
35903     getText : function(){
35904         return this.text;
35905     },
35906     /*
35907     autoSize : function(){
35908         //this.el.beginMeasure();
35909         this.textEl.setWidth(1);
35910         /*
35911          *  #2804 [new] Tabs in Roojs
35912          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
35913          */
35914         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
35915         //this.el.endMeasure();
35916     //},
35917
35918     /**
35919      * Sets the text for the tab (Note: this also sets the tooltip text)
35920      * @param {String} text The tab's text and tooltip
35921      */
35922     setText : function(text){
35923         this.text = text;
35924         this.textEl.update(text);
35925         this.setTooltip(text);
35926         //if(!this.tabPanel.resizeTabs){
35927         //    this.autoSize();
35928         //}
35929     },
35930     /**
35931      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
35932      */
35933     activate : function(){
35934         this.tabPanel.activate(this.id);
35935     },
35936
35937     /**
35938      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
35939      */
35940     disable : function(){
35941         if(this.tabPanel.active != this){
35942             this.disabled = true;
35943             this.pnode.addClass("disabled");
35944         }
35945     },
35946
35947     /**
35948      * Enables this TabPanelItem if it was previously disabled.
35949      */
35950     enable : function(){
35951         this.disabled = false;
35952         this.pnode.removeClass("disabled");
35953     },
35954
35955     /**
35956      * Sets the content for this TabPanelItem.
35957      * @param {String} content The content
35958      * @param {Boolean} loadScripts true to look for and load scripts
35959      */
35960     setContent : function(content, loadScripts){
35961         this.bodyEl.update(content, loadScripts);
35962     },
35963
35964     /**
35965      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
35966      * @return {Roo.UpdateManager} The UpdateManager
35967      */
35968     getUpdateManager : function(){
35969         return this.bodyEl.getUpdateManager();
35970     },
35971
35972     /**
35973      * Set a URL to be used to load the content for this TabPanelItem.
35974      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
35975      * @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)
35976      * @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)
35977      * @return {Roo.UpdateManager} The UpdateManager
35978      */
35979     setUrl : function(url, params, loadOnce){
35980         if(this.refreshDelegate){
35981             this.un('activate', this.refreshDelegate);
35982         }
35983         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35984         this.on("activate", this.refreshDelegate);
35985         return this.bodyEl.getUpdateManager();
35986     },
35987
35988     /** @private */
35989     _handleRefresh : function(url, params, loadOnce){
35990         if(!loadOnce || !this.loaded){
35991             var updater = this.bodyEl.getUpdateManager();
35992             updater.update(url, params, this._setLoaded.createDelegate(this));
35993         }
35994     },
35995
35996     /**
35997      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
35998      *   Will fail silently if the setUrl method has not been called.
35999      *   This does not activate the panel, just updates its content.
36000      */
36001     refresh : function(){
36002         if(this.refreshDelegate){
36003            this.loaded = false;
36004            this.refreshDelegate();
36005         }
36006     },
36007
36008     /** @private */
36009     _setLoaded : function(){
36010         this.loaded = true;
36011     },
36012
36013     /** @private */
36014     closeClick : function(e){
36015         var o = {};
36016         e.stopEvent();
36017         this.fireEvent("beforeclose", this, o);
36018         if(o.cancel !== true){
36019             this.tabPanel.removeTab(this.id);
36020         }
36021     },
36022     /**
36023      * The text displayed in the tooltip for the close icon.
36024      * @type String
36025      */
36026     closeText : "Close this tab"
36027 });