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         if (this.triggerEl) {
14261             this.triggerEl.on("click", this.showTouchView, this);
14262         }
14263         
14264         
14265         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14266         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14267         
14268         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14269         
14270         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14271         this.store.on('load', this.onTouchViewLoad, this);
14272         this.store.on('loadexception', this.onTouchViewLoadException, this);
14273         
14274         if(this.hiddenName){
14275             
14276             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14277             
14278             this.hiddenField.dom.value =
14279                 this.hiddenValue !== undefined ? this.hiddenValue :
14280                 this.value !== undefined ? this.value : '';
14281         
14282             this.el.dom.removeAttribute('name');
14283             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14284         }
14285         
14286         if(this.multiple){
14287             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14288             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14289         }
14290         
14291         if(this.removable && !this.multiple){
14292             var close = this.closeTriggerEl();
14293             if(close){
14294                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14295                 close.on('click', this.removeBtnClick, this, close);
14296             }
14297         }
14298         /*
14299          * fix the bug in Safari iOS8
14300          */
14301         this.inputEl().on("focus", function(e){
14302             document.activeElement.blur();
14303         }, this);
14304         
14305         return;
14306         
14307         
14308     },
14309     
14310     renderTouchView : function()
14311     {
14312         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14313         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14314         
14315         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14316         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14317         
14318         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14319         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14320         this.touchViewBodyEl.setStyle('overflow', 'auto');
14321         
14322         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14323         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14324         
14325         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14326         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14327         
14328     },
14329     
14330     showTouchView : function()
14331     {
14332         if(this.disabled){
14333             return;
14334         }
14335         
14336         this.touchViewHeaderEl.hide();
14337
14338         if(this.modalTitle.length){
14339             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14340             this.touchViewHeaderEl.show();
14341         }
14342
14343         this.touchViewEl.show();
14344
14345         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14346         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14347                 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14348
14349         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14350
14351         if(this.modalTitle.length){
14352             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14353         }
14354         
14355         this.touchViewBodyEl.setHeight(bodyHeight);
14356
14357         if(this.animate){
14358             var _this = this;
14359             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14360         }else{
14361             this.touchViewEl.addClass('in');
14362         }
14363
14364         this.doTouchViewQuery();
14365         
14366     },
14367     
14368     hideTouchView : function()
14369     {
14370         this.touchViewEl.removeClass('in');
14371
14372         if(this.animate){
14373             var _this = this;
14374             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14375         }else{
14376             this.touchViewEl.setStyle('display', 'none');
14377         }
14378         
14379     },
14380     
14381     setTouchViewValue : function()
14382     {
14383         if(this.multiple){
14384             this.clearItem();
14385         
14386             var _this = this;
14387
14388             Roo.each(this.tickItems, function(o){
14389                 this.addItem(o);
14390             }, this);
14391         }
14392         
14393         this.hideTouchView();
14394     },
14395     
14396     doTouchViewQuery : function()
14397     {
14398         var qe = {
14399             query: '',
14400             forceAll: true,
14401             combo: this,
14402             cancel:false
14403         };
14404         
14405         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14406             return false;
14407         }
14408         
14409         if(!this.alwaysQuery || this.mode == 'local'){
14410             this.onTouchViewLoad();
14411             return;
14412         }
14413         
14414         this.store.load();
14415     },
14416     
14417     onTouchViewBeforeLoad : function(combo,opts)
14418     {
14419         return;
14420     },
14421
14422     // private
14423     onTouchViewLoad : function()
14424     {
14425         if(this.store.getCount() < 1){
14426             this.onTouchViewEmptyResults();
14427             return;
14428         }
14429         
14430         this.clearTouchView();
14431         
14432         var rawValue = this.getRawValue();
14433         
14434         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14435         
14436         this.tickItems = [];
14437         
14438         this.store.data.each(function(d, rowIndex){
14439             var row = this.touchViewListGroup.createChild(template);
14440             
14441             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14442                 row.addClass(d.data.cls);
14443             }
14444             
14445             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14446                 var cfg = {
14447                     data : d.data,
14448                     html : d.data[this.displayField]
14449                 };
14450                 
14451                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14452                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14453                 }
14454             }
14455             row.removeClass('selected');
14456             if(!this.multiple && this.valueField &&
14457                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14458             {
14459                 // radio buttons..
14460                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14461                 row.addClass('selected');
14462             }
14463             
14464             if(this.multiple && this.valueField &&
14465                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14466             {
14467                 
14468                 // checkboxes...
14469                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14470                 this.tickItems.push(d.data);
14471             }
14472             
14473             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14474             
14475         }, this);
14476         
14477         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14478         
14479         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14480
14481         if(this.modalTitle.length){
14482             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14483         }
14484
14485         var listHeight = this.touchViewListGroup.getHeight();
14486         
14487         var _this = this;
14488         
14489         if(firstChecked && listHeight > bodyHeight){
14490             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14491         }
14492         
14493     },
14494     
14495     onTouchViewLoadException : function()
14496     {
14497         this.hideTouchView();
14498     },
14499     
14500     onTouchViewEmptyResults : function()
14501     {
14502         this.clearTouchView();
14503         
14504         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14505         
14506         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14507         
14508     },
14509     
14510     clearTouchView : function()
14511     {
14512         this.touchViewListGroup.dom.innerHTML = '';
14513     },
14514     
14515     onTouchViewClick : function(e, el, o)
14516     {
14517         e.preventDefault();
14518         
14519         var row = o.row;
14520         var rowIndex = o.rowIndex;
14521         
14522         var r = this.store.getAt(rowIndex);
14523         
14524         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14525             
14526             if(!this.multiple){
14527                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14528                     c.dom.removeAttribute('checked');
14529                 }, this);
14530
14531                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14532
14533                 this.setFromData(r.data);
14534
14535                 var close = this.closeTriggerEl();
14536
14537                 if(close){
14538                     close.show();
14539                 }
14540
14541                 this.hideTouchView();
14542
14543                 this.fireEvent('select', this, r, rowIndex);
14544
14545                 return;
14546             }
14547
14548             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14549                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14550                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14551                 return;
14552             }
14553
14554             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14555             this.addItem(r.data);
14556             this.tickItems.push(r.data);
14557         }
14558     },
14559     
14560     getAutoCreateNativeIOS : function()
14561     {
14562         var cfg = {
14563             cls: 'form-group' //input-group,
14564         };
14565         
14566         var combobox =  {
14567             tag: 'select',
14568             cls : 'roo-ios-select'
14569         };
14570         
14571         if (this.name) {
14572             combobox.name = this.name;
14573         }
14574         
14575         if (this.disabled) {
14576             combobox.disabled = true;
14577         }
14578         
14579         var settings = this;
14580         
14581         ['xs','sm','md','lg'].map(function(size){
14582             if (settings[size]) {
14583                 cfg.cls += ' col-' + size + '-' + settings[size];
14584             }
14585         });
14586         
14587         cfg.cn = combobox;
14588         
14589         return cfg;
14590         
14591     },
14592     
14593     initIOSView : function()
14594     {
14595         this.store.on('load', this.onIOSViewLoad, this);
14596         
14597         return;
14598     },
14599     
14600     onIOSViewLoad : function()
14601     {
14602         if(this.store.getCount() < 1){
14603             return;
14604         }
14605         
14606         this.clearIOSView();
14607         
14608         if(this.allowBlank) {
14609             
14610             var default_text = '-- SELECT --';
14611             
14612             var opt = this.inputEl().createChild({
14613                 tag: 'option',
14614                 value : 0,
14615                 html : default_text
14616             });
14617             
14618             var o = {};
14619             o[this.valueField] = 0;
14620             o[this.displayField] = default_text;
14621             
14622             this.ios_options.push({
14623                 data : o,
14624                 el : opt
14625             });
14626             
14627         }
14628         
14629         this.store.data.each(function(d, rowIndex){
14630             
14631             var html = '';
14632             
14633             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14634                 html = d.data[this.displayField];
14635             }
14636             
14637             var value = '';
14638             
14639             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
14640                 value = d.data[this.valueField];
14641             }
14642             
14643             var option = {
14644                 tag: 'option',
14645                 value : value,
14646                 html : html
14647             };
14648             
14649             if(this.value == d.data[this.valueField]){
14650                 option['selected'] = true;
14651             }
14652             
14653             var opt = this.inputEl().createChild(option);
14654             
14655             this.ios_options.push({
14656                 data : d.data,
14657                 el : opt
14658             });
14659             
14660         }, this);
14661         
14662         this.inputEl().on('change', function(){
14663            this.fireEvent('select', this);
14664         }, this);
14665         
14666     },
14667     
14668     clearIOSView: function()
14669     {
14670         this.inputEl().dom.innerHTML = '';
14671         
14672         this.ios_options = [];
14673     },
14674     
14675     setIOSValue: function(v)
14676     {
14677         this.value = v;
14678         
14679         if(!this.ios_options){
14680             return;
14681         }
14682         
14683         Roo.each(this.ios_options, function(opts){
14684            
14685            opts.el.dom.removeAttribute('selected');
14686            
14687            if(opts.data[this.valueField] != v){
14688                return;
14689            }
14690            
14691            opts.el.dom.setAttribute('selected', true);
14692            
14693         }, this);
14694     }
14695
14696     /** 
14697     * @cfg {Boolean} grow 
14698     * @hide 
14699     */
14700     /** 
14701     * @cfg {Number} growMin 
14702     * @hide 
14703     */
14704     /** 
14705     * @cfg {Number} growMax 
14706     * @hide 
14707     */
14708     /**
14709      * @hide
14710      * @method autoSize
14711      */
14712 });
14713
14714 Roo.apply(Roo.bootstrap.ComboBox,  {
14715     
14716     header : {
14717         tag: 'div',
14718         cls: 'modal-header',
14719         cn: [
14720             {
14721                 tag: 'h4',
14722                 cls: 'modal-title'
14723             }
14724         ]
14725     },
14726     
14727     body : {
14728         tag: 'div',
14729         cls: 'modal-body',
14730         cn: [
14731             {
14732                 tag: 'ul',
14733                 cls: 'list-group'
14734             }
14735         ]
14736     },
14737     
14738     listItemRadio : {
14739         tag: 'li',
14740         cls: 'list-group-item',
14741         cn: [
14742             {
14743                 tag: 'span',
14744                 cls: 'roo-combobox-list-group-item-value'
14745             },
14746             {
14747                 tag: 'div',
14748                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14749                 cn: [
14750                     {
14751                         tag: 'input',
14752                         type: 'radio'
14753                     },
14754                     {
14755                         tag: 'label'
14756                     }
14757                 ]
14758             }
14759         ]
14760     },
14761     
14762     listItemCheckbox : {
14763         tag: 'li',
14764         cls: 'list-group-item',
14765         cn: [
14766             {
14767                 tag: 'span',
14768                 cls: 'roo-combobox-list-group-item-value'
14769             },
14770             {
14771                 tag: 'div',
14772                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14773                 cn: [
14774                     {
14775                         tag: 'input',
14776                         type: 'checkbox'
14777                     },
14778                     {
14779                         tag: 'label'
14780                     }
14781                 ]
14782             }
14783         ]
14784     },
14785     
14786     emptyResult : {
14787         tag: 'div',
14788         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14789     },
14790     
14791     footer : {
14792         tag: 'div',
14793         cls: 'modal-footer',
14794         cn: [
14795             {
14796                 tag: 'div',
14797                 cls: 'row',
14798                 cn: [
14799                     {
14800                         tag: 'div',
14801                         cls: 'col-xs-6 text-left',
14802                         cn: {
14803                             tag: 'button',
14804                             cls: 'btn btn-danger roo-touch-view-cancel',
14805                             html: 'Cancel'
14806                         }
14807                     },
14808                     {
14809                         tag: 'div',
14810                         cls: 'col-xs-6 text-right',
14811                         cn: {
14812                             tag: 'button',
14813                             cls: 'btn btn-success roo-touch-view-ok',
14814                             html: 'OK'
14815                         }
14816                     }
14817                 ]
14818             }
14819         ]
14820         
14821     }
14822 });
14823
14824 Roo.apply(Roo.bootstrap.ComboBox,  {
14825     
14826     touchViewTemplate : {
14827         tag: 'div',
14828         cls: 'modal fade roo-combobox-touch-view',
14829         cn: [
14830             {
14831                 tag: 'div',
14832                 cls: 'modal-dialog',
14833                 style : 'position:fixed', // we have to fix position....
14834                 cn: [
14835                     {
14836                         tag: 'div',
14837                         cls: 'modal-content',
14838                         cn: [
14839                             Roo.bootstrap.ComboBox.header,
14840                             Roo.bootstrap.ComboBox.body,
14841                             Roo.bootstrap.ComboBox.footer
14842                         ]
14843                     }
14844                 ]
14845             }
14846         ]
14847     }
14848 });/*
14849  * Based on:
14850  * Ext JS Library 1.1.1
14851  * Copyright(c) 2006-2007, Ext JS, LLC.
14852  *
14853  * Originally Released Under LGPL - original licence link has changed is not relivant.
14854  *
14855  * Fork - LGPL
14856  * <script type="text/javascript">
14857  */
14858
14859 /**
14860  * @class Roo.View
14861  * @extends Roo.util.Observable
14862  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
14863  * This class also supports single and multi selection modes. <br>
14864  * Create a data model bound view:
14865  <pre><code>
14866  var store = new Roo.data.Store(...);
14867
14868  var view = new Roo.View({
14869     el : "my-element",
14870     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
14871  
14872     singleSelect: true,
14873     selectedClass: "ydataview-selected",
14874     store: store
14875  });
14876
14877  // listen for node click?
14878  view.on("click", function(vw, index, node, e){
14879  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14880  });
14881
14882  // load XML data
14883  dataModel.load("foobar.xml");
14884  </code></pre>
14885  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14886  * <br><br>
14887  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14888  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14889  * 
14890  * Note: old style constructor is still suported (container, template, config)
14891  * 
14892  * @constructor
14893  * Create a new View
14894  * @param {Object} config The config object
14895  * 
14896  */
14897 Roo.View = function(config, depreciated_tpl, depreciated_config){
14898     
14899     this.parent = false;
14900     
14901     if (typeof(depreciated_tpl) == 'undefined') {
14902         // new way.. - universal constructor.
14903         Roo.apply(this, config);
14904         this.el  = Roo.get(this.el);
14905     } else {
14906         // old format..
14907         this.el  = Roo.get(config);
14908         this.tpl = depreciated_tpl;
14909         Roo.apply(this, depreciated_config);
14910     }
14911     this.wrapEl  = this.el.wrap().wrap();
14912     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14913     
14914     
14915     if(typeof(this.tpl) == "string"){
14916         this.tpl = new Roo.Template(this.tpl);
14917     } else {
14918         // support xtype ctors..
14919         this.tpl = new Roo.factory(this.tpl, Roo);
14920     }
14921     
14922     
14923     this.tpl.compile();
14924     
14925     /** @private */
14926     this.addEvents({
14927         /**
14928          * @event beforeclick
14929          * Fires before a click is processed. Returns false to cancel the default action.
14930          * @param {Roo.View} this
14931          * @param {Number} index The index of the target node
14932          * @param {HTMLElement} node The target node
14933          * @param {Roo.EventObject} e The raw event object
14934          */
14935             "beforeclick" : true,
14936         /**
14937          * @event click
14938          * Fires when a template node is clicked.
14939          * @param {Roo.View} this
14940          * @param {Number} index The index of the target node
14941          * @param {HTMLElement} node The target node
14942          * @param {Roo.EventObject} e The raw event object
14943          */
14944             "click" : true,
14945         /**
14946          * @event dblclick
14947          * Fires when a template node is double clicked.
14948          * @param {Roo.View} this
14949          * @param {Number} index The index of the target node
14950          * @param {HTMLElement} node The target node
14951          * @param {Roo.EventObject} e The raw event object
14952          */
14953             "dblclick" : true,
14954         /**
14955          * @event contextmenu
14956          * Fires when a template node is right clicked.
14957          * @param {Roo.View} this
14958          * @param {Number} index The index of the target node
14959          * @param {HTMLElement} node The target node
14960          * @param {Roo.EventObject} e The raw event object
14961          */
14962             "contextmenu" : true,
14963         /**
14964          * @event selectionchange
14965          * Fires when the selected nodes change.
14966          * @param {Roo.View} this
14967          * @param {Array} selections Array of the selected nodes
14968          */
14969             "selectionchange" : true,
14970     
14971         /**
14972          * @event beforeselect
14973          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14974          * @param {Roo.View} this
14975          * @param {HTMLElement} node The node to be selected
14976          * @param {Array} selections Array of currently selected nodes
14977          */
14978             "beforeselect" : true,
14979         /**
14980          * @event preparedata
14981          * Fires on every row to render, to allow you to change the data.
14982          * @param {Roo.View} this
14983          * @param {Object} data to be rendered (change this)
14984          */
14985           "preparedata" : true
14986           
14987           
14988         });
14989
14990
14991
14992     this.el.on({
14993         "click": this.onClick,
14994         "dblclick": this.onDblClick,
14995         "contextmenu": this.onContextMenu,
14996         scope:this
14997     });
14998
14999     this.selections = [];
15000     this.nodes = [];
15001     this.cmp = new Roo.CompositeElementLite([]);
15002     if(this.store){
15003         this.store = Roo.factory(this.store, Roo.data);
15004         this.setStore(this.store, true);
15005     }
15006     
15007     if ( this.footer && this.footer.xtype) {
15008            
15009          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15010         
15011         this.footer.dataSource = this.store;
15012         this.footer.container = fctr;
15013         this.footer = Roo.factory(this.footer, Roo);
15014         fctr.insertFirst(this.el);
15015         
15016         // this is a bit insane - as the paging toolbar seems to detach the el..
15017 //        dom.parentNode.parentNode.parentNode
15018          // they get detached?
15019     }
15020     
15021     
15022     Roo.View.superclass.constructor.call(this);
15023     
15024     
15025 };
15026
15027 Roo.extend(Roo.View, Roo.util.Observable, {
15028     
15029      /**
15030      * @cfg {Roo.data.Store} store Data store to load data from.
15031      */
15032     store : false,
15033     
15034     /**
15035      * @cfg {String|Roo.Element} el The container element.
15036      */
15037     el : '',
15038     
15039     /**
15040      * @cfg {String|Roo.Template} tpl The template used by this View 
15041      */
15042     tpl : false,
15043     /**
15044      * @cfg {String} dataName the named area of the template to use as the data area
15045      *                          Works with domtemplates roo-name="name"
15046      */
15047     dataName: false,
15048     /**
15049      * @cfg {String} selectedClass The css class to add to selected nodes
15050      */
15051     selectedClass : "x-view-selected",
15052      /**
15053      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15054      */
15055     emptyText : "",
15056     
15057     /**
15058      * @cfg {String} text to display on mask (default Loading)
15059      */
15060     mask : false,
15061     /**
15062      * @cfg {Boolean} multiSelect Allow multiple selection
15063      */
15064     multiSelect : false,
15065     /**
15066      * @cfg {Boolean} singleSelect Allow single selection
15067      */
15068     singleSelect:  false,
15069     
15070     /**
15071      * @cfg {Boolean} toggleSelect - selecting 
15072      */
15073     toggleSelect : false,
15074     
15075     /**
15076      * @cfg {Boolean} tickable - selecting 
15077      */
15078     tickable : false,
15079     
15080     /**
15081      * Returns the element this view is bound to.
15082      * @return {Roo.Element}
15083      */
15084     getEl : function(){
15085         return this.wrapEl;
15086     },
15087     
15088     
15089
15090     /**
15091      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15092      */
15093     refresh : function(){
15094         //Roo.log('refresh');
15095         var t = this.tpl;
15096         
15097         // if we are using something like 'domtemplate', then
15098         // the what gets used is:
15099         // t.applySubtemplate(NAME, data, wrapping data..)
15100         // the outer template then get' applied with
15101         //     the store 'extra data'
15102         // and the body get's added to the
15103         //      roo-name="data" node?
15104         //      <span class='roo-tpl-{name}'></span> ?????
15105         
15106         
15107         
15108         this.clearSelections();
15109         this.el.update("");
15110         var html = [];
15111         var records = this.store.getRange();
15112         if(records.length < 1) {
15113             
15114             // is this valid??  = should it render a template??
15115             
15116             this.el.update(this.emptyText);
15117             return;
15118         }
15119         var el = this.el;
15120         if (this.dataName) {
15121             this.el.update(t.apply(this.store.meta)); //????
15122             el = this.el.child('.roo-tpl-' + this.dataName);
15123         }
15124         
15125         for(var i = 0, len = records.length; i < len; i++){
15126             var data = this.prepareData(records[i].data, i, records[i]);
15127             this.fireEvent("preparedata", this, data, i, records[i]);
15128             
15129             var d = Roo.apply({}, data);
15130             
15131             if(this.tickable){
15132                 Roo.apply(d, {'roo-id' : Roo.id()});
15133                 
15134                 var _this = this;
15135             
15136                 Roo.each(this.parent.item, function(item){
15137                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15138                         return;
15139                     }
15140                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15141                 });
15142             }
15143             
15144             html[html.length] = Roo.util.Format.trim(
15145                 this.dataName ?
15146                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15147                     t.apply(d)
15148             );
15149         }
15150         
15151         
15152         
15153         el.update(html.join(""));
15154         this.nodes = el.dom.childNodes;
15155         this.updateIndexes(0);
15156     },
15157     
15158
15159     /**
15160      * Function to override to reformat the data that is sent to
15161      * the template for each node.
15162      * DEPRICATED - use the preparedata event handler.
15163      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15164      * a JSON object for an UpdateManager bound view).
15165      */
15166     prepareData : function(data, index, record)
15167     {
15168         this.fireEvent("preparedata", this, data, index, record);
15169         return data;
15170     },
15171
15172     onUpdate : function(ds, record){
15173         // Roo.log('on update');   
15174         this.clearSelections();
15175         var index = this.store.indexOf(record);
15176         var n = this.nodes[index];
15177         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15178         n.parentNode.removeChild(n);
15179         this.updateIndexes(index, index);
15180     },
15181
15182     
15183     
15184 // --------- FIXME     
15185     onAdd : function(ds, records, index)
15186     {
15187         //Roo.log(['on Add', ds, records, index] );        
15188         this.clearSelections();
15189         if(this.nodes.length == 0){
15190             this.refresh();
15191             return;
15192         }
15193         var n = this.nodes[index];
15194         for(var i = 0, len = records.length; i < len; i++){
15195             var d = this.prepareData(records[i].data, i, records[i]);
15196             if(n){
15197                 this.tpl.insertBefore(n, d);
15198             }else{
15199                 
15200                 this.tpl.append(this.el, d);
15201             }
15202         }
15203         this.updateIndexes(index);
15204     },
15205
15206     onRemove : function(ds, record, index){
15207        // Roo.log('onRemove');
15208         this.clearSelections();
15209         var el = this.dataName  ?
15210             this.el.child('.roo-tpl-' + this.dataName) :
15211             this.el; 
15212         
15213         el.dom.removeChild(this.nodes[index]);
15214         this.updateIndexes(index);
15215     },
15216
15217     /**
15218      * Refresh an individual node.
15219      * @param {Number} index
15220      */
15221     refreshNode : function(index){
15222         this.onUpdate(this.store, this.store.getAt(index));
15223     },
15224
15225     updateIndexes : function(startIndex, endIndex){
15226         var ns = this.nodes;
15227         startIndex = startIndex || 0;
15228         endIndex = endIndex || ns.length - 1;
15229         for(var i = startIndex; i <= endIndex; i++){
15230             ns[i].nodeIndex = i;
15231         }
15232     },
15233
15234     /**
15235      * Changes the data store this view uses and refresh the view.
15236      * @param {Store} store
15237      */
15238     setStore : function(store, initial){
15239         if(!initial && this.store){
15240             this.store.un("datachanged", this.refresh);
15241             this.store.un("add", this.onAdd);
15242             this.store.un("remove", this.onRemove);
15243             this.store.un("update", this.onUpdate);
15244             this.store.un("clear", this.refresh);
15245             this.store.un("beforeload", this.onBeforeLoad);
15246             this.store.un("load", this.onLoad);
15247             this.store.un("loadexception", this.onLoad);
15248         }
15249         if(store){
15250           
15251             store.on("datachanged", this.refresh, this);
15252             store.on("add", this.onAdd, this);
15253             store.on("remove", this.onRemove, this);
15254             store.on("update", this.onUpdate, this);
15255             store.on("clear", this.refresh, this);
15256             store.on("beforeload", this.onBeforeLoad, this);
15257             store.on("load", this.onLoad, this);
15258             store.on("loadexception", this.onLoad, this);
15259         }
15260         
15261         if(store){
15262             this.refresh();
15263         }
15264     },
15265     /**
15266      * onbeforeLoad - masks the loading area.
15267      *
15268      */
15269     onBeforeLoad : function(store,opts)
15270     {
15271          //Roo.log('onBeforeLoad');   
15272         if (!opts.add) {
15273             this.el.update("");
15274         }
15275         this.el.mask(this.mask ? this.mask : "Loading" ); 
15276     },
15277     onLoad : function ()
15278     {
15279         this.el.unmask();
15280     },
15281     
15282
15283     /**
15284      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15285      * @param {HTMLElement} node
15286      * @return {HTMLElement} The template node
15287      */
15288     findItemFromChild : function(node){
15289         var el = this.dataName  ?
15290             this.el.child('.roo-tpl-' + this.dataName,true) :
15291             this.el.dom; 
15292         
15293         if(!node || node.parentNode == el){
15294                     return node;
15295             }
15296             var p = node.parentNode;
15297             while(p && p != el){
15298             if(p.parentNode == el){
15299                 return p;
15300             }
15301             p = p.parentNode;
15302         }
15303             return null;
15304     },
15305
15306     /** @ignore */
15307     onClick : function(e){
15308         var item = this.findItemFromChild(e.getTarget());
15309         if(item){
15310             var index = this.indexOf(item);
15311             if(this.onItemClick(item, index, e) !== false){
15312                 this.fireEvent("click", this, index, item, e);
15313             }
15314         }else{
15315             this.clearSelections();
15316         }
15317     },
15318
15319     /** @ignore */
15320     onContextMenu : function(e){
15321         var item = this.findItemFromChild(e.getTarget());
15322         if(item){
15323             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15324         }
15325     },
15326
15327     /** @ignore */
15328     onDblClick : function(e){
15329         var item = this.findItemFromChild(e.getTarget());
15330         if(item){
15331             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15332         }
15333     },
15334
15335     onItemClick : function(item, index, e)
15336     {
15337         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15338             return false;
15339         }
15340         if (this.toggleSelect) {
15341             var m = this.isSelected(item) ? 'unselect' : 'select';
15342             //Roo.log(m);
15343             var _t = this;
15344             _t[m](item, true, false);
15345             return true;
15346         }
15347         if(this.multiSelect || this.singleSelect){
15348             if(this.multiSelect && e.shiftKey && this.lastSelection){
15349                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15350             }else{
15351                 this.select(item, this.multiSelect && e.ctrlKey);
15352                 this.lastSelection = item;
15353             }
15354             
15355             if(!this.tickable){
15356                 e.preventDefault();
15357             }
15358             
15359         }
15360         return true;
15361     },
15362
15363     /**
15364      * Get the number of selected nodes.
15365      * @return {Number}
15366      */
15367     getSelectionCount : function(){
15368         return this.selections.length;
15369     },
15370
15371     /**
15372      * Get the currently selected nodes.
15373      * @return {Array} An array of HTMLElements
15374      */
15375     getSelectedNodes : function(){
15376         return this.selections;
15377     },
15378
15379     /**
15380      * Get the indexes of the selected nodes.
15381      * @return {Array}
15382      */
15383     getSelectedIndexes : function(){
15384         var indexes = [], s = this.selections;
15385         for(var i = 0, len = s.length; i < len; i++){
15386             indexes.push(s[i].nodeIndex);
15387         }
15388         return indexes;
15389     },
15390
15391     /**
15392      * Clear all selections
15393      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15394      */
15395     clearSelections : function(suppressEvent){
15396         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15397             this.cmp.elements = this.selections;
15398             this.cmp.removeClass(this.selectedClass);
15399             this.selections = [];
15400             if(!suppressEvent){
15401                 this.fireEvent("selectionchange", this, this.selections);
15402             }
15403         }
15404     },
15405
15406     /**
15407      * Returns true if the passed node is selected
15408      * @param {HTMLElement/Number} node The node or node index
15409      * @return {Boolean}
15410      */
15411     isSelected : function(node){
15412         var s = this.selections;
15413         if(s.length < 1){
15414             return false;
15415         }
15416         node = this.getNode(node);
15417         return s.indexOf(node) !== -1;
15418     },
15419
15420     /**
15421      * Selects nodes.
15422      * @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
15423      * @param {Boolean} keepExisting (optional) true to keep existing selections
15424      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15425      */
15426     select : function(nodeInfo, keepExisting, suppressEvent){
15427         if(nodeInfo instanceof Array){
15428             if(!keepExisting){
15429                 this.clearSelections(true);
15430             }
15431             for(var i = 0, len = nodeInfo.length; i < len; i++){
15432                 this.select(nodeInfo[i], true, true);
15433             }
15434             return;
15435         } 
15436         var node = this.getNode(nodeInfo);
15437         if(!node || this.isSelected(node)){
15438             return; // already selected.
15439         }
15440         if(!keepExisting){
15441             this.clearSelections(true);
15442         }
15443         
15444         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15445             Roo.fly(node).addClass(this.selectedClass);
15446             this.selections.push(node);
15447             if(!suppressEvent){
15448                 this.fireEvent("selectionchange", this, this.selections);
15449             }
15450         }
15451         
15452         
15453     },
15454       /**
15455      * Unselects nodes.
15456      * @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
15457      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15458      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15459      */
15460     unselect : function(nodeInfo, keepExisting, suppressEvent)
15461     {
15462         if(nodeInfo instanceof Array){
15463             Roo.each(this.selections, function(s) {
15464                 this.unselect(s, nodeInfo);
15465             }, this);
15466             return;
15467         }
15468         var node = this.getNode(nodeInfo);
15469         if(!node || !this.isSelected(node)){
15470             //Roo.log("not selected");
15471             return; // not selected.
15472         }
15473         // fireevent???
15474         var ns = [];
15475         Roo.each(this.selections, function(s) {
15476             if (s == node ) {
15477                 Roo.fly(node).removeClass(this.selectedClass);
15478
15479                 return;
15480             }
15481             ns.push(s);
15482         },this);
15483         
15484         this.selections= ns;
15485         this.fireEvent("selectionchange", this, this.selections);
15486     },
15487
15488     /**
15489      * Gets a template node.
15490      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15491      * @return {HTMLElement} The node or null if it wasn't found
15492      */
15493     getNode : function(nodeInfo){
15494         if(typeof nodeInfo == "string"){
15495             return document.getElementById(nodeInfo);
15496         }else if(typeof nodeInfo == "number"){
15497             return this.nodes[nodeInfo];
15498         }
15499         return nodeInfo;
15500     },
15501
15502     /**
15503      * Gets a range template nodes.
15504      * @param {Number} startIndex
15505      * @param {Number} endIndex
15506      * @return {Array} An array of nodes
15507      */
15508     getNodes : function(start, end){
15509         var ns = this.nodes;
15510         start = start || 0;
15511         end = typeof end == "undefined" ? ns.length - 1 : end;
15512         var nodes = [];
15513         if(start <= end){
15514             for(var i = start; i <= end; i++){
15515                 nodes.push(ns[i]);
15516             }
15517         } else{
15518             for(var i = start; i >= end; i--){
15519                 nodes.push(ns[i]);
15520             }
15521         }
15522         return nodes;
15523     },
15524
15525     /**
15526      * Finds the index of the passed node
15527      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15528      * @return {Number} The index of the node or -1
15529      */
15530     indexOf : function(node){
15531         node = this.getNode(node);
15532         if(typeof node.nodeIndex == "number"){
15533             return node.nodeIndex;
15534         }
15535         var ns = this.nodes;
15536         for(var i = 0, len = ns.length; i < len; i++){
15537             if(ns[i] == node){
15538                 return i;
15539             }
15540         }
15541         return -1;
15542     }
15543 });
15544 /*
15545  * - LGPL
15546  *
15547  * based on jquery fullcalendar
15548  * 
15549  */
15550
15551 Roo.bootstrap = Roo.bootstrap || {};
15552 /**
15553  * @class Roo.bootstrap.Calendar
15554  * @extends Roo.bootstrap.Component
15555  * Bootstrap Calendar class
15556  * @cfg {Boolean} loadMask (true|false) default false
15557  * @cfg {Object} header generate the user specific header of the calendar, default false
15558
15559  * @constructor
15560  * Create a new Container
15561  * @param {Object} config The config object
15562  */
15563
15564
15565
15566 Roo.bootstrap.Calendar = function(config){
15567     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15568      this.addEvents({
15569         /**
15570              * @event select
15571              * Fires when a date is selected
15572              * @param {DatePicker} this
15573              * @param {Date} date The selected date
15574              */
15575         'select': true,
15576         /**
15577              * @event monthchange
15578              * Fires when the displayed month changes 
15579              * @param {DatePicker} this
15580              * @param {Date} date The selected month
15581              */
15582         'monthchange': true,
15583         /**
15584              * @event evententer
15585              * Fires when mouse over an event
15586              * @param {Calendar} this
15587              * @param {event} Event
15588              */
15589         'evententer': true,
15590         /**
15591              * @event eventleave
15592              * Fires when the mouse leaves an
15593              * @param {Calendar} this
15594              * @param {event}
15595              */
15596         'eventleave': true,
15597         /**
15598              * @event eventclick
15599              * Fires when the mouse click an
15600              * @param {Calendar} this
15601              * @param {event}
15602              */
15603         'eventclick': true
15604         
15605     });
15606
15607 };
15608
15609 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
15610     
15611      /**
15612      * @cfg {Number} startDay
15613      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15614      */
15615     startDay : 0,
15616     
15617     loadMask : false,
15618     
15619     header : false,
15620       
15621     getAutoCreate : function(){
15622         
15623         
15624         var fc_button = function(name, corner, style, content ) {
15625             return Roo.apply({},{
15626                 tag : 'span',
15627                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
15628                          (corner.length ?
15629                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15630                             ''
15631                         ),
15632                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15633                 unselectable: 'on'
15634             });
15635         };
15636         
15637         var header = {};
15638         
15639         if(!this.header){
15640             header = {
15641                 tag : 'table',
15642                 cls : 'fc-header',
15643                 style : 'width:100%',
15644                 cn : [
15645                     {
15646                         tag: 'tr',
15647                         cn : [
15648                             {
15649                                 tag : 'td',
15650                                 cls : 'fc-header-left',
15651                                 cn : [
15652                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
15653                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
15654                                     { tag: 'span', cls: 'fc-header-space' },
15655                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
15656
15657
15658                                 ]
15659                             },
15660
15661                             {
15662                                 tag : 'td',
15663                                 cls : 'fc-header-center',
15664                                 cn : [
15665                                     {
15666                                         tag: 'span',
15667                                         cls: 'fc-header-title',
15668                                         cn : {
15669                                             tag: 'H2',
15670                                             html : 'month / year'
15671                                         }
15672                                     }
15673
15674                                 ]
15675                             },
15676                             {
15677                                 tag : 'td',
15678                                 cls : 'fc-header-right',
15679                                 cn : [
15680                               /*      fc_button('month', 'left', '', 'month' ),
15681                                     fc_button('week', '', '', 'week' ),
15682                                     fc_button('day', 'right', '', 'day' )
15683                                 */    
15684
15685                                 ]
15686                             }
15687
15688                         ]
15689                     }
15690                 ]
15691             };
15692         }
15693         
15694         header = this.header;
15695         
15696        
15697         var cal_heads = function() {
15698             var ret = [];
15699             // fixme - handle this.
15700             
15701             for (var i =0; i < Date.dayNames.length; i++) {
15702                 var d = Date.dayNames[i];
15703                 ret.push({
15704                     tag: 'th',
15705                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15706                     html : d.substring(0,3)
15707                 });
15708                 
15709             }
15710             ret[0].cls += ' fc-first';
15711             ret[6].cls += ' fc-last';
15712             return ret;
15713         };
15714         var cal_cell = function(n) {
15715             return  {
15716                 tag: 'td',
15717                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15718                 cn : [
15719                     {
15720                         cn : [
15721                             {
15722                                 cls: 'fc-day-number',
15723                                 html: 'D'
15724                             },
15725                             {
15726                                 cls: 'fc-day-content',
15727                              
15728                                 cn : [
15729                                      {
15730                                         style: 'position: relative;' // height: 17px;
15731                                     }
15732                                 ]
15733                             }
15734                             
15735                             
15736                         ]
15737                     }
15738                 ]
15739                 
15740             }
15741         };
15742         var cal_rows = function() {
15743             
15744             var ret = [];
15745             for (var r = 0; r < 6; r++) {
15746                 var row= {
15747                     tag : 'tr',
15748                     cls : 'fc-week',
15749                     cn : []
15750                 };
15751                 
15752                 for (var i =0; i < Date.dayNames.length; i++) {
15753                     var d = Date.dayNames[i];
15754                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15755
15756                 }
15757                 row.cn[0].cls+=' fc-first';
15758                 row.cn[0].cn[0].style = 'min-height:90px';
15759                 row.cn[6].cls+=' fc-last';
15760                 ret.push(row);
15761                 
15762             }
15763             ret[0].cls += ' fc-first';
15764             ret[4].cls += ' fc-prev-last';
15765             ret[5].cls += ' fc-last';
15766             return ret;
15767             
15768         };
15769         
15770         var cal_table = {
15771             tag: 'table',
15772             cls: 'fc-border-separate',
15773             style : 'width:100%',
15774             cellspacing  : 0,
15775             cn : [
15776                 { 
15777                     tag: 'thead',
15778                     cn : [
15779                         { 
15780                             tag: 'tr',
15781                             cls : 'fc-first fc-last',
15782                             cn : cal_heads()
15783                         }
15784                     ]
15785                 },
15786                 { 
15787                     tag: 'tbody',
15788                     cn : cal_rows()
15789                 }
15790                   
15791             ]
15792         };
15793          
15794          var cfg = {
15795             cls : 'fc fc-ltr',
15796             cn : [
15797                 header,
15798                 {
15799                     cls : 'fc-content',
15800                     style : "position: relative;",
15801                     cn : [
15802                         {
15803                             cls : 'fc-view fc-view-month fc-grid',
15804                             style : 'position: relative',
15805                             unselectable : 'on',
15806                             cn : [
15807                                 {
15808                                     cls : 'fc-event-container',
15809                                     style : 'position:absolute;z-index:8;top:0;left:0;'
15810                                 },
15811                                 cal_table
15812                             ]
15813                         }
15814                     ]
15815     
15816                 }
15817            ] 
15818             
15819         };
15820         
15821          
15822         
15823         return cfg;
15824     },
15825     
15826     
15827     initEvents : function()
15828     {
15829         if(!this.store){
15830             throw "can not find store for calendar";
15831         }
15832         
15833         var mark = {
15834             tag: "div",
15835             cls:"x-dlg-mask",
15836             style: "text-align:center",
15837             cn: [
15838                 {
15839                     tag: "div",
15840                     style: "background-color:white;width:50%;margin:250 auto",
15841                     cn: [
15842                         {
15843                             tag: "img",
15844                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
15845                         },
15846                         {
15847                             tag: "span",
15848                             html: "Loading"
15849                         }
15850                         
15851                     ]
15852                 }
15853             ]
15854         };
15855         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15856         
15857         var size = this.el.select('.fc-content', true).first().getSize();
15858         this.maskEl.setSize(size.width, size.height);
15859         this.maskEl.enableDisplayMode("block");
15860         if(!this.loadMask){
15861             this.maskEl.hide();
15862         }
15863         
15864         this.store = Roo.factory(this.store, Roo.data);
15865         this.store.on('load', this.onLoad, this);
15866         this.store.on('beforeload', this.onBeforeLoad, this);
15867         
15868         this.resize();
15869         
15870         this.cells = this.el.select('.fc-day',true);
15871         //Roo.log(this.cells);
15872         this.textNodes = this.el.query('.fc-day-number');
15873         this.cells.addClassOnOver('fc-state-hover');
15874         
15875         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15876         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15877         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15878         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15879         
15880         this.on('monthchange', this.onMonthChange, this);
15881         
15882         this.update(new Date().clearTime());
15883     },
15884     
15885     resize : function() {
15886         var sz  = this.el.getSize();
15887         
15888         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15889         this.el.select('.fc-day-content div',true).setHeight(34);
15890     },
15891     
15892     
15893     // private
15894     showPrevMonth : function(e){
15895         this.update(this.activeDate.add("mo", -1));
15896     },
15897     showToday : function(e){
15898         this.update(new Date().clearTime());
15899     },
15900     // private
15901     showNextMonth : function(e){
15902         this.update(this.activeDate.add("mo", 1));
15903     },
15904
15905     // private
15906     showPrevYear : function(){
15907         this.update(this.activeDate.add("y", -1));
15908     },
15909
15910     // private
15911     showNextYear : function(){
15912         this.update(this.activeDate.add("y", 1));
15913     },
15914
15915     
15916    // private
15917     update : function(date)
15918     {
15919         var vd = this.activeDate;
15920         this.activeDate = date;
15921 //        if(vd && this.el){
15922 //            var t = date.getTime();
15923 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15924 //                Roo.log('using add remove');
15925 //                
15926 //                this.fireEvent('monthchange', this, date);
15927 //                
15928 //                this.cells.removeClass("fc-state-highlight");
15929 //                this.cells.each(function(c){
15930 //                   if(c.dateValue == t){
15931 //                       c.addClass("fc-state-highlight");
15932 //                       setTimeout(function(){
15933 //                            try{c.dom.firstChild.focus();}catch(e){}
15934 //                       }, 50);
15935 //                       return false;
15936 //                   }
15937 //                   return true;
15938 //                });
15939 //                return;
15940 //            }
15941 //        }
15942         
15943         var days = date.getDaysInMonth();
15944         
15945         var firstOfMonth = date.getFirstDateOfMonth();
15946         var startingPos = firstOfMonth.getDay()-this.startDay;
15947         
15948         if(startingPos < this.startDay){
15949             startingPos += 7;
15950         }
15951         
15952         var pm = date.add(Date.MONTH, -1);
15953         var prevStart = pm.getDaysInMonth()-startingPos;
15954 //        
15955         this.cells = this.el.select('.fc-day',true);
15956         this.textNodes = this.el.query('.fc-day-number');
15957         this.cells.addClassOnOver('fc-state-hover');
15958         
15959         var cells = this.cells.elements;
15960         var textEls = this.textNodes;
15961         
15962         Roo.each(cells, function(cell){
15963             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15964         });
15965         
15966         days += startingPos;
15967
15968         // convert everything to numbers so it's fast
15969         var day = 86400000;
15970         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15971         //Roo.log(d);
15972         //Roo.log(pm);
15973         //Roo.log(prevStart);
15974         
15975         var today = new Date().clearTime().getTime();
15976         var sel = date.clearTime().getTime();
15977         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15978         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15979         var ddMatch = this.disabledDatesRE;
15980         var ddText = this.disabledDatesText;
15981         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15982         var ddaysText = this.disabledDaysText;
15983         var format = this.format;
15984         
15985         var setCellClass = function(cal, cell){
15986             cell.row = 0;
15987             cell.events = [];
15988             cell.more = [];
15989             //Roo.log('set Cell Class');
15990             cell.title = "";
15991             var t = d.getTime();
15992             
15993             //Roo.log(d);
15994             
15995             cell.dateValue = t;
15996             if(t == today){
15997                 cell.className += " fc-today";
15998                 cell.className += " fc-state-highlight";
15999                 cell.title = cal.todayText;
16000             }
16001             if(t == sel){
16002                 // disable highlight in other month..
16003                 //cell.className += " fc-state-highlight";
16004                 
16005             }
16006             // disabling
16007             if(t < min) {
16008                 cell.className = " fc-state-disabled";
16009                 cell.title = cal.minText;
16010                 return;
16011             }
16012             if(t > max) {
16013                 cell.className = " fc-state-disabled";
16014                 cell.title = cal.maxText;
16015                 return;
16016             }
16017             if(ddays){
16018                 if(ddays.indexOf(d.getDay()) != -1){
16019                     cell.title = ddaysText;
16020                     cell.className = " fc-state-disabled";
16021                 }
16022             }
16023             if(ddMatch && format){
16024                 var fvalue = d.dateFormat(format);
16025                 if(ddMatch.test(fvalue)){
16026                     cell.title = ddText.replace("%0", fvalue);
16027                     cell.className = " fc-state-disabled";
16028                 }
16029             }
16030             
16031             if (!cell.initialClassName) {
16032                 cell.initialClassName = cell.dom.className;
16033             }
16034             
16035             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16036         };
16037
16038         var i = 0;
16039         
16040         for(; i < startingPos; i++) {
16041             textEls[i].innerHTML = (++prevStart);
16042             d.setDate(d.getDate()+1);
16043             
16044             cells[i].className = "fc-past fc-other-month";
16045             setCellClass(this, cells[i]);
16046         }
16047         
16048         var intDay = 0;
16049         
16050         for(; i < days; i++){
16051             intDay = i - startingPos + 1;
16052             textEls[i].innerHTML = (intDay);
16053             d.setDate(d.getDate()+1);
16054             
16055             cells[i].className = ''; // "x-date-active";
16056             setCellClass(this, cells[i]);
16057         }
16058         var extraDays = 0;
16059         
16060         for(; i < 42; i++) {
16061             textEls[i].innerHTML = (++extraDays);
16062             d.setDate(d.getDate()+1);
16063             
16064             cells[i].className = "fc-future fc-other-month";
16065             setCellClass(this, cells[i]);
16066         }
16067         
16068         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16069         
16070         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16071         
16072         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16073         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16074         
16075         if(totalRows != 6){
16076             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16077             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16078         }
16079         
16080         this.fireEvent('monthchange', this, date);
16081         
16082         
16083         /*
16084         if(!this.internalRender){
16085             var main = this.el.dom.firstChild;
16086             var w = main.offsetWidth;
16087             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16088             Roo.fly(main).setWidth(w);
16089             this.internalRender = true;
16090             // opera does not respect the auto grow header center column
16091             // then, after it gets a width opera refuses to recalculate
16092             // without a second pass
16093             if(Roo.isOpera && !this.secondPass){
16094                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16095                 this.secondPass = true;
16096                 this.update.defer(10, this, [date]);
16097             }
16098         }
16099         */
16100         
16101     },
16102     
16103     findCell : function(dt) {
16104         dt = dt.clearTime().getTime();
16105         var ret = false;
16106         this.cells.each(function(c){
16107             //Roo.log("check " +c.dateValue + '?=' + dt);
16108             if(c.dateValue == dt){
16109                 ret = c;
16110                 return false;
16111             }
16112             return true;
16113         });
16114         
16115         return ret;
16116     },
16117     
16118     findCells : function(ev) {
16119         var s = ev.start.clone().clearTime().getTime();
16120        // Roo.log(s);
16121         var e= ev.end.clone().clearTime().getTime();
16122        // Roo.log(e);
16123         var ret = [];
16124         this.cells.each(function(c){
16125              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16126             
16127             if(c.dateValue > e){
16128                 return ;
16129             }
16130             if(c.dateValue < s){
16131                 return ;
16132             }
16133             ret.push(c);
16134         });
16135         
16136         return ret;    
16137     },
16138     
16139 //    findBestRow: function(cells)
16140 //    {
16141 //        var ret = 0;
16142 //        
16143 //        for (var i =0 ; i < cells.length;i++) {
16144 //            ret  = Math.max(cells[i].rows || 0,ret);
16145 //        }
16146 //        return ret;
16147 //        
16148 //    },
16149     
16150     
16151     addItem : function(ev)
16152     {
16153         // look for vertical location slot in
16154         var cells = this.findCells(ev);
16155         
16156 //        ev.row = this.findBestRow(cells);
16157         
16158         // work out the location.
16159         
16160         var crow = false;
16161         var rows = [];
16162         for(var i =0; i < cells.length; i++) {
16163             
16164             cells[i].row = cells[0].row;
16165             
16166             if(i == 0){
16167                 cells[i].row = cells[i].row + 1;
16168             }
16169             
16170             if (!crow) {
16171                 crow = {
16172                     start : cells[i],
16173                     end :  cells[i]
16174                 };
16175                 continue;
16176             }
16177             if (crow.start.getY() == cells[i].getY()) {
16178                 // on same row.
16179                 crow.end = cells[i];
16180                 continue;
16181             }
16182             // different row.
16183             rows.push(crow);
16184             crow = {
16185                 start: cells[i],
16186                 end : cells[i]
16187             };
16188             
16189         }
16190         
16191         rows.push(crow);
16192         ev.els = [];
16193         ev.rows = rows;
16194         ev.cells = cells;
16195         
16196         cells[0].events.push(ev);
16197         
16198         this.calevents.push(ev);
16199     },
16200     
16201     clearEvents: function() {
16202         
16203         if(!this.calevents){
16204             return;
16205         }
16206         
16207         Roo.each(this.cells.elements, function(c){
16208             c.row = 0;
16209             c.events = [];
16210             c.more = [];
16211         });
16212         
16213         Roo.each(this.calevents, function(e) {
16214             Roo.each(e.els, function(el) {
16215                 el.un('mouseenter' ,this.onEventEnter, this);
16216                 el.un('mouseleave' ,this.onEventLeave, this);
16217                 el.remove();
16218             },this);
16219         },this);
16220         
16221         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16222             e.remove();
16223         });
16224         
16225     },
16226     
16227     renderEvents: function()
16228     {   
16229         var _this = this;
16230         
16231         this.cells.each(function(c) {
16232             
16233             if(c.row < 5){
16234                 return;
16235             }
16236             
16237             var ev = c.events;
16238             
16239             var r = 4;
16240             if(c.row != c.events.length){
16241                 r = 4 - (4 - (c.row - c.events.length));
16242             }
16243             
16244             c.events = ev.slice(0, r);
16245             c.more = ev.slice(r);
16246             
16247             if(c.more.length && c.more.length == 1){
16248                 c.events.push(c.more.pop());
16249             }
16250             
16251             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16252             
16253         });
16254             
16255         this.cells.each(function(c) {
16256             
16257             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16258             
16259             
16260             for (var e = 0; e < c.events.length; e++){
16261                 var ev = c.events[e];
16262                 var rows = ev.rows;
16263                 
16264                 for(var i = 0; i < rows.length; i++) {
16265                 
16266                     // how many rows should it span..
16267
16268                     var  cfg = {
16269                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16270                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16271
16272                         unselectable : "on",
16273                         cn : [
16274                             {
16275                                 cls: 'fc-event-inner',
16276                                 cn : [
16277     //                                {
16278     //                                  tag:'span',
16279     //                                  cls: 'fc-event-time',
16280     //                                  html : cells.length > 1 ? '' : ev.time
16281     //                                },
16282                                     {
16283                                       tag:'span',
16284                                       cls: 'fc-event-title',
16285                                       html : String.format('{0}', ev.title)
16286                                     }
16287
16288
16289                                 ]
16290                             },
16291                             {
16292                                 cls: 'ui-resizable-handle ui-resizable-e',
16293                                 html : '&nbsp;&nbsp;&nbsp'
16294                             }
16295
16296                         ]
16297                     };
16298
16299                     if (i == 0) {
16300                         cfg.cls += ' fc-event-start';
16301                     }
16302                     if ((i+1) == rows.length) {
16303                         cfg.cls += ' fc-event-end';
16304                     }
16305
16306                     var ctr = _this.el.select('.fc-event-container',true).first();
16307                     var cg = ctr.createChild(cfg);
16308
16309                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16310                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16311
16312                     var r = (c.more.length) ? 1 : 0;
16313                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16314                     cg.setWidth(ebox.right - sbox.x -2);
16315
16316                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16317                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16318                     cg.on('click', _this.onEventClick, _this, ev);
16319
16320                     ev.els.push(cg);
16321                     
16322                 }
16323                 
16324             }
16325             
16326             
16327             if(c.more.length){
16328                 var  cfg = {
16329                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16330                     style : 'position: absolute',
16331                     unselectable : "on",
16332                     cn : [
16333                         {
16334                             cls: 'fc-event-inner',
16335                             cn : [
16336                                 {
16337                                   tag:'span',
16338                                   cls: 'fc-event-title',
16339                                   html : 'More'
16340                                 }
16341
16342
16343                             ]
16344                         },
16345                         {
16346                             cls: 'ui-resizable-handle ui-resizable-e',
16347                             html : '&nbsp;&nbsp;&nbsp'
16348                         }
16349
16350                     ]
16351                 };
16352
16353                 var ctr = _this.el.select('.fc-event-container',true).first();
16354                 var cg = ctr.createChild(cfg);
16355
16356                 var sbox = c.select('.fc-day-content',true).first().getBox();
16357                 var ebox = c.select('.fc-day-content',true).first().getBox();
16358                 //Roo.log(cg);
16359                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
16360                 cg.setWidth(ebox.right - sbox.x -2);
16361
16362                 cg.on('click', _this.onMoreEventClick, _this, c.more);
16363                 
16364             }
16365             
16366         });
16367         
16368         
16369         
16370     },
16371     
16372     onEventEnter: function (e, el,event,d) {
16373         this.fireEvent('evententer', this, el, event);
16374     },
16375     
16376     onEventLeave: function (e, el,event,d) {
16377         this.fireEvent('eventleave', this, el, event);
16378     },
16379     
16380     onEventClick: function (e, el,event,d) {
16381         this.fireEvent('eventclick', this, el, event);
16382     },
16383     
16384     onMonthChange: function () {
16385         this.store.load();
16386     },
16387     
16388     onMoreEventClick: function(e, el, more)
16389     {
16390         var _this = this;
16391         
16392         this.calpopover.placement = 'right';
16393         this.calpopover.setTitle('More');
16394         
16395         this.calpopover.setContent('');
16396         
16397         var ctr = this.calpopover.el.select('.popover-content', true).first();
16398         
16399         Roo.each(more, function(m){
16400             var cfg = {
16401                 cls : 'fc-event-hori fc-event-draggable',
16402                 html : m.title
16403             };
16404             var cg = ctr.createChild(cfg);
16405             
16406             cg.on('click', _this.onEventClick, _this, m);
16407         });
16408         
16409         this.calpopover.show(el);
16410         
16411         
16412     },
16413     
16414     onLoad: function () 
16415     {   
16416         this.calevents = [];
16417         var cal = this;
16418         
16419         if(this.store.getCount() > 0){
16420             this.store.data.each(function(d){
16421                cal.addItem({
16422                     id : d.data.id,
16423                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16424                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16425                     time : d.data.start_time,
16426                     title : d.data.title,
16427                     description : d.data.description,
16428                     venue : d.data.venue
16429                 });
16430             });
16431         }
16432         
16433         this.renderEvents();
16434         
16435         if(this.calevents.length && this.loadMask){
16436             this.maskEl.hide();
16437         }
16438     },
16439     
16440     onBeforeLoad: function()
16441     {
16442         this.clearEvents();
16443         if(this.loadMask){
16444             this.maskEl.show();
16445         }
16446     }
16447 });
16448
16449  
16450  /*
16451  * - LGPL
16452  *
16453  * element
16454  * 
16455  */
16456
16457 /**
16458  * @class Roo.bootstrap.Popover
16459  * @extends Roo.bootstrap.Component
16460  * Bootstrap Popover class
16461  * @cfg {String} html contents of the popover   (or false to use children..)
16462  * @cfg {String} title of popover (or false to hide)
16463  * @cfg {String} placement how it is placed
16464  * @cfg {String} trigger click || hover (or false to trigger manually)
16465  * @cfg {String} over what (parent or false to trigger manually.)
16466  * @cfg {Number} delay - delay before showing
16467  
16468  * @constructor
16469  * Create a new Popover
16470  * @param {Object} config The config object
16471  */
16472
16473 Roo.bootstrap.Popover = function(config){
16474     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16475     
16476     this.addEvents({
16477         // raw events
16478          /**
16479          * @event show
16480          * After the popover show
16481          * 
16482          * @param {Roo.bootstrap.Popover} this
16483          */
16484         "show" : true,
16485         /**
16486          * @event hide
16487          * After the popover hide
16488          * 
16489          * @param {Roo.bootstrap.Popover} this
16490          */
16491         "hide" : true
16492     });
16493 };
16494
16495 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
16496     
16497     title: 'Fill in a title',
16498     html: false,
16499     
16500     placement : 'right',
16501     trigger : 'hover', // hover
16502     
16503     delay : 0,
16504     
16505     over: 'parent',
16506     
16507     can_build_overlaid : false,
16508     
16509     getChildContainer : function()
16510     {
16511         return this.el.select('.popover-content',true).first();
16512     },
16513     
16514     getAutoCreate : function(){
16515          
16516         var cfg = {
16517            cls : 'popover roo-dynamic',
16518            style: 'display:block',
16519            cn : [
16520                 {
16521                     cls : 'arrow'
16522                 },
16523                 {
16524                     cls : 'popover-inner',
16525                     cn : [
16526                         {
16527                             tag: 'h3',
16528                             cls: 'popover-title',
16529                             html : this.title
16530                         },
16531                         {
16532                             cls : 'popover-content',
16533                             html : this.html
16534                         }
16535                     ]
16536                     
16537                 }
16538            ]
16539         };
16540         
16541         return cfg;
16542     },
16543     setTitle: function(str)
16544     {
16545         this.title = str;
16546         this.el.select('.popover-title',true).first().dom.innerHTML = str;
16547     },
16548     setContent: function(str)
16549     {
16550         this.html = str;
16551         this.el.select('.popover-content',true).first().dom.innerHTML = str;
16552     },
16553     // as it get's added to the bottom of the page.
16554     onRender : function(ct, position)
16555     {
16556         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16557         if(!this.el){
16558             var cfg = Roo.apply({},  this.getAutoCreate());
16559             cfg.id = Roo.id();
16560             
16561             if (this.cls) {
16562                 cfg.cls += ' ' + this.cls;
16563             }
16564             if (this.style) {
16565                 cfg.style = this.style;
16566             }
16567             //Roo.log("adding to ");
16568             this.el = Roo.get(document.body).createChild(cfg, position);
16569 //            Roo.log(this.el);
16570         }
16571         this.initEvents();
16572     },
16573     
16574     initEvents : function()
16575     {
16576         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16577         this.el.enableDisplayMode('block');
16578         this.el.hide();
16579         if (this.over === false) {
16580             return; 
16581         }
16582         if (this.triggers === false) {
16583             return;
16584         }
16585         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16586         var triggers = this.trigger ? this.trigger.split(' ') : [];
16587         Roo.each(triggers, function(trigger) {
16588         
16589             if (trigger == 'click') {
16590                 on_el.on('click', this.toggle, this);
16591             } else if (trigger != 'manual') {
16592                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
16593                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16594       
16595                 on_el.on(eventIn  ,this.enter, this);
16596                 on_el.on(eventOut, this.leave, this);
16597             }
16598         }, this);
16599         
16600     },
16601     
16602     
16603     // private
16604     timeout : null,
16605     hoverState : null,
16606     
16607     toggle : function () {
16608         this.hoverState == 'in' ? this.leave() : this.enter();
16609     },
16610     
16611     enter : function () {
16612         
16613         clearTimeout(this.timeout);
16614     
16615         this.hoverState = 'in';
16616     
16617         if (!this.delay || !this.delay.show) {
16618             this.show();
16619             return;
16620         }
16621         var _t = this;
16622         this.timeout = setTimeout(function () {
16623             if (_t.hoverState == 'in') {
16624                 _t.show();
16625             }
16626         }, this.delay.show)
16627     },
16628     
16629     leave : function() {
16630         clearTimeout(this.timeout);
16631     
16632         this.hoverState = 'out';
16633     
16634         if (!this.delay || !this.delay.hide) {
16635             this.hide();
16636             return;
16637         }
16638         var _t = this;
16639         this.timeout = setTimeout(function () {
16640             if (_t.hoverState == 'out') {
16641                 _t.hide();
16642             }
16643         }, this.delay.hide)
16644     },
16645     
16646     show : function (on_el)
16647     {
16648         if (!on_el) {
16649             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16650         }
16651         
16652         // set content.
16653         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16654         if (this.html !== false) {
16655             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16656         }
16657         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16658         if (!this.title.length) {
16659             this.el.select('.popover-title',true).hide();
16660         }
16661         
16662         var placement = typeof this.placement == 'function' ?
16663             this.placement.call(this, this.el, on_el) :
16664             this.placement;
16665             
16666         var autoToken = /\s?auto?\s?/i;
16667         var autoPlace = autoToken.test(placement);
16668         if (autoPlace) {
16669             placement = placement.replace(autoToken, '') || 'top';
16670         }
16671         
16672         //this.el.detach()
16673         //this.el.setXY([0,0]);
16674         this.el.show();
16675         this.el.dom.style.display='block';
16676         this.el.addClass(placement);
16677         
16678         //this.el.appendTo(on_el);
16679         
16680         var p = this.getPosition();
16681         var box = this.el.getBox();
16682         
16683         if (autoPlace) {
16684             // fixme..
16685         }
16686         var align = Roo.bootstrap.Popover.alignment[placement];
16687         this.el.alignTo(on_el, align[0],align[1]);
16688         //var arrow = this.el.select('.arrow',true).first();
16689         //arrow.set(align[2], 
16690         
16691         this.el.addClass('in');
16692         
16693         
16694         if (this.el.hasClass('fade')) {
16695             // fade it?
16696         }
16697         
16698         this.hoverState = 'in';
16699         
16700         this.fireEvent('show', this);
16701         
16702     },
16703     hide : function()
16704     {
16705         this.el.setXY([0,0]);
16706         this.el.removeClass('in');
16707         this.el.hide();
16708         this.hoverState = null;
16709         
16710         this.fireEvent('hide', this);
16711     }
16712     
16713 });
16714
16715 Roo.bootstrap.Popover.alignment = {
16716     'left' : ['r-l', [-10,0], 'right'],
16717     'right' : ['l-r', [10,0], 'left'],
16718     'bottom' : ['t-b', [0,10], 'top'],
16719     'top' : [ 'b-t', [0,-10], 'bottom']
16720 };
16721
16722  /*
16723  * - LGPL
16724  *
16725  * Progress
16726  * 
16727  */
16728
16729 /**
16730  * @class Roo.bootstrap.Progress
16731  * @extends Roo.bootstrap.Component
16732  * Bootstrap Progress class
16733  * @cfg {Boolean} striped striped of the progress bar
16734  * @cfg {Boolean} active animated of the progress bar
16735  * 
16736  * 
16737  * @constructor
16738  * Create a new Progress
16739  * @param {Object} config The config object
16740  */
16741
16742 Roo.bootstrap.Progress = function(config){
16743     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16744 };
16745
16746 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
16747     
16748     striped : false,
16749     active: false,
16750     
16751     getAutoCreate : function(){
16752         var cfg = {
16753             tag: 'div',
16754             cls: 'progress'
16755         };
16756         
16757         
16758         if(this.striped){
16759             cfg.cls += ' progress-striped';
16760         }
16761       
16762         if(this.active){
16763             cfg.cls += ' active';
16764         }
16765         
16766         
16767         return cfg;
16768     }
16769    
16770 });
16771
16772  
16773
16774  /*
16775  * - LGPL
16776  *
16777  * ProgressBar
16778  * 
16779  */
16780
16781 /**
16782  * @class Roo.bootstrap.ProgressBar
16783  * @extends Roo.bootstrap.Component
16784  * Bootstrap ProgressBar class
16785  * @cfg {Number} aria_valuenow aria-value now
16786  * @cfg {Number} aria_valuemin aria-value min
16787  * @cfg {Number} aria_valuemax aria-value max
16788  * @cfg {String} label label for the progress bar
16789  * @cfg {String} panel (success | info | warning | danger )
16790  * @cfg {String} role role of the progress bar
16791  * @cfg {String} sr_only text
16792  * 
16793  * 
16794  * @constructor
16795  * Create a new ProgressBar
16796  * @param {Object} config The config object
16797  */
16798
16799 Roo.bootstrap.ProgressBar = function(config){
16800     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16801 };
16802
16803 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
16804     
16805     aria_valuenow : 0,
16806     aria_valuemin : 0,
16807     aria_valuemax : 100,
16808     label : false,
16809     panel : false,
16810     role : false,
16811     sr_only: false,
16812     
16813     getAutoCreate : function()
16814     {
16815         
16816         var cfg = {
16817             tag: 'div',
16818             cls: 'progress-bar',
16819             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16820         };
16821         
16822         if(this.sr_only){
16823             cfg.cn = {
16824                 tag: 'span',
16825                 cls: 'sr-only',
16826                 html: this.sr_only
16827             }
16828         }
16829         
16830         if(this.role){
16831             cfg.role = this.role;
16832         }
16833         
16834         if(this.aria_valuenow){
16835             cfg['aria-valuenow'] = this.aria_valuenow;
16836         }
16837         
16838         if(this.aria_valuemin){
16839             cfg['aria-valuemin'] = this.aria_valuemin;
16840         }
16841         
16842         if(this.aria_valuemax){
16843             cfg['aria-valuemax'] = this.aria_valuemax;
16844         }
16845         
16846         if(this.label && !this.sr_only){
16847             cfg.html = this.label;
16848         }
16849         
16850         if(this.panel){
16851             cfg.cls += ' progress-bar-' + this.panel;
16852         }
16853         
16854         return cfg;
16855     },
16856     
16857     update : function(aria_valuenow)
16858     {
16859         this.aria_valuenow = aria_valuenow;
16860         
16861         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16862     }
16863    
16864 });
16865
16866  
16867
16868  /*
16869  * - LGPL
16870  *
16871  * column
16872  * 
16873  */
16874
16875 /**
16876  * @class Roo.bootstrap.TabGroup
16877  * @extends Roo.bootstrap.Column
16878  * Bootstrap Column class
16879  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16880  * @cfg {Boolean} carousel true to make the group behave like a carousel
16881  * @cfg {Boolean} bullets show bullets for the panels
16882  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16883  * @cfg {Number} timer auto slide timer .. default 0 millisecond
16884  * @cfg {Boolean} showarrow (true|false) show arrow default true
16885  * 
16886  * @constructor
16887  * Create a new TabGroup
16888  * @param {Object} config The config object
16889  */
16890
16891 Roo.bootstrap.TabGroup = function(config){
16892     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16893     if (!this.navId) {
16894         this.navId = Roo.id();
16895     }
16896     this.tabs = [];
16897     Roo.bootstrap.TabGroup.register(this);
16898     
16899 };
16900
16901 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
16902     
16903     carousel : false,
16904     transition : false,
16905     bullets : 0,
16906     timer : 0,
16907     autoslide : false,
16908     slideFn : false,
16909     slideOnTouch : false,
16910     showarrow : true,
16911     
16912     getAutoCreate : function()
16913     {
16914         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16915         
16916         cfg.cls += ' tab-content';
16917         
16918         if (this.carousel) {
16919             cfg.cls += ' carousel slide';
16920             
16921             cfg.cn = [{
16922                cls : 'carousel-inner',
16923                cn : []
16924             }];
16925         
16926             if(this.bullets  && !Roo.isTouch){
16927                 
16928                 var bullets = {
16929                     cls : 'carousel-bullets',
16930                     cn : []
16931                 };
16932                
16933                 if(this.bullets_cls){
16934                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16935                 }
16936                 
16937                 bullets.cn.push({
16938                     cls : 'clear'
16939                 });
16940                 
16941                 cfg.cn[0].cn.push(bullets);
16942             }
16943             
16944             if(this.showarrow){
16945                 cfg.cn[0].cn.push({
16946                     tag : 'div',
16947                     class : 'carousel-arrow',
16948                     cn : [
16949                         {
16950                             tag : 'div',
16951                             class : 'carousel-prev',
16952                             cn : [
16953                                 {
16954                                     tag : 'i',
16955                                     class : 'fa fa-chevron-left'
16956                                 }
16957                             ]
16958                         },
16959                         {
16960                             tag : 'div',
16961                             class : 'carousel-next',
16962                             cn : [
16963                                 {
16964                                     tag : 'i',
16965                                     class : 'fa fa-chevron-right'
16966                                 }
16967                             ]
16968                         }
16969                     ]
16970                 });
16971             }
16972             
16973         }
16974         
16975         return cfg;
16976     },
16977     
16978     initEvents:  function()
16979     {
16980 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
16981 //            this.el.on("touchstart", this.onTouchStart, this);
16982 //        }
16983         
16984         if(this.autoslide){
16985             var _this = this;
16986             
16987             this.slideFn = window.setInterval(function() {
16988                 _this.showPanelNext();
16989             }, this.timer);
16990         }
16991         
16992         if(this.showarrow){
16993             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
16994             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
16995         }
16996         
16997         
16998     },
16999     
17000 //    onTouchStart : function(e, el, o)
17001 //    {
17002 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17003 //            return;
17004 //        }
17005 //        
17006 //        this.showPanelNext();
17007 //    },
17008     
17009     
17010     getChildContainer : function()
17011     {
17012         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17013     },
17014     
17015     /**
17016     * register a Navigation item
17017     * @param {Roo.bootstrap.NavItem} the navitem to add
17018     */
17019     register : function(item)
17020     {
17021         this.tabs.push( item);
17022         item.navId = this.navId; // not really needed..
17023         this.addBullet();
17024     
17025     },
17026     
17027     getActivePanel : function()
17028     {
17029         var r = false;
17030         Roo.each(this.tabs, function(t) {
17031             if (t.active) {
17032                 r = t;
17033                 return false;
17034             }
17035             return null;
17036         });
17037         return r;
17038         
17039     },
17040     getPanelByName : function(n)
17041     {
17042         var r = false;
17043         Roo.each(this.tabs, function(t) {
17044             if (t.tabId == n) {
17045                 r = t;
17046                 return false;
17047             }
17048             return null;
17049         });
17050         return r;
17051     },
17052     indexOfPanel : function(p)
17053     {
17054         var r = false;
17055         Roo.each(this.tabs, function(t,i) {
17056             if (t.tabId == p.tabId) {
17057                 r = i;
17058                 return false;
17059             }
17060             return null;
17061         });
17062         return r;
17063     },
17064     /**
17065      * show a specific panel
17066      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17067      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17068      */
17069     showPanel : function (pan)
17070     {
17071         if(this.transition || typeof(pan) == 'undefined'){
17072             Roo.log("waiting for the transitionend");
17073             return;
17074         }
17075         
17076         if (typeof(pan) == 'number') {
17077             pan = this.tabs[pan];
17078         }
17079         
17080         if (typeof(pan) == 'string') {
17081             pan = this.getPanelByName(pan);
17082         }
17083         
17084         var cur = this.getActivePanel();
17085         
17086         if(!pan || !cur){
17087             Roo.log('pan or acitve pan is undefined');
17088             return false;
17089         }
17090         
17091         if (pan.tabId == this.getActivePanel().tabId) {
17092             return true;
17093         }
17094         
17095         if (false === cur.fireEvent('beforedeactivate')) {
17096             return false;
17097         }
17098         
17099         if(this.bullets > 0 && !Roo.isTouch){
17100             this.setActiveBullet(this.indexOfPanel(pan));
17101         }
17102         
17103         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17104             
17105             this.transition = true;
17106             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17107             var lr = dir == 'next' ? 'left' : 'right';
17108             pan.el.addClass(dir); // or prev
17109             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17110             cur.el.addClass(lr); // or right
17111             pan.el.addClass(lr);
17112             
17113             var _this = this;
17114             cur.el.on('transitionend', function() {
17115                 Roo.log("trans end?");
17116                 
17117                 pan.el.removeClass([lr,dir]);
17118                 pan.setActive(true);
17119                 
17120                 cur.el.removeClass([lr]);
17121                 cur.setActive(false);
17122                 
17123                 _this.transition = false;
17124                 
17125             }, this, { single:  true } );
17126             
17127             return true;
17128         }
17129         
17130         cur.setActive(false);
17131         pan.setActive(true);
17132         
17133         return true;
17134         
17135     },
17136     showPanelNext : function()
17137     {
17138         var i = this.indexOfPanel(this.getActivePanel());
17139         
17140         if (i >= this.tabs.length - 1 && !this.autoslide) {
17141             return;
17142         }
17143         
17144         if (i >= this.tabs.length - 1 && this.autoslide) {
17145             i = -1;
17146         }
17147         
17148         this.showPanel(this.tabs[i+1]);
17149     },
17150     
17151     showPanelPrev : function()
17152     {
17153         var i = this.indexOfPanel(this.getActivePanel());
17154         
17155         if (i  < 1 && !this.autoslide) {
17156             return;
17157         }
17158         
17159         if (i < 1 && this.autoslide) {
17160             i = this.tabs.length;
17161         }
17162         
17163         this.showPanel(this.tabs[i-1]);
17164     },
17165     
17166     
17167     addBullet: function()
17168     {
17169         if(!this.bullets || Roo.isTouch){
17170             return;
17171         }
17172         var ctr = this.el.select('.carousel-bullets',true).first();
17173         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17174         var bullet = ctr.createChild({
17175             cls : 'bullet bullet-' + i
17176         },ctr.dom.lastChild);
17177         
17178         
17179         var _this = this;
17180         
17181         bullet.on('click', (function(e, el, o, ii, t){
17182
17183             e.preventDefault();
17184
17185             this.showPanel(ii);
17186
17187             if(this.autoslide && this.slideFn){
17188                 clearInterval(this.slideFn);
17189                 this.slideFn = window.setInterval(function() {
17190                     _this.showPanelNext();
17191                 }, this.timer);
17192             }
17193
17194         }).createDelegate(this, [i, bullet], true));
17195                 
17196         
17197     },
17198      
17199     setActiveBullet : function(i)
17200     {
17201         if(Roo.isTouch){
17202             return;
17203         }
17204         
17205         Roo.each(this.el.select('.bullet', true).elements, function(el){
17206             el.removeClass('selected');
17207         });
17208
17209         var bullet = this.el.select('.bullet-' + i, true).first();
17210         
17211         if(!bullet){
17212             return;
17213         }
17214         
17215         bullet.addClass('selected');
17216     }
17217     
17218     
17219   
17220 });
17221
17222  
17223
17224  
17225  
17226 Roo.apply(Roo.bootstrap.TabGroup, {
17227     
17228     groups: {},
17229      /**
17230     * register a Navigation Group
17231     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17232     */
17233     register : function(navgrp)
17234     {
17235         this.groups[navgrp.navId] = navgrp;
17236         
17237     },
17238     /**
17239     * fetch a Navigation Group based on the navigation ID
17240     * if one does not exist , it will get created.
17241     * @param {string} the navgroup to add
17242     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17243     */
17244     get: function(navId) {
17245         if (typeof(this.groups[navId]) == 'undefined') {
17246             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17247         }
17248         return this.groups[navId] ;
17249     }
17250     
17251     
17252     
17253 });
17254
17255  /*
17256  * - LGPL
17257  *
17258  * TabPanel
17259  * 
17260  */
17261
17262 /**
17263  * @class Roo.bootstrap.TabPanel
17264  * @extends Roo.bootstrap.Component
17265  * Bootstrap TabPanel class
17266  * @cfg {Boolean} active panel active
17267  * @cfg {String} html panel content
17268  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17269  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17270  * @cfg {String} href click to link..
17271  * 
17272  * 
17273  * @constructor
17274  * Create a new TabPanel
17275  * @param {Object} config The config object
17276  */
17277
17278 Roo.bootstrap.TabPanel = function(config){
17279     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17280     this.addEvents({
17281         /**
17282              * @event changed
17283              * Fires when the active status changes
17284              * @param {Roo.bootstrap.TabPanel} this
17285              * @param {Boolean} state the new state
17286             
17287          */
17288         'changed': true,
17289         /**
17290              * @event beforedeactivate
17291              * Fires before a tab is de-activated - can be used to do validation on a form.
17292              * @param {Roo.bootstrap.TabPanel} this
17293              * @return {Boolean} false if there is an error
17294             
17295          */
17296         'beforedeactivate': true
17297      });
17298     
17299     this.tabId = this.tabId || Roo.id();
17300   
17301 };
17302
17303 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17304     
17305     active: false,
17306     html: false,
17307     tabId: false,
17308     navId : false,
17309     href : '',
17310     
17311     getAutoCreate : function(){
17312         var cfg = {
17313             tag: 'div',
17314             // item is needed for carousel - not sure if it has any effect otherwise
17315             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17316             html: this.html || ''
17317         };
17318         
17319         if(this.active){
17320             cfg.cls += ' active';
17321         }
17322         
17323         if(this.tabId){
17324             cfg.tabId = this.tabId;
17325         }
17326         
17327         
17328         return cfg;
17329     },
17330     
17331     initEvents:  function()
17332     {
17333         var p = this.parent();
17334         
17335         this.navId = this.navId || p.navId;
17336         
17337         if (typeof(this.navId) != 'undefined') {
17338             // not really needed.. but just in case.. parent should be a NavGroup.
17339             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17340             
17341             tg.register(this);
17342             
17343             var i = tg.tabs.length - 1;
17344             
17345             if(this.active && tg.bullets > 0 && i < tg.bullets){
17346                 tg.setActiveBullet(i);
17347             }
17348         }
17349         
17350         this.el.on('click', this.onClick, this);
17351         
17352         if(Roo.isTouch){
17353             this.el.on("touchstart", this.onTouchStart, this);
17354             this.el.on("touchmove", this.onTouchMove, this);
17355             this.el.on("touchend", this.onTouchEnd, this);
17356         }
17357         
17358     },
17359     
17360     onRender : function(ct, position)
17361     {
17362         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17363     },
17364     
17365     setActive : function(state)
17366     {
17367         Roo.log("panel - set active " + this.tabId + "=" + state);
17368         
17369         this.active = state;
17370         if (!state) {
17371             this.el.removeClass('active');
17372             
17373         } else  if (!this.el.hasClass('active')) {
17374             this.el.addClass('active');
17375         }
17376         
17377         this.fireEvent('changed', this, state);
17378     },
17379     
17380     onClick : function(e)
17381     {
17382         e.preventDefault();
17383         
17384         if(!this.href.length){
17385             return;
17386         }
17387         
17388         window.location.href = this.href;
17389     },
17390     
17391     startX : 0,
17392     startY : 0,
17393     endX : 0,
17394     endY : 0,
17395     swiping : false,
17396     
17397     onTouchStart : function(e)
17398     {
17399         this.swiping = false;
17400         
17401         this.startX = e.browserEvent.touches[0].clientX;
17402         this.startY = e.browserEvent.touches[0].clientY;
17403     },
17404     
17405     onTouchMove : function(e)
17406     {
17407         this.swiping = true;
17408         
17409         this.endX = e.browserEvent.touches[0].clientX;
17410         this.endY = e.browserEvent.touches[0].clientY;
17411     },
17412     
17413     onTouchEnd : function(e)
17414     {
17415         if(!this.swiping){
17416             this.onClick(e);
17417             return;
17418         }
17419         
17420         var tabGroup = this.parent();
17421         
17422         if(this.endX > this.startX){ // swiping right
17423             tabGroup.showPanelPrev();
17424             return;
17425         }
17426         
17427         if(this.startX > this.endX){ // swiping left
17428             tabGroup.showPanelNext();
17429             return;
17430         }
17431     }
17432     
17433     
17434 });
17435  
17436
17437  
17438
17439  /*
17440  * - LGPL
17441  *
17442  * DateField
17443  * 
17444  */
17445
17446 /**
17447  * @class Roo.bootstrap.DateField
17448  * @extends Roo.bootstrap.Input
17449  * Bootstrap DateField class
17450  * @cfg {Number} weekStart default 0
17451  * @cfg {String} viewMode default empty, (months|years)
17452  * @cfg {String} minViewMode default empty, (months|years)
17453  * @cfg {Number} startDate default -Infinity
17454  * @cfg {Number} endDate default Infinity
17455  * @cfg {Boolean} todayHighlight default false
17456  * @cfg {Boolean} todayBtn default false
17457  * @cfg {Boolean} calendarWeeks default false
17458  * @cfg {Object} daysOfWeekDisabled default empty
17459  * @cfg {Boolean} singleMode default false (true | false)
17460  * 
17461  * @cfg {Boolean} keyboardNavigation default true
17462  * @cfg {String} language default en
17463  * 
17464  * @constructor
17465  * Create a new DateField
17466  * @param {Object} config The config object
17467  */
17468
17469 Roo.bootstrap.DateField = function(config){
17470     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
17471      this.addEvents({
17472             /**
17473              * @event show
17474              * Fires when this field show.
17475              * @param {Roo.bootstrap.DateField} this
17476              * @param {Mixed} date The date value
17477              */
17478             show : true,
17479             /**
17480              * @event show
17481              * Fires when this field hide.
17482              * @param {Roo.bootstrap.DateField} this
17483              * @param {Mixed} date The date value
17484              */
17485             hide : true,
17486             /**
17487              * @event select
17488              * Fires when select a date.
17489              * @param {Roo.bootstrap.DateField} this
17490              * @param {Mixed} date The date value
17491              */
17492             select : true,
17493             /**
17494              * @event beforeselect
17495              * Fires when before select a date.
17496              * @param {Roo.bootstrap.DateField} this
17497              * @param {Mixed} date The date value
17498              */
17499             beforeselect : true
17500         });
17501 };
17502
17503 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
17504     
17505     /**
17506      * @cfg {String} format
17507      * The default date format string which can be overriden for localization support.  The format must be
17508      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17509      */
17510     format : "m/d/y",
17511     /**
17512      * @cfg {String} altFormats
17513      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17514      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17515      */
17516     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17517     
17518     weekStart : 0,
17519     
17520     viewMode : '',
17521     
17522     minViewMode : '',
17523     
17524     todayHighlight : false,
17525     
17526     todayBtn: false,
17527     
17528     language: 'en',
17529     
17530     keyboardNavigation: true,
17531     
17532     calendarWeeks: false,
17533     
17534     startDate: -Infinity,
17535     
17536     endDate: Infinity,
17537     
17538     daysOfWeekDisabled: [],
17539     
17540     _events: [],
17541     
17542     singleMode : false,
17543     
17544     UTCDate: function()
17545     {
17546         return new Date(Date.UTC.apply(Date, arguments));
17547     },
17548     
17549     UTCToday: function()
17550     {
17551         var today = new Date();
17552         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
17553     },
17554     
17555     getDate: function() {
17556             var d = this.getUTCDate();
17557             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
17558     },
17559     
17560     getUTCDate: function() {
17561             return this.date;
17562     },
17563     
17564     setDate: function(d) {
17565             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
17566     },
17567     
17568     setUTCDate: function(d) {
17569             this.date = d;
17570             this.setValue(this.formatDate(this.date));
17571     },
17572         
17573     onRender: function(ct, position)
17574     {
17575         
17576         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
17577         
17578         this.language = this.language || 'en';
17579         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
17580         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
17581         
17582         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17583         this.format = this.format || 'm/d/y';
17584         this.isInline = false;
17585         this.isInput = true;
17586         this.component = this.el.select('.add-on', true).first() || false;
17587         this.component = (this.component && this.component.length === 0) ? false : this.component;
17588         this.hasInput = this.component && this.inputEl().length;
17589         
17590         if (typeof(this.minViewMode === 'string')) {
17591             switch (this.minViewMode) {
17592                 case 'months':
17593                     this.minViewMode = 1;
17594                     break;
17595                 case 'years':
17596                     this.minViewMode = 2;
17597                     break;
17598                 default:
17599                     this.minViewMode = 0;
17600                     break;
17601             }
17602         }
17603         
17604         if (typeof(this.viewMode === 'string')) {
17605             switch (this.viewMode) {
17606                 case 'months':
17607                     this.viewMode = 1;
17608                     break;
17609                 case 'years':
17610                     this.viewMode = 2;
17611                     break;
17612                 default:
17613                     this.viewMode = 0;
17614                     break;
17615             }
17616         }
17617                 
17618         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
17619         
17620 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
17621         
17622         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17623         
17624         this.picker().on('mousedown', this.onMousedown, this);
17625         this.picker().on('click', this.onClick, this);
17626         
17627         this.picker().addClass('datepicker-dropdown');
17628         
17629         this.startViewMode = this.viewMode;
17630         
17631         if(this.singleMode){
17632             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17633                 v.setVisibilityMode(Roo.Element.DISPLAY);
17634                 v.hide();
17635             });
17636             
17637             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17638                 v.setStyle('width', '189px');
17639             });
17640         }
17641         
17642         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17643             if(!this.calendarWeeks){
17644                 v.remove();
17645                 return;
17646             }
17647             
17648             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17649             v.attr('colspan', function(i, val){
17650                 return parseInt(val) + 1;
17651             });
17652         });
17653                         
17654         
17655         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17656         
17657         this.setStartDate(this.startDate);
17658         this.setEndDate(this.endDate);
17659         
17660         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17661         
17662         this.fillDow();
17663         this.fillMonths();
17664         this.update();
17665         this.showMode();
17666         
17667         if(this.isInline) {
17668             this.show();
17669         }
17670     },
17671     
17672     picker : function()
17673     {
17674         return this.pickerEl;
17675 //        return this.el.select('.datepicker', true).first();
17676     },
17677     
17678     fillDow: function()
17679     {
17680         var dowCnt = this.weekStart;
17681         
17682         var dow = {
17683             tag: 'tr',
17684             cn: [
17685                 
17686             ]
17687         };
17688         
17689         if(this.calendarWeeks){
17690             dow.cn.push({
17691                 tag: 'th',
17692                 cls: 'cw',
17693                 html: '&nbsp;'
17694             })
17695         }
17696         
17697         while (dowCnt < this.weekStart + 7) {
17698             dow.cn.push({
17699                 tag: 'th',
17700                 cls: 'dow',
17701                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17702             });
17703         }
17704         
17705         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17706     },
17707     
17708     fillMonths: function()
17709     {    
17710         var i = 0;
17711         var months = this.picker().select('>.datepicker-months td', true).first();
17712         
17713         months.dom.innerHTML = '';
17714         
17715         while (i < 12) {
17716             var month = {
17717                 tag: 'span',
17718                 cls: 'month',
17719                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17720             };
17721             
17722             months.createChild(month);
17723         }
17724         
17725     },
17726     
17727     update: function()
17728     {
17729         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;
17730         
17731         if (this.date < this.startDate) {
17732             this.viewDate = new Date(this.startDate);
17733         } else if (this.date > this.endDate) {
17734             this.viewDate = new Date(this.endDate);
17735         } else {
17736             this.viewDate = new Date(this.date);
17737         }
17738         
17739         this.fill();
17740     },
17741     
17742     fill: function() 
17743     {
17744         var d = new Date(this.viewDate),
17745                 year = d.getUTCFullYear(),
17746                 month = d.getUTCMonth(),
17747                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17748                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17749                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17750                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17751                 currentDate = this.date && this.date.valueOf(),
17752                 today = this.UTCToday();
17753         
17754         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17755         
17756 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17757         
17758 //        this.picker.select('>tfoot th.today').
17759 //                                              .text(dates[this.language].today)
17760 //                                              .toggle(this.todayBtn !== false);
17761     
17762         this.updateNavArrows();
17763         this.fillMonths();
17764                                                 
17765         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17766         
17767         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17768          
17769         prevMonth.setUTCDate(day);
17770         
17771         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17772         
17773         var nextMonth = new Date(prevMonth);
17774         
17775         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17776         
17777         nextMonth = nextMonth.valueOf();
17778         
17779         var fillMonths = false;
17780         
17781         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17782         
17783         while(prevMonth.valueOf() < nextMonth) {
17784             var clsName = '';
17785             
17786             if (prevMonth.getUTCDay() === this.weekStart) {
17787                 if(fillMonths){
17788                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17789                 }
17790                     
17791                 fillMonths = {
17792                     tag: 'tr',
17793                     cn: []
17794                 };
17795                 
17796                 if(this.calendarWeeks){
17797                     // ISO 8601: First week contains first thursday.
17798                     // ISO also states week starts on Monday, but we can be more abstract here.
17799                     var
17800                     // Start of current week: based on weekstart/current date
17801                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17802                     // Thursday of this week
17803                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17804                     // First Thursday of year, year from thursday
17805                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17806                     // Calendar week: ms between thursdays, div ms per day, div 7 days
17807                     calWeek =  (th - yth) / 864e5 / 7 + 1;
17808                     
17809                     fillMonths.cn.push({
17810                         tag: 'td',
17811                         cls: 'cw',
17812                         html: calWeek
17813                     });
17814                 }
17815             }
17816             
17817             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17818                 clsName += ' old';
17819             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17820                 clsName += ' new';
17821             }
17822             if (this.todayHighlight &&
17823                 prevMonth.getUTCFullYear() == today.getFullYear() &&
17824                 prevMonth.getUTCMonth() == today.getMonth() &&
17825                 prevMonth.getUTCDate() == today.getDate()) {
17826                 clsName += ' today';
17827             }
17828             
17829             if (currentDate && prevMonth.valueOf() === currentDate) {
17830                 clsName += ' active';
17831             }
17832             
17833             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17834                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17835                     clsName += ' disabled';
17836             }
17837             
17838             fillMonths.cn.push({
17839                 tag: 'td',
17840                 cls: 'day ' + clsName,
17841                 html: prevMonth.getDate()
17842             });
17843             
17844             prevMonth.setDate(prevMonth.getDate()+1);
17845         }
17846           
17847         var currentYear = this.date && this.date.getUTCFullYear();
17848         var currentMonth = this.date && this.date.getUTCMonth();
17849         
17850         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17851         
17852         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17853             v.removeClass('active');
17854             
17855             if(currentYear === year && k === currentMonth){
17856                 v.addClass('active');
17857             }
17858             
17859             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17860                 v.addClass('disabled');
17861             }
17862             
17863         });
17864         
17865         
17866         year = parseInt(year/10, 10) * 10;
17867         
17868         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17869         
17870         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17871         
17872         year -= 1;
17873         for (var i = -1; i < 11; i++) {
17874             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17875                 tag: 'span',
17876                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17877                 html: year
17878             });
17879             
17880             year += 1;
17881         }
17882     },
17883     
17884     showMode: function(dir) 
17885     {
17886         if (dir) {
17887             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17888         }
17889         
17890         Roo.each(this.picker().select('>div',true).elements, function(v){
17891             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17892             v.hide();
17893         });
17894         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17895     },
17896     
17897     place: function()
17898     {
17899         if(this.isInline) {
17900             return;
17901         }
17902         
17903         this.picker().removeClass(['bottom', 'top']);
17904         
17905         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17906             /*
17907              * place to the top of element!
17908              *
17909              */
17910             
17911             this.picker().addClass('top');
17912             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17913             
17914             return;
17915         }
17916         
17917         this.picker().addClass('bottom');
17918         
17919         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17920     },
17921     
17922     parseDate : function(value)
17923     {
17924         if(!value || value instanceof Date){
17925             return value;
17926         }
17927         var v = Date.parseDate(value, this.format);
17928         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17929             v = Date.parseDate(value, 'Y-m-d');
17930         }
17931         if(!v && this.altFormats){
17932             if(!this.altFormatsArray){
17933                 this.altFormatsArray = this.altFormats.split("|");
17934             }
17935             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17936                 v = Date.parseDate(value, this.altFormatsArray[i]);
17937             }
17938         }
17939         return v;
17940     },
17941     
17942     formatDate : function(date, fmt)
17943     {   
17944         return (!date || !(date instanceof Date)) ?
17945         date : date.dateFormat(fmt || this.format);
17946     },
17947     
17948     onFocus : function()
17949     {
17950         Roo.bootstrap.DateField.superclass.onFocus.call(this);
17951         this.show();
17952     },
17953     
17954     onBlur : function()
17955     {
17956         Roo.bootstrap.DateField.superclass.onBlur.call(this);
17957         
17958         var d = this.inputEl().getValue();
17959         
17960         this.setValue(d);
17961                 
17962         this.hide();
17963     },
17964     
17965     show : function()
17966     {
17967         this.picker().show();
17968         this.update();
17969         this.place();
17970         
17971         this.fireEvent('show', this, this.date);
17972     },
17973     
17974     hide : function()
17975     {
17976         if(this.isInline) {
17977             return;
17978         }
17979         this.picker().hide();
17980         this.viewMode = this.startViewMode;
17981         this.showMode();
17982         
17983         this.fireEvent('hide', this, this.date);
17984         
17985     },
17986     
17987     onMousedown: function(e)
17988     {
17989         e.stopPropagation();
17990         e.preventDefault();
17991     },
17992     
17993     keyup: function(e)
17994     {
17995         Roo.bootstrap.DateField.superclass.keyup.call(this);
17996         this.update();
17997     },
17998
17999     setValue: function(v)
18000     {
18001         if(this.fireEvent('beforeselect', this, v) !== false){
18002             var d = new Date(this.parseDate(v) ).clearTime();
18003         
18004             if(isNaN(d.getTime())){
18005                 this.date = this.viewDate = '';
18006                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18007                 return;
18008             }
18009
18010             v = this.formatDate(d);
18011
18012             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18013
18014             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18015
18016             this.update();
18017
18018             this.fireEvent('select', this, this.date);
18019         }
18020     },
18021     
18022     getValue: function()
18023     {
18024         return this.formatDate(this.date);
18025     },
18026     
18027     fireKey: function(e)
18028     {
18029         if (!this.picker().isVisible()){
18030             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18031                 this.show();
18032             }
18033             return;
18034         }
18035         
18036         var dateChanged = false,
18037         dir, day, month,
18038         newDate, newViewDate;
18039         
18040         switch(e.keyCode){
18041             case 27: // escape
18042                 this.hide();
18043                 e.preventDefault();
18044                 break;
18045             case 37: // left
18046             case 39: // right
18047                 if (!this.keyboardNavigation) {
18048                     break;
18049                 }
18050                 dir = e.keyCode == 37 ? -1 : 1;
18051                 
18052                 if (e.ctrlKey){
18053                     newDate = this.moveYear(this.date, dir);
18054                     newViewDate = this.moveYear(this.viewDate, dir);
18055                 } else if (e.shiftKey){
18056                     newDate = this.moveMonth(this.date, dir);
18057                     newViewDate = this.moveMonth(this.viewDate, dir);
18058                 } else {
18059                     newDate = new Date(this.date);
18060                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18061                     newViewDate = new Date(this.viewDate);
18062                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18063                 }
18064                 if (this.dateWithinRange(newDate)){
18065                     this.date = newDate;
18066                     this.viewDate = newViewDate;
18067                     this.setValue(this.formatDate(this.date));
18068 //                    this.update();
18069                     e.preventDefault();
18070                     dateChanged = true;
18071                 }
18072                 break;
18073             case 38: // up
18074             case 40: // down
18075                 if (!this.keyboardNavigation) {
18076                     break;
18077                 }
18078                 dir = e.keyCode == 38 ? -1 : 1;
18079                 if (e.ctrlKey){
18080                     newDate = this.moveYear(this.date, dir);
18081                     newViewDate = this.moveYear(this.viewDate, dir);
18082                 } else if (e.shiftKey){
18083                     newDate = this.moveMonth(this.date, dir);
18084                     newViewDate = this.moveMonth(this.viewDate, dir);
18085                 } else {
18086                     newDate = new Date(this.date);
18087                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18088                     newViewDate = new Date(this.viewDate);
18089                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18090                 }
18091                 if (this.dateWithinRange(newDate)){
18092                     this.date = newDate;
18093                     this.viewDate = newViewDate;
18094                     this.setValue(this.formatDate(this.date));
18095 //                    this.update();
18096                     e.preventDefault();
18097                     dateChanged = true;
18098                 }
18099                 break;
18100             case 13: // enter
18101                 this.setValue(this.formatDate(this.date));
18102                 this.hide();
18103                 e.preventDefault();
18104                 break;
18105             case 9: // tab
18106                 this.setValue(this.formatDate(this.date));
18107                 this.hide();
18108                 break;
18109             case 16: // shift
18110             case 17: // ctrl
18111             case 18: // alt
18112                 break;
18113             default :
18114                 this.hide();
18115                 
18116         }
18117     },
18118     
18119     
18120     onClick: function(e) 
18121     {
18122         e.stopPropagation();
18123         e.preventDefault();
18124         
18125         var target = e.getTarget();
18126         
18127         if(target.nodeName.toLowerCase() === 'i'){
18128             target = Roo.get(target).dom.parentNode;
18129         }
18130         
18131         var nodeName = target.nodeName;
18132         var className = target.className;
18133         var html = target.innerHTML;
18134         //Roo.log(nodeName);
18135         
18136         switch(nodeName.toLowerCase()) {
18137             case 'th':
18138                 switch(className) {
18139                     case 'switch':
18140                         this.showMode(1);
18141                         break;
18142                     case 'prev':
18143                     case 'next':
18144                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18145                         switch(this.viewMode){
18146                                 case 0:
18147                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18148                                         break;
18149                                 case 1:
18150                                 case 2:
18151                                         this.viewDate = this.moveYear(this.viewDate, dir);
18152                                         break;
18153                         }
18154                         this.fill();
18155                         break;
18156                     case 'today':
18157                         var date = new Date();
18158                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18159 //                        this.fill()
18160                         this.setValue(this.formatDate(this.date));
18161                         
18162                         this.hide();
18163                         break;
18164                 }
18165                 break;
18166             case 'span':
18167                 if (className.indexOf('disabled') < 0) {
18168                     this.viewDate.setUTCDate(1);
18169                     if (className.indexOf('month') > -1) {
18170                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18171                     } else {
18172                         var year = parseInt(html, 10) || 0;
18173                         this.viewDate.setUTCFullYear(year);
18174                         
18175                     }
18176                     
18177                     if(this.singleMode){
18178                         this.setValue(this.formatDate(this.viewDate));
18179                         this.hide();
18180                         return;
18181                     }
18182                     
18183                     this.showMode(-1);
18184                     this.fill();
18185                 }
18186                 break;
18187                 
18188             case 'td':
18189                 //Roo.log(className);
18190                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18191                     var day = parseInt(html, 10) || 1;
18192                     var year = this.viewDate.getUTCFullYear(),
18193                         month = this.viewDate.getUTCMonth();
18194
18195                     if (className.indexOf('old') > -1) {
18196                         if(month === 0 ){
18197                             month = 11;
18198                             year -= 1;
18199                         }else{
18200                             month -= 1;
18201                         }
18202                     } else if (className.indexOf('new') > -1) {
18203                         if (month == 11) {
18204                             month = 0;
18205                             year += 1;
18206                         } else {
18207                             month += 1;
18208                         }
18209                     }
18210                     //Roo.log([year,month,day]);
18211                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18212                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18213 //                    this.fill();
18214                     //Roo.log(this.formatDate(this.date));
18215                     this.setValue(this.formatDate(this.date));
18216                     this.hide();
18217                 }
18218                 break;
18219         }
18220     },
18221     
18222     setStartDate: function(startDate)
18223     {
18224         this.startDate = startDate || -Infinity;
18225         if (this.startDate !== -Infinity) {
18226             this.startDate = this.parseDate(this.startDate);
18227         }
18228         this.update();
18229         this.updateNavArrows();
18230     },
18231
18232     setEndDate: function(endDate)
18233     {
18234         this.endDate = endDate || Infinity;
18235         if (this.endDate !== Infinity) {
18236             this.endDate = this.parseDate(this.endDate);
18237         }
18238         this.update();
18239         this.updateNavArrows();
18240     },
18241     
18242     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18243     {
18244         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18245         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18246             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18247         }
18248         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18249             return parseInt(d, 10);
18250         });
18251         this.update();
18252         this.updateNavArrows();
18253     },
18254     
18255     updateNavArrows: function() 
18256     {
18257         if(this.singleMode){
18258             return;
18259         }
18260         
18261         var d = new Date(this.viewDate),
18262         year = d.getUTCFullYear(),
18263         month = d.getUTCMonth();
18264         
18265         Roo.each(this.picker().select('.prev', true).elements, function(v){
18266             v.show();
18267             switch (this.viewMode) {
18268                 case 0:
18269
18270                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18271                         v.hide();
18272                     }
18273                     break;
18274                 case 1:
18275                 case 2:
18276                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18277                         v.hide();
18278                     }
18279                     break;
18280             }
18281         });
18282         
18283         Roo.each(this.picker().select('.next', true).elements, function(v){
18284             v.show();
18285             switch (this.viewMode) {
18286                 case 0:
18287
18288                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18289                         v.hide();
18290                     }
18291                     break;
18292                 case 1:
18293                 case 2:
18294                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18295                         v.hide();
18296                     }
18297                     break;
18298             }
18299         })
18300     },
18301     
18302     moveMonth: function(date, dir)
18303     {
18304         if (!dir) {
18305             return date;
18306         }
18307         var new_date = new Date(date.valueOf()),
18308         day = new_date.getUTCDate(),
18309         month = new_date.getUTCMonth(),
18310         mag = Math.abs(dir),
18311         new_month, test;
18312         dir = dir > 0 ? 1 : -1;
18313         if (mag == 1){
18314             test = dir == -1
18315             // If going back one month, make sure month is not current month
18316             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18317             ? function(){
18318                 return new_date.getUTCMonth() == month;
18319             }
18320             // If going forward one month, make sure month is as expected
18321             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18322             : function(){
18323                 return new_date.getUTCMonth() != new_month;
18324             };
18325             new_month = month + dir;
18326             new_date.setUTCMonth(new_month);
18327             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18328             if (new_month < 0 || new_month > 11) {
18329                 new_month = (new_month + 12) % 12;
18330             }
18331         } else {
18332             // For magnitudes >1, move one month at a time...
18333             for (var i=0; i<mag; i++) {
18334                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18335                 new_date = this.moveMonth(new_date, dir);
18336             }
18337             // ...then reset the day, keeping it in the new month
18338             new_month = new_date.getUTCMonth();
18339             new_date.setUTCDate(day);
18340             test = function(){
18341                 return new_month != new_date.getUTCMonth();
18342             };
18343         }
18344         // Common date-resetting loop -- if date is beyond end of month, make it
18345         // end of month
18346         while (test()){
18347             new_date.setUTCDate(--day);
18348             new_date.setUTCMonth(new_month);
18349         }
18350         return new_date;
18351     },
18352
18353     moveYear: function(date, dir)
18354     {
18355         return this.moveMonth(date, dir*12);
18356     },
18357
18358     dateWithinRange: function(date)
18359     {
18360         return date >= this.startDate && date <= this.endDate;
18361     },
18362
18363     
18364     remove: function() 
18365     {
18366         this.picker().remove();
18367     },
18368     
18369     validateValue : function(value)
18370     {
18371         if(value.length < 1)  {
18372             if(this.allowBlank){
18373                 return true;
18374             }
18375             return false;
18376         }
18377         
18378         if(value.length < this.minLength){
18379             return false;
18380         }
18381         if(value.length > this.maxLength){
18382             return false;
18383         }
18384         if(this.vtype){
18385             var vt = Roo.form.VTypes;
18386             if(!vt[this.vtype](value, this)){
18387                 return false;
18388             }
18389         }
18390         if(typeof this.validator == "function"){
18391             var msg = this.validator(value);
18392             if(msg !== true){
18393                 return false;
18394             }
18395         }
18396         
18397         if(this.regex && !this.regex.test(value)){
18398             return false;
18399         }
18400         
18401         if(typeof(this.parseDate(value)) == 'undefined'){
18402             return false;
18403         }
18404         
18405         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
18406             return false;
18407         }      
18408         
18409         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
18410             return false;
18411         } 
18412         
18413         
18414         return true;
18415     }
18416    
18417 });
18418
18419 Roo.apply(Roo.bootstrap.DateField,  {
18420     
18421     head : {
18422         tag: 'thead',
18423         cn: [
18424         {
18425             tag: 'tr',
18426             cn: [
18427             {
18428                 tag: 'th',
18429                 cls: 'prev',
18430                 html: '<i class="fa fa-arrow-left"/>'
18431             },
18432             {
18433                 tag: 'th',
18434                 cls: 'switch',
18435                 colspan: '5'
18436             },
18437             {
18438                 tag: 'th',
18439                 cls: 'next',
18440                 html: '<i class="fa fa-arrow-right"/>'
18441             }
18442
18443             ]
18444         }
18445         ]
18446     },
18447     
18448     content : {
18449         tag: 'tbody',
18450         cn: [
18451         {
18452             tag: 'tr',
18453             cn: [
18454             {
18455                 tag: 'td',
18456                 colspan: '7'
18457             }
18458             ]
18459         }
18460         ]
18461     },
18462     
18463     footer : {
18464         tag: 'tfoot',
18465         cn: [
18466         {
18467             tag: 'tr',
18468             cn: [
18469             {
18470                 tag: 'th',
18471                 colspan: '7',
18472                 cls: 'today'
18473             }
18474                     
18475             ]
18476         }
18477         ]
18478     },
18479     
18480     dates:{
18481         en: {
18482             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
18483             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
18484             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
18485             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18486             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
18487             today: "Today"
18488         }
18489     },
18490     
18491     modes: [
18492     {
18493         clsName: 'days',
18494         navFnc: 'Month',
18495         navStep: 1
18496     },
18497     {
18498         clsName: 'months',
18499         navFnc: 'FullYear',
18500         navStep: 1
18501     },
18502     {
18503         clsName: 'years',
18504         navFnc: 'FullYear',
18505         navStep: 10
18506     }]
18507 });
18508
18509 Roo.apply(Roo.bootstrap.DateField,  {
18510   
18511     template : {
18512         tag: 'div',
18513         cls: 'datepicker dropdown-menu roo-dynamic',
18514         cn: [
18515         {
18516             tag: 'div',
18517             cls: 'datepicker-days',
18518             cn: [
18519             {
18520                 tag: 'table',
18521                 cls: 'table-condensed',
18522                 cn:[
18523                 Roo.bootstrap.DateField.head,
18524                 {
18525                     tag: 'tbody'
18526                 },
18527                 Roo.bootstrap.DateField.footer
18528                 ]
18529             }
18530             ]
18531         },
18532         {
18533             tag: 'div',
18534             cls: 'datepicker-months',
18535             cn: [
18536             {
18537                 tag: 'table',
18538                 cls: 'table-condensed',
18539                 cn:[
18540                 Roo.bootstrap.DateField.head,
18541                 Roo.bootstrap.DateField.content,
18542                 Roo.bootstrap.DateField.footer
18543                 ]
18544             }
18545             ]
18546         },
18547         {
18548             tag: 'div',
18549             cls: 'datepicker-years',
18550             cn: [
18551             {
18552                 tag: 'table',
18553                 cls: 'table-condensed',
18554                 cn:[
18555                 Roo.bootstrap.DateField.head,
18556                 Roo.bootstrap.DateField.content,
18557                 Roo.bootstrap.DateField.footer
18558                 ]
18559             }
18560             ]
18561         }
18562         ]
18563     }
18564 });
18565
18566  
18567
18568  /*
18569  * - LGPL
18570  *
18571  * TimeField
18572  * 
18573  */
18574
18575 /**
18576  * @class Roo.bootstrap.TimeField
18577  * @extends Roo.bootstrap.Input
18578  * Bootstrap DateField class
18579  * 
18580  * 
18581  * @constructor
18582  * Create a new TimeField
18583  * @param {Object} config The config object
18584  */
18585
18586 Roo.bootstrap.TimeField = function(config){
18587     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
18588     this.addEvents({
18589             /**
18590              * @event show
18591              * Fires when this field show.
18592              * @param {Roo.bootstrap.DateField} thisthis
18593              * @param {Mixed} date The date value
18594              */
18595             show : true,
18596             /**
18597              * @event show
18598              * Fires when this field hide.
18599              * @param {Roo.bootstrap.DateField} this
18600              * @param {Mixed} date The date value
18601              */
18602             hide : true,
18603             /**
18604              * @event select
18605              * Fires when select a date.
18606              * @param {Roo.bootstrap.DateField} this
18607              * @param {Mixed} date The date value
18608              */
18609             select : true
18610         });
18611 };
18612
18613 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
18614     
18615     /**
18616      * @cfg {String} format
18617      * The default time format string which can be overriden for localization support.  The format must be
18618      * valid according to {@link Date#parseDate} (defaults to 'H:i').
18619      */
18620     format : "H:i",
18621        
18622     onRender: function(ct, position)
18623     {
18624         
18625         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
18626                 
18627         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
18628         
18629         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18630         
18631         this.pop = this.picker().select('>.datepicker-time',true).first();
18632         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18633         
18634         this.picker().on('mousedown', this.onMousedown, this);
18635         this.picker().on('click', this.onClick, this);
18636         
18637         this.picker().addClass('datepicker-dropdown');
18638     
18639         this.fillTime();
18640         this.update();
18641             
18642         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
18643         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
18644         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
18645         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
18646         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
18647         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
18648
18649     },
18650     
18651     fireKey: function(e){
18652         if (!this.picker().isVisible()){
18653             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18654                 this.show();
18655             }
18656             return;
18657         }
18658
18659         e.preventDefault();
18660         
18661         switch(e.keyCode){
18662             case 27: // escape
18663                 this.hide();
18664                 break;
18665             case 37: // left
18666             case 39: // right
18667                 this.onTogglePeriod();
18668                 break;
18669             case 38: // up
18670                 this.onIncrementMinutes();
18671                 break;
18672             case 40: // down
18673                 this.onDecrementMinutes();
18674                 break;
18675             case 13: // enter
18676             case 9: // tab
18677                 this.setTime();
18678                 break;
18679         }
18680     },
18681     
18682     onClick: function(e) {
18683         e.stopPropagation();
18684         e.preventDefault();
18685     },
18686     
18687     picker : function()
18688     {
18689         return this.el.select('.datepicker', true).first();
18690     },
18691     
18692     fillTime: function()
18693     {    
18694         var time = this.pop.select('tbody', true).first();
18695         
18696         time.dom.innerHTML = '';
18697         
18698         time.createChild({
18699             tag: 'tr',
18700             cn: [
18701                 {
18702                     tag: 'td',
18703                     cn: [
18704                         {
18705                             tag: 'a',
18706                             href: '#',
18707                             cls: 'btn',
18708                             cn: [
18709                                 {
18710                                     tag: 'span',
18711                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
18712                                 }
18713                             ]
18714                         } 
18715                     ]
18716                 },
18717                 {
18718                     tag: 'td',
18719                     cls: 'separator'
18720                 },
18721                 {
18722                     tag: 'td',
18723                     cn: [
18724                         {
18725                             tag: 'a',
18726                             href: '#',
18727                             cls: 'btn',
18728                             cn: [
18729                                 {
18730                                     tag: 'span',
18731                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
18732                                 }
18733                             ]
18734                         }
18735                     ]
18736                 },
18737                 {
18738                     tag: 'td',
18739                     cls: 'separator'
18740                 }
18741             ]
18742         });
18743         
18744         time.createChild({
18745             tag: 'tr',
18746             cn: [
18747                 {
18748                     tag: 'td',
18749                     cn: [
18750                         {
18751                             tag: 'span',
18752                             cls: 'timepicker-hour',
18753                             html: '00'
18754                         }  
18755                     ]
18756                 },
18757                 {
18758                     tag: 'td',
18759                     cls: 'separator',
18760                     html: ':'
18761                 },
18762                 {
18763                     tag: 'td',
18764                     cn: [
18765                         {
18766                             tag: 'span',
18767                             cls: 'timepicker-minute',
18768                             html: '00'
18769                         }  
18770                     ]
18771                 },
18772                 {
18773                     tag: 'td',
18774                     cls: 'separator'
18775                 },
18776                 {
18777                     tag: 'td',
18778                     cn: [
18779                         {
18780                             tag: 'button',
18781                             type: 'button',
18782                             cls: 'btn btn-primary period',
18783                             html: 'AM'
18784                             
18785                         }
18786                     ]
18787                 }
18788             ]
18789         });
18790         
18791         time.createChild({
18792             tag: 'tr',
18793             cn: [
18794                 {
18795                     tag: 'td',
18796                     cn: [
18797                         {
18798                             tag: 'a',
18799                             href: '#',
18800                             cls: 'btn',
18801                             cn: [
18802                                 {
18803                                     tag: 'span',
18804                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
18805                                 }
18806                             ]
18807                         }
18808                     ]
18809                 },
18810                 {
18811                     tag: 'td',
18812                     cls: 'separator'
18813                 },
18814                 {
18815                     tag: 'td',
18816                     cn: [
18817                         {
18818                             tag: 'a',
18819                             href: '#',
18820                             cls: 'btn',
18821                             cn: [
18822                                 {
18823                                     tag: 'span',
18824                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
18825                                 }
18826                             ]
18827                         }
18828                     ]
18829                 },
18830                 {
18831                     tag: 'td',
18832                     cls: 'separator'
18833                 }
18834             ]
18835         });
18836         
18837     },
18838     
18839     update: function()
18840     {
18841         
18842         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18843         
18844         this.fill();
18845     },
18846     
18847     fill: function() 
18848     {
18849         var hours = this.time.getHours();
18850         var minutes = this.time.getMinutes();
18851         var period = 'AM';
18852         
18853         if(hours > 11){
18854             period = 'PM';
18855         }
18856         
18857         if(hours == 0){
18858             hours = 12;
18859         }
18860         
18861         
18862         if(hours > 12){
18863             hours = hours - 12;
18864         }
18865         
18866         if(hours < 10){
18867             hours = '0' + hours;
18868         }
18869         
18870         if(minutes < 10){
18871             minutes = '0' + minutes;
18872         }
18873         
18874         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18875         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18876         this.pop.select('button', true).first().dom.innerHTML = period;
18877         
18878     },
18879     
18880     place: function()
18881     {   
18882         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18883         
18884         var cls = ['bottom'];
18885         
18886         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18887             cls.pop();
18888             cls.push('top');
18889         }
18890         
18891         cls.push('right');
18892         
18893         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18894             cls.pop();
18895             cls.push('left');
18896         }
18897         
18898         this.picker().addClass(cls.join('-'));
18899         
18900         var _this = this;
18901         
18902         Roo.each(cls, function(c){
18903             if(c == 'bottom'){
18904                 _this.picker().setTop(_this.inputEl().getHeight());
18905                 return;
18906             }
18907             if(c == 'top'){
18908                 _this.picker().setTop(0 - _this.picker().getHeight());
18909                 return;
18910             }
18911             
18912             if(c == 'left'){
18913                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18914                 return;
18915             }
18916             if(c == 'right'){
18917                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18918                 return;
18919             }
18920         });
18921         
18922     },
18923   
18924     onFocus : function()
18925     {
18926         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
18927         this.show();
18928     },
18929     
18930     onBlur : function()
18931     {
18932         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
18933         this.hide();
18934     },
18935     
18936     show : function()
18937     {
18938         this.picker().show();
18939         this.pop.show();
18940         this.update();
18941         this.place();
18942         
18943         this.fireEvent('show', this, this.date);
18944     },
18945     
18946     hide : function()
18947     {
18948         this.picker().hide();
18949         this.pop.hide();
18950         
18951         this.fireEvent('hide', this, this.date);
18952     },
18953     
18954     setTime : function()
18955     {
18956         this.hide();
18957         this.setValue(this.time.format(this.format));
18958         
18959         this.fireEvent('select', this, this.date);
18960         
18961         
18962     },
18963     
18964     onMousedown: function(e){
18965         e.stopPropagation();
18966         e.preventDefault();
18967     },
18968     
18969     onIncrementHours: function()
18970     {
18971         Roo.log('onIncrementHours');
18972         this.time = this.time.add(Date.HOUR, 1);
18973         this.update();
18974         
18975     },
18976     
18977     onDecrementHours: function()
18978     {
18979         Roo.log('onDecrementHours');
18980         this.time = this.time.add(Date.HOUR, -1);
18981         this.update();
18982     },
18983     
18984     onIncrementMinutes: function()
18985     {
18986         Roo.log('onIncrementMinutes');
18987         this.time = this.time.add(Date.MINUTE, 1);
18988         this.update();
18989     },
18990     
18991     onDecrementMinutes: function()
18992     {
18993         Roo.log('onDecrementMinutes');
18994         this.time = this.time.add(Date.MINUTE, -1);
18995         this.update();
18996     },
18997     
18998     onTogglePeriod: function()
18999     {
19000         Roo.log('onTogglePeriod');
19001         this.time = this.time.add(Date.HOUR, 12);
19002         this.update();
19003     }
19004     
19005    
19006 });
19007
19008 Roo.apply(Roo.bootstrap.TimeField,  {
19009     
19010     content : {
19011         tag: 'tbody',
19012         cn: [
19013             {
19014                 tag: 'tr',
19015                 cn: [
19016                 {
19017                     tag: 'td',
19018                     colspan: '7'
19019                 }
19020                 ]
19021             }
19022         ]
19023     },
19024     
19025     footer : {
19026         tag: 'tfoot',
19027         cn: [
19028             {
19029                 tag: 'tr',
19030                 cn: [
19031                 {
19032                     tag: 'th',
19033                     colspan: '7',
19034                     cls: '',
19035                     cn: [
19036                         {
19037                             tag: 'button',
19038                             cls: 'btn btn-info ok',
19039                             html: 'OK'
19040                         }
19041                     ]
19042                 }
19043
19044                 ]
19045             }
19046         ]
19047     }
19048 });
19049
19050 Roo.apply(Roo.bootstrap.TimeField,  {
19051   
19052     template : {
19053         tag: 'div',
19054         cls: 'datepicker dropdown-menu',
19055         cn: [
19056             {
19057                 tag: 'div',
19058                 cls: 'datepicker-time',
19059                 cn: [
19060                 {
19061                     tag: 'table',
19062                     cls: 'table-condensed',
19063                     cn:[
19064                     Roo.bootstrap.TimeField.content,
19065                     Roo.bootstrap.TimeField.footer
19066                     ]
19067                 }
19068                 ]
19069             }
19070         ]
19071     }
19072 });
19073
19074  
19075
19076  /*
19077  * - LGPL
19078  *
19079  * MonthField
19080  * 
19081  */
19082
19083 /**
19084  * @class Roo.bootstrap.MonthField
19085  * @extends Roo.bootstrap.Input
19086  * Bootstrap MonthField class
19087  * 
19088  * @cfg {String} language default en
19089  * 
19090  * @constructor
19091  * Create a new MonthField
19092  * @param {Object} config The config object
19093  */
19094
19095 Roo.bootstrap.MonthField = function(config){
19096     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19097     
19098     this.addEvents({
19099         /**
19100          * @event show
19101          * Fires when this field show.
19102          * @param {Roo.bootstrap.MonthField} this
19103          * @param {Mixed} date The date value
19104          */
19105         show : true,
19106         /**
19107          * @event show
19108          * Fires when this field hide.
19109          * @param {Roo.bootstrap.MonthField} this
19110          * @param {Mixed} date The date value
19111          */
19112         hide : true,
19113         /**
19114          * @event select
19115          * Fires when select a date.
19116          * @param {Roo.bootstrap.MonthField} this
19117          * @param {String} oldvalue The old value
19118          * @param {String} newvalue The new value
19119          */
19120         select : true
19121     });
19122 };
19123
19124 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19125     
19126     onRender: function(ct, position)
19127     {
19128         
19129         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19130         
19131         this.language = this.language || 'en';
19132         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19133         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19134         
19135         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19136         this.isInline = false;
19137         this.isInput = true;
19138         this.component = this.el.select('.add-on', true).first() || false;
19139         this.component = (this.component && this.component.length === 0) ? false : this.component;
19140         this.hasInput = this.component && this.inputEL().length;
19141         
19142         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19143         
19144         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19145         
19146         this.picker().on('mousedown', this.onMousedown, this);
19147         this.picker().on('click', this.onClick, this);
19148         
19149         this.picker().addClass('datepicker-dropdown');
19150         
19151         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19152             v.setStyle('width', '189px');
19153         });
19154         
19155         this.fillMonths();
19156         
19157         this.update();
19158         
19159         if(this.isInline) {
19160             this.show();
19161         }
19162         
19163     },
19164     
19165     setValue: function(v, suppressEvent)
19166     {   
19167         var o = this.getValue();
19168         
19169         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19170         
19171         this.update();
19172
19173         if(suppressEvent !== true){
19174             this.fireEvent('select', this, o, v);
19175         }
19176         
19177     },
19178     
19179     getValue: function()
19180     {
19181         return this.value;
19182     },
19183     
19184     onClick: function(e) 
19185     {
19186         e.stopPropagation();
19187         e.preventDefault();
19188         
19189         var target = e.getTarget();
19190         
19191         if(target.nodeName.toLowerCase() === 'i'){
19192             target = Roo.get(target).dom.parentNode;
19193         }
19194         
19195         var nodeName = target.nodeName;
19196         var className = target.className;
19197         var html = target.innerHTML;
19198         
19199         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19200             return;
19201         }
19202         
19203         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19204         
19205         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19206         
19207         this.hide();
19208                         
19209     },
19210     
19211     picker : function()
19212     {
19213         return this.pickerEl;
19214     },
19215     
19216     fillMonths: function()
19217     {    
19218         var i = 0;
19219         var months = this.picker().select('>.datepicker-months td', true).first();
19220         
19221         months.dom.innerHTML = '';
19222         
19223         while (i < 12) {
19224             var month = {
19225                 tag: 'span',
19226                 cls: 'month',
19227                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19228             };
19229             
19230             months.createChild(month);
19231         }
19232         
19233     },
19234     
19235     update: function()
19236     {
19237         var _this = this;
19238         
19239         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19240             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19241         }
19242         
19243         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19244             e.removeClass('active');
19245             
19246             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19247                 e.addClass('active');
19248             }
19249         })
19250     },
19251     
19252     place: function()
19253     {
19254         if(this.isInline) {
19255             return;
19256         }
19257         
19258         this.picker().removeClass(['bottom', 'top']);
19259         
19260         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19261             /*
19262              * place to the top of element!
19263              *
19264              */
19265             
19266             this.picker().addClass('top');
19267             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19268             
19269             return;
19270         }
19271         
19272         this.picker().addClass('bottom');
19273         
19274         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19275     },
19276     
19277     onFocus : function()
19278     {
19279         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19280         this.show();
19281     },
19282     
19283     onBlur : function()
19284     {
19285         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19286         
19287         var d = this.inputEl().getValue();
19288         
19289         this.setValue(d);
19290                 
19291         this.hide();
19292     },
19293     
19294     show : function()
19295     {
19296         this.picker().show();
19297         this.picker().select('>.datepicker-months', true).first().show();
19298         this.update();
19299         this.place();
19300         
19301         this.fireEvent('show', this, this.date);
19302     },
19303     
19304     hide : function()
19305     {
19306         if(this.isInline) {
19307             return;
19308         }
19309         this.picker().hide();
19310         this.fireEvent('hide', this, this.date);
19311         
19312     },
19313     
19314     onMousedown: function(e)
19315     {
19316         e.stopPropagation();
19317         e.preventDefault();
19318     },
19319     
19320     keyup: function(e)
19321     {
19322         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19323         this.update();
19324     },
19325
19326     fireKey: function(e)
19327     {
19328         if (!this.picker().isVisible()){
19329             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19330                 this.show();
19331             }
19332             return;
19333         }
19334         
19335         var dir;
19336         
19337         switch(e.keyCode){
19338             case 27: // escape
19339                 this.hide();
19340                 e.preventDefault();
19341                 break;
19342             case 37: // left
19343             case 39: // right
19344                 dir = e.keyCode == 37 ? -1 : 1;
19345                 
19346                 this.vIndex = this.vIndex + dir;
19347                 
19348                 if(this.vIndex < 0){
19349                     this.vIndex = 0;
19350                 }
19351                 
19352                 if(this.vIndex > 11){
19353                     this.vIndex = 11;
19354                 }
19355                 
19356                 if(isNaN(this.vIndex)){
19357                     this.vIndex = 0;
19358                 }
19359                 
19360                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19361                 
19362                 break;
19363             case 38: // up
19364             case 40: // down
19365                 
19366                 dir = e.keyCode == 38 ? -1 : 1;
19367                 
19368                 this.vIndex = this.vIndex + dir * 4;
19369                 
19370                 if(this.vIndex < 0){
19371                     this.vIndex = 0;
19372                 }
19373                 
19374                 if(this.vIndex > 11){
19375                     this.vIndex = 11;
19376                 }
19377                 
19378                 if(isNaN(this.vIndex)){
19379                     this.vIndex = 0;
19380                 }
19381                 
19382                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19383                 break;
19384                 
19385             case 13: // enter
19386                 
19387                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19388                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19389                 }
19390                 
19391                 this.hide();
19392                 e.preventDefault();
19393                 break;
19394             case 9: // tab
19395                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19396                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19397                 }
19398                 this.hide();
19399                 break;
19400             case 16: // shift
19401             case 17: // ctrl
19402             case 18: // alt
19403                 break;
19404             default :
19405                 this.hide();
19406                 
19407         }
19408     },
19409     
19410     remove: function() 
19411     {
19412         this.picker().remove();
19413     }
19414    
19415 });
19416
19417 Roo.apply(Roo.bootstrap.MonthField,  {
19418     
19419     content : {
19420         tag: 'tbody',
19421         cn: [
19422         {
19423             tag: 'tr',
19424             cn: [
19425             {
19426                 tag: 'td',
19427                 colspan: '7'
19428             }
19429             ]
19430         }
19431         ]
19432     },
19433     
19434     dates:{
19435         en: {
19436             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19437             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19438         }
19439     }
19440 });
19441
19442 Roo.apply(Roo.bootstrap.MonthField,  {
19443   
19444     template : {
19445         tag: 'div',
19446         cls: 'datepicker dropdown-menu roo-dynamic',
19447         cn: [
19448             {
19449                 tag: 'div',
19450                 cls: 'datepicker-months',
19451                 cn: [
19452                 {
19453                     tag: 'table',
19454                     cls: 'table-condensed',
19455                     cn:[
19456                         Roo.bootstrap.DateField.content
19457                     ]
19458                 }
19459                 ]
19460             }
19461         ]
19462     }
19463 });
19464
19465  
19466
19467  
19468  /*
19469  * - LGPL
19470  *
19471  * CheckBox
19472  * 
19473  */
19474
19475 /**
19476  * @class Roo.bootstrap.CheckBox
19477  * @extends Roo.bootstrap.Input
19478  * Bootstrap CheckBox class
19479  * 
19480  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
19481  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
19482  * @cfg {String} boxLabel The text that appears beside the checkbox
19483  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
19484  * @cfg {Boolean} checked initnal the element
19485  * @cfg {Boolean} inline inline the element (default false)
19486  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
19487  * 
19488  * @constructor
19489  * Create a new CheckBox
19490  * @param {Object} config The config object
19491  */
19492
19493 Roo.bootstrap.CheckBox = function(config){
19494     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
19495    
19496     this.addEvents({
19497         /**
19498         * @event check
19499         * Fires when the element is checked or unchecked.
19500         * @param {Roo.bootstrap.CheckBox} this This input
19501         * @param {Boolean} checked The new checked value
19502         */
19503        check : true
19504     });
19505     
19506 };
19507
19508 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
19509   
19510     inputType: 'checkbox',
19511     inputValue: 1,
19512     valueOff: 0,
19513     boxLabel: false,
19514     checked: false,
19515     weight : false,
19516     inline: false,
19517     
19518     getAutoCreate : function()
19519     {
19520         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19521         
19522         var id = Roo.id();
19523         
19524         var cfg = {};
19525         
19526         cfg.cls = 'form-group ' + this.inputType; //input-group
19527         
19528         if(this.inline){
19529             cfg.cls += ' ' + this.inputType + '-inline';
19530         }
19531         
19532         var input =  {
19533             tag: 'input',
19534             id : id,
19535             type : this.inputType,
19536             value : this.inputValue,
19537             cls : 'roo-' + this.inputType, //'form-box',
19538             placeholder : this.placeholder || ''
19539             
19540         };
19541         
19542         if(this.inputType != 'radio'){
19543             var hidden =  {
19544                 tag: 'input',
19545                 type : 'hidden',
19546                 cls : 'roo-hidden-value',
19547                 value : this.checked ? this.valueOff : this.inputValue
19548             };
19549         }
19550         
19551             
19552         if (this.weight) { // Validity check?
19553             cfg.cls += " " + this.inputType + "-" + this.weight;
19554         }
19555         
19556         if (this.disabled) {
19557             input.disabled=true;
19558         }
19559         
19560         if(this.checked){
19561             input.checked = this.checked;
19562             
19563         }
19564         
19565         
19566         if (this.name) {
19567             
19568             input.name = this.name;
19569             
19570             if(this.inputType != 'radio'){
19571                 hidden.name = this.name;
19572                 input.name = '_hidden_' + this.name;
19573             }
19574         }
19575         
19576         if (this.size) {
19577             input.cls += ' input-' + this.size;
19578         }
19579         
19580         var settings=this;
19581         
19582         ['xs','sm','md','lg'].map(function(size){
19583             if (settings[size]) {
19584                 cfg.cls += ' col-' + size + '-' + settings[size];
19585             }
19586         });
19587         
19588         var inputblock = input;
19589          
19590         if (this.before || this.after) {
19591             
19592             inputblock = {
19593                 cls : 'input-group',
19594                 cn :  [] 
19595             };
19596             
19597             if (this.before) {
19598                 inputblock.cn.push({
19599                     tag :'span',
19600                     cls : 'input-group-addon',
19601                     html : this.before
19602                 });
19603             }
19604             
19605             inputblock.cn.push(input);
19606             
19607             if(this.inputType != 'radio'){
19608                 inputblock.cn.push(hidden);
19609             }
19610             
19611             if (this.after) {
19612                 inputblock.cn.push({
19613                     tag :'span',
19614                     cls : 'input-group-addon',
19615                     html : this.after
19616                 });
19617             }
19618             
19619         }
19620         
19621         if (align ==='left' && this.fieldLabel.length) {
19622 //                Roo.log("left and has label");
19623                 cfg.cn = [
19624                     
19625                     {
19626                         tag: 'label',
19627                         'for' :  id,
19628                         cls : 'control-label col-md-' + this.labelWidth,
19629                         html : this.fieldLabel
19630                         
19631                     },
19632                     {
19633                         cls : "col-md-" + (12 - this.labelWidth), 
19634                         cn: [
19635                             inputblock
19636                         ]
19637                     }
19638                     
19639                 ];
19640         } else if ( this.fieldLabel.length) {
19641 //                Roo.log(" label");
19642                 cfg.cn = [
19643                    
19644                     {
19645                         tag: this.boxLabel ? 'span' : 'label',
19646                         'for': id,
19647                         cls: 'control-label box-input-label',
19648                         //cls : 'input-group-addon',
19649                         html : this.fieldLabel
19650                         
19651                     },
19652                     
19653                     inputblock
19654                     
19655                 ];
19656
19657         } else {
19658             
19659 //                Roo.log(" no label && no align");
19660                 cfg.cn = [  inputblock ] ;
19661                 
19662                 
19663         }
19664         
19665         if(this.boxLabel){
19666              var boxLabelCfg = {
19667                 tag: 'label',
19668                 //'for': id, // box label is handled by onclick - so no for...
19669                 cls: 'box-label',
19670                 html: this.boxLabel
19671             };
19672             
19673             if(this.tooltip){
19674                 boxLabelCfg.tooltip = this.tooltip;
19675             }
19676              
19677             cfg.cn.push(boxLabelCfg);
19678         }
19679         
19680         if(this.inputType != 'radio'){
19681             cfg.cn.push(hidden);
19682         }
19683         
19684         return cfg;
19685         
19686     },
19687     
19688     /**
19689      * return the real input element.
19690      */
19691     inputEl: function ()
19692     {
19693         return this.el.select('input.roo-' + this.inputType,true).first();
19694     },
19695     hiddenEl: function ()
19696     {
19697         return this.el.select('input.roo-hidden-value',true).first();
19698     },
19699     
19700     labelEl: function()
19701     {
19702         return this.el.select('label.control-label',true).first();
19703     },
19704     /* depricated... */
19705     
19706     label: function()
19707     {
19708         return this.labelEl();
19709     },
19710     
19711     boxLabelEl: function()
19712     {
19713         return this.el.select('label.box-label',true).first();
19714     },
19715     
19716     initEvents : function()
19717     {
19718 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19719         
19720         this.inputEl().on('click', this.onClick,  this);
19721         
19722         if (this.boxLabel) { 
19723             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
19724         }
19725         
19726         this.startValue = this.getValue();
19727         
19728         if(this.groupId){
19729             Roo.bootstrap.CheckBox.register(this);
19730         }
19731     },
19732     
19733     onClick : function()
19734     {   
19735         this.setChecked(!this.checked);
19736     },
19737     
19738     setChecked : function(state,suppressEvent)
19739     {
19740         this.startValue = this.getValue();
19741         
19742         if(this.inputType == 'radio'){
19743             
19744             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19745                 e.dom.checked = false;
19746             });
19747             
19748             this.inputEl().dom.checked = true;
19749             
19750             this.inputEl().dom.value = this.inputValue;
19751             
19752             if(suppressEvent !== true){
19753                 this.fireEvent('check', this, true);
19754             }
19755             
19756             this.validate();
19757             
19758             return;
19759         }
19760         
19761         this.checked = state;
19762         
19763         this.inputEl().dom.checked = state;
19764         
19765         
19766         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
19767         
19768         if(suppressEvent !== true){
19769             this.fireEvent('check', this, state);
19770         }
19771         
19772         this.validate();
19773     },
19774     
19775     getValue : function()
19776     {
19777         if(this.inputType == 'radio'){
19778             return this.getGroupValue();
19779         }
19780         
19781         return this.hiddenEl().dom.value;
19782         
19783     },
19784     
19785     getGroupValue : function()
19786     {
19787         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19788             return '';
19789         }
19790         
19791         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19792     },
19793     
19794     setValue : function(v,suppressEvent)
19795     {
19796         if(this.inputType == 'radio'){
19797             this.setGroupValue(v, suppressEvent);
19798             return;
19799         }
19800         
19801         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19802         
19803         this.validate();
19804     },
19805     
19806     setGroupValue : function(v, suppressEvent)
19807     {
19808         this.startValue = this.getValue();
19809         
19810         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19811             e.dom.checked = false;
19812             
19813             if(e.dom.value == v){
19814                 e.dom.checked = true;
19815             }
19816         });
19817         
19818         if(suppressEvent !== true){
19819             this.fireEvent('check', this, true);
19820         }
19821
19822         this.validate();
19823         
19824         return;
19825     },
19826     
19827     validate : function()
19828     {
19829         if(
19830                 this.disabled || 
19831                 (this.inputType == 'radio' && this.validateRadio()) ||
19832                 (this.inputType == 'checkbox' && this.validateCheckbox())
19833         ){
19834             this.markValid();
19835             return true;
19836         }
19837         
19838         this.markInvalid();
19839         return false;
19840     },
19841     
19842     validateRadio : function()
19843     {
19844         if(this.allowBlank){
19845             return true;
19846         }
19847         
19848         var valid = false;
19849         
19850         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19851             if(!e.dom.checked){
19852                 return;
19853             }
19854             
19855             valid = true;
19856             
19857             return false;
19858         });
19859         
19860         return valid;
19861     },
19862     
19863     validateCheckbox : function()
19864     {
19865         if(!this.groupId){
19866             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19867         }
19868         
19869         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19870         
19871         if(!group){
19872             return false;
19873         }
19874         
19875         var r = false;
19876         
19877         for(var i in group){
19878             if(r){
19879                 break;
19880             }
19881             
19882             r = (group[i].getValue() == group[i].inputValue) ? true : false;
19883         }
19884         
19885         return r;
19886     },
19887     
19888     /**
19889      * Mark this field as valid
19890      */
19891     markValid : function()
19892     {
19893         if(this.allowBlank){
19894             return;
19895         }
19896         
19897         var _this = this;
19898         
19899         this.fireEvent('valid', this);
19900         
19901         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19902         
19903         if(this.groupId){
19904             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19905         }
19906         
19907         if(label){
19908             label.markValid();
19909         }
19910         
19911         if(this.inputType == 'radio'){
19912             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19913                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19914                 e.findParent('.form-group', false, true).addClass(_this.validClass);
19915             });
19916             
19917             return;
19918         }
19919         
19920         if(!this.groupId){
19921             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19922             this.el.findParent('.form-group', false, true).addClass(this.validClass);
19923             return;
19924         }
19925         
19926         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19927             
19928         if(!group){
19929             return;
19930         }
19931         
19932         for(var i in group){
19933             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19934             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
19935         }
19936     },
19937     
19938      /**
19939      * Mark this field as invalid
19940      * @param {String} msg The validation message
19941      */
19942     markInvalid : function(msg)
19943     {
19944         if(this.allowBlank){
19945             return;
19946         }
19947         
19948         var _this = this;
19949         
19950         this.fireEvent('invalid', this, msg);
19951         
19952         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19953         
19954         if(this.groupId){
19955             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19956         }
19957         
19958         if(label){
19959             label.markInvalid();
19960         }
19961             
19962         if(this.inputType == 'radio'){
19963             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19964                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19965                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
19966             });
19967             
19968             return;
19969         }
19970         
19971         if(!this.groupId){
19972             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19973             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
19974             return;
19975         }
19976         
19977         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19978         
19979         if(!group){
19980             return;
19981         }
19982         
19983         for(var i in group){
19984             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19985             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
19986         }
19987         
19988     },
19989     
19990     disable : function()
19991     {
19992         if(this.inputType != 'radio'){
19993             Roo.bootstrap.CheckBox.superclass.disable.call(this);
19994             return;
19995         }
19996         
19997         var _this = this;
19998         
19999         if(this.rendered){
20000             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20001                 _this.getActionEl().addClass(this.disabledClass);
20002                 e.dom.disabled = true;
20003             });
20004         }
20005         
20006         this.disabled = true;
20007         this.fireEvent("disable", this);
20008         return this;
20009     },
20010
20011     enable : function()
20012     {
20013         if(this.inputType != 'radio'){
20014             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20015             return;
20016         }
20017         
20018         var _this = this;
20019         
20020         if(this.rendered){
20021             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20022                 _this.getActionEl().removeClass(this.disabledClass);
20023                 e.dom.disabled = false;
20024             });
20025         }
20026         
20027         this.disabled = false;
20028         this.fireEvent("enable", this);
20029         return this;
20030     }
20031
20032 });
20033
20034 Roo.apply(Roo.bootstrap.CheckBox, {
20035     
20036     groups: {},
20037     
20038      /**
20039     * register a CheckBox Group
20040     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20041     */
20042     register : function(checkbox)
20043     {
20044         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20045             this.groups[checkbox.groupId] = {};
20046         }
20047         
20048         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20049             return;
20050         }
20051         
20052         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20053         
20054     },
20055     /**
20056     * fetch a CheckBox Group based on the group ID
20057     * @param {string} the group ID
20058     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20059     */
20060     get: function(groupId) {
20061         if (typeof(this.groups[groupId]) == 'undefined') {
20062             return false;
20063         }
20064         
20065         return this.groups[groupId] ;
20066     }
20067     
20068     
20069 });
20070 /*
20071  * - LGPL
20072  *
20073  * Radio
20074  *
20075  *
20076  * not inline
20077  *<div class="radio">
20078   <label>
20079     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
20080     Option one is this and that&mdash;be sure to include why it's great
20081   </label>
20082 </div>
20083  *
20084  *
20085  *inline
20086  *<span>
20087  *<label class="radio-inline">fieldLabel</label>
20088  *<label class="radio-inline">
20089   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
20090 </label>
20091 <span>
20092  *
20093  *
20094  */
20095
20096 /**
20097  * @class Roo.bootstrap.Radio
20098  * @extends Roo.bootstrap.CheckBox
20099  * Bootstrap Radio class
20100
20101  * @constructor
20102  * Create a new Radio
20103  * @param {Object} config The config object
20104  */
20105
20106 Roo.bootstrap.Radio = function(config){
20107     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20108
20109 };
20110
20111 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
20112
20113     inputType: 'radio',
20114     inputValue: '',
20115     valueOff: '',
20116
20117     getAutoCreate : function()
20118     {
20119         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20120         align = align || 'left'; // default...
20121
20122
20123
20124         var id = Roo.id();
20125
20126         var cfg = {
20127                 tag : this.inline ? 'span' : 'div',
20128                 cls : 'form-group',
20129                 cn : []
20130         };
20131
20132         var inline = this.inline ? ' radio-inline' : '';
20133
20134         var lbl = {
20135                 tag: 'label' ,
20136                 // does not need for, as we wrap the input with it..
20137                 'for' : id,
20138                 cls : 'control-label box-label' + inline,
20139                 cn : []
20140         };
20141         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
20142
20143         var fieldLabel = {
20144             tag: 'label' ,
20145             //cls : 'control-label' + inline,
20146             html : this.fieldLabel,
20147             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
20148         };
20149
20150         var input =  {
20151             tag: 'input',
20152             id : id,
20153             type : this.inputType,
20154             //value : (!this.checked) ? this.valueOff : this.inputValue,
20155             value : this.inputValue,
20156             cls : 'roo-radio',
20157             placeholder : this.placeholder || '' // ?? needed????
20158
20159         };
20160         if (this.weight) { // Validity check?
20161             input.cls += " radio-" + this.weight;
20162         }
20163         if (this.disabled) {
20164             input.disabled=true;
20165         }
20166
20167         if(this.checked){
20168             input.checked = this.checked;
20169         }
20170
20171         if (this.name) {
20172             input.name = this.name;
20173         }
20174
20175         if (this.size) {
20176             input.cls += ' input-' + this.size;
20177         }
20178
20179         //?? can span's inline have a width??
20180
20181         var settings=this;
20182         ['xs','sm','md','lg'].map(function(size){
20183             if (settings[size]) {
20184                 cfg.cls += ' col-' + size + '-' + settings[size];
20185             }
20186         });
20187
20188         var inputblock = input;
20189
20190         if (this.before || this.after) {
20191
20192             inputblock = {
20193                 cls : 'input-group',
20194                 tag : 'span',
20195                 cn :  []
20196             };
20197             if (this.before) {
20198                 inputblock.cn.push({
20199                     tag :'span',
20200                     cls : 'input-group-addon',
20201                     html : this.before
20202                 });
20203             }
20204             inputblock.cn.push(input);
20205             if (this.after) {
20206                 inputblock.cn.push({
20207                     tag :'span',
20208                     cls : 'input-group-addon',
20209                     html : this.after
20210                 });
20211             }
20212
20213         };
20214
20215
20216         if (this.fieldLabel && this.fieldLabel.length) {
20217             cfg.cn.push(fieldLabel);
20218         }
20219
20220         // normal bootstrap puts the input inside the label.
20221         // however with our styled version - it has to go after the input.
20222
20223         //lbl.cn.push(inputblock);
20224
20225         var lblwrap =  {
20226             tag: 'span',
20227             cls: 'radio' + inline,
20228             cn: [
20229                 inputblock,
20230                 lbl
20231             ]
20232         };
20233
20234         cfg.cn.push( lblwrap);
20235
20236         if(this.boxLabel){
20237             lbl.cn.push({
20238                 tag: 'span',
20239                 html: this.boxLabel
20240             })
20241         }
20242
20243
20244         return cfg;
20245
20246     },
20247
20248     initEvents : function()
20249     {
20250 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20251
20252         this.inputEl().on('click', this.onClick,  this);
20253         if (this.boxLabel) {
20254             //Roo.log('find label');
20255             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
20256         }
20257
20258     },
20259
20260     inputEl: function ()
20261     {
20262         return this.el.select('input.roo-radio',true).first();
20263     },
20264     onClick : function()
20265     {
20266         Roo.log("click");
20267         this.setChecked(true);
20268     },
20269
20270     setChecked : function(state,suppressEvent)
20271     {
20272         if(state){
20273             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
20274                 v.dom.checked = false;
20275             });
20276         }
20277         Roo.log(this.inputEl().dom);
20278         this.checked = state;
20279         this.inputEl().dom.checked = state;
20280
20281         if(suppressEvent !== true){
20282             this.fireEvent('check', this, state);
20283         }
20284
20285         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
20286
20287     },
20288
20289     getGroupValue : function()
20290     {
20291         var value = '';
20292         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
20293             if(v.dom.checked == true){
20294                 value = v.dom.value;
20295             }
20296         });
20297
20298         return value;
20299     },
20300
20301     /**
20302      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
20303      * @return {Mixed} value The field value
20304      */
20305     getValue : function(){
20306         return this.getGroupValue();
20307     }
20308
20309 });
20310 //<script type="text/javascript">
20311
20312 /*
20313  * Based  Ext JS Library 1.1.1
20314  * Copyright(c) 2006-2007, Ext JS, LLC.
20315  * LGPL
20316  *
20317  */
20318  
20319 /**
20320  * @class Roo.HtmlEditorCore
20321  * @extends Roo.Component
20322  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20323  *
20324  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20325  */
20326
20327 Roo.HtmlEditorCore = function(config){
20328     
20329     
20330     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20331     
20332     
20333     this.addEvents({
20334         /**
20335          * @event initialize
20336          * Fires when the editor is fully initialized (including the iframe)
20337          * @param {Roo.HtmlEditorCore} this
20338          */
20339         initialize: true,
20340         /**
20341          * @event activate
20342          * Fires when the editor is first receives the focus. Any insertion must wait
20343          * until after this event.
20344          * @param {Roo.HtmlEditorCore} this
20345          */
20346         activate: true,
20347          /**
20348          * @event beforesync
20349          * Fires before the textarea is updated with content from the editor iframe. Return false
20350          * to cancel the sync.
20351          * @param {Roo.HtmlEditorCore} this
20352          * @param {String} html
20353          */
20354         beforesync: true,
20355          /**
20356          * @event beforepush
20357          * Fires before the iframe editor is updated with content from the textarea. Return false
20358          * to cancel the push.
20359          * @param {Roo.HtmlEditorCore} this
20360          * @param {String} html
20361          */
20362         beforepush: true,
20363          /**
20364          * @event sync
20365          * Fires when the textarea is updated with content from the editor iframe.
20366          * @param {Roo.HtmlEditorCore} this
20367          * @param {String} html
20368          */
20369         sync: true,
20370          /**
20371          * @event push
20372          * Fires when the iframe editor is updated with content from the textarea.
20373          * @param {Roo.HtmlEditorCore} this
20374          * @param {String} html
20375          */
20376         push: true,
20377         
20378         /**
20379          * @event editorevent
20380          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20381          * @param {Roo.HtmlEditorCore} this
20382          */
20383         editorevent: true
20384         
20385     });
20386     
20387     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20388     
20389     // defaults : white / black...
20390     this.applyBlacklists();
20391     
20392     
20393     
20394 };
20395
20396
20397 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20398
20399
20400      /**
20401      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20402      */
20403     
20404     owner : false,
20405     
20406      /**
20407      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20408      *                        Roo.resizable.
20409      */
20410     resizable : false,
20411      /**
20412      * @cfg {Number} height (in pixels)
20413      */   
20414     height: 300,
20415    /**
20416      * @cfg {Number} width (in pixels)
20417      */   
20418     width: 500,
20419     
20420     /**
20421      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20422      * 
20423      */
20424     stylesheets: false,
20425     
20426     // id of frame..
20427     frameId: false,
20428     
20429     // private properties
20430     validationEvent : false,
20431     deferHeight: true,
20432     initialized : false,
20433     activated : false,
20434     sourceEditMode : false,
20435     onFocus : Roo.emptyFn,
20436     iframePad:3,
20437     hideMode:'offsets',
20438     
20439     clearUp: true,
20440     
20441     // blacklist + whitelisted elements..
20442     black: false,
20443     white: false,
20444      
20445     
20446
20447     /**
20448      * Protected method that will not generally be called directly. It
20449      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20450      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20451      */
20452     getDocMarkup : function(){
20453         // body styles..
20454         var st = '';
20455         
20456         // inherit styels from page...?? 
20457         if (this.stylesheets === false) {
20458             
20459             Roo.get(document.head).select('style').each(function(node) {
20460                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20461             });
20462             
20463             Roo.get(document.head).select('link').each(function(node) { 
20464                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20465             });
20466             
20467         } else if (!this.stylesheets.length) {
20468                 // simple..
20469                 st = '<style type="text/css">' +
20470                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20471                    '</style>';
20472         } else { 
20473             
20474         }
20475         
20476         st +=  '<style type="text/css">' +
20477             'IMG { cursor: pointer } ' +
20478         '</style>';
20479
20480         
20481         return '<html><head>' + st  +
20482             //<style type="text/css">' +
20483             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20484             //'</style>' +
20485             ' </head><body class="roo-htmleditor-body"></body></html>';
20486     },
20487
20488     // private
20489     onRender : function(ct, position)
20490     {
20491         var _t = this;
20492         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20493         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20494         
20495         
20496         this.el.dom.style.border = '0 none';
20497         this.el.dom.setAttribute('tabIndex', -1);
20498         this.el.addClass('x-hidden hide');
20499         
20500         
20501         
20502         if(Roo.isIE){ // fix IE 1px bogus margin
20503             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20504         }
20505        
20506         
20507         this.frameId = Roo.id();
20508         
20509          
20510         
20511         var iframe = this.owner.wrap.createChild({
20512             tag: 'iframe',
20513             cls: 'form-control', // bootstrap..
20514             id: this.frameId,
20515             name: this.frameId,
20516             frameBorder : 'no',
20517             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20518         }, this.el
20519         );
20520         
20521         
20522         this.iframe = iframe.dom;
20523
20524          this.assignDocWin();
20525         
20526         this.doc.designMode = 'on';
20527        
20528         this.doc.open();
20529         this.doc.write(this.getDocMarkup());
20530         this.doc.close();
20531
20532         
20533         var task = { // must defer to wait for browser to be ready
20534             run : function(){
20535                 //console.log("run task?" + this.doc.readyState);
20536                 this.assignDocWin();
20537                 if(this.doc.body || this.doc.readyState == 'complete'){
20538                     try {
20539                         this.doc.designMode="on";
20540                     } catch (e) {
20541                         return;
20542                     }
20543                     Roo.TaskMgr.stop(task);
20544                     this.initEditor.defer(10, this);
20545                 }
20546             },
20547             interval : 10,
20548             duration: 10000,
20549             scope: this
20550         };
20551         Roo.TaskMgr.start(task);
20552
20553     },
20554
20555     // private
20556     onResize : function(w, h)
20557     {
20558          Roo.log('resize: ' +w + ',' + h );
20559         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20560         if(!this.iframe){
20561             return;
20562         }
20563         if(typeof w == 'number'){
20564             
20565             this.iframe.style.width = w + 'px';
20566         }
20567         if(typeof h == 'number'){
20568             
20569             this.iframe.style.height = h + 'px';
20570             if(this.doc){
20571                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20572             }
20573         }
20574         
20575     },
20576
20577     /**
20578      * Toggles the editor between standard and source edit mode.
20579      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20580      */
20581     toggleSourceEdit : function(sourceEditMode){
20582         
20583         this.sourceEditMode = sourceEditMode === true;
20584         
20585         if(this.sourceEditMode){
20586  
20587             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20588             
20589         }else{
20590             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20591             //this.iframe.className = '';
20592             this.deferFocus();
20593         }
20594         //this.setSize(this.owner.wrap.getSize());
20595         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20596     },
20597
20598     
20599   
20600
20601     /**
20602      * Protected method that will not generally be called directly. If you need/want
20603      * custom HTML cleanup, this is the method you should override.
20604      * @param {String} html The HTML to be cleaned
20605      * return {String} The cleaned HTML
20606      */
20607     cleanHtml : function(html){
20608         html = String(html);
20609         if(html.length > 5){
20610             if(Roo.isSafari){ // strip safari nonsense
20611                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20612             }
20613         }
20614         if(html == '&nbsp;'){
20615             html = '';
20616         }
20617         return html;
20618     },
20619
20620     /**
20621      * HTML Editor -> Textarea
20622      * Protected method that will not generally be called directly. Syncs the contents
20623      * of the editor iframe with the textarea.
20624      */
20625     syncValue : function(){
20626         if(this.initialized){
20627             var bd = (this.doc.body || this.doc.documentElement);
20628             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20629             var html = bd.innerHTML;
20630             if(Roo.isSafari){
20631                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20632                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20633                 if(m && m[1]){
20634                     html = '<div style="'+m[0]+'">' + html + '</div>';
20635                 }
20636             }
20637             html = this.cleanHtml(html);
20638             // fix up the special chars.. normaly like back quotes in word...
20639             // however we do not want to do this with chinese..
20640             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20641                 var cc = b.charCodeAt();
20642                 if (
20643                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20644                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20645                     (cc >= 0xf900 && cc < 0xfb00 )
20646                 ) {
20647                         return b;
20648                 }
20649                 return "&#"+cc+";" 
20650             });
20651             if(this.owner.fireEvent('beforesync', this, html) !== false){
20652                 this.el.dom.value = html;
20653                 this.owner.fireEvent('sync', this, html);
20654             }
20655         }
20656     },
20657
20658     /**
20659      * Protected method that will not generally be called directly. Pushes the value of the textarea
20660      * into the iframe editor.
20661      */
20662     pushValue : function(){
20663         if(this.initialized){
20664             var v = this.el.dom.value.trim();
20665             
20666 //            if(v.length < 1){
20667 //                v = '&#160;';
20668 //            }
20669             
20670             if(this.owner.fireEvent('beforepush', this, v) !== false){
20671                 var d = (this.doc.body || this.doc.documentElement);
20672                 d.innerHTML = v;
20673                 this.cleanUpPaste();
20674                 this.el.dom.value = d.innerHTML;
20675                 this.owner.fireEvent('push', this, v);
20676             }
20677         }
20678     },
20679
20680     // private
20681     deferFocus : function(){
20682         this.focus.defer(10, this);
20683     },
20684
20685     // doc'ed in Field
20686     focus : function(){
20687         if(this.win && !this.sourceEditMode){
20688             this.win.focus();
20689         }else{
20690             this.el.focus();
20691         }
20692     },
20693     
20694     assignDocWin: function()
20695     {
20696         var iframe = this.iframe;
20697         
20698          if(Roo.isIE){
20699             this.doc = iframe.contentWindow.document;
20700             this.win = iframe.contentWindow;
20701         } else {
20702 //            if (!Roo.get(this.frameId)) {
20703 //                return;
20704 //            }
20705 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20706 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20707             
20708             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20709                 return;
20710             }
20711             
20712             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20713             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20714         }
20715     },
20716     
20717     // private
20718     initEditor : function(){
20719         //console.log("INIT EDITOR");
20720         this.assignDocWin();
20721         
20722         
20723         
20724         this.doc.designMode="on";
20725         this.doc.open();
20726         this.doc.write(this.getDocMarkup());
20727         this.doc.close();
20728         
20729         var dbody = (this.doc.body || this.doc.documentElement);
20730         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20731         // this copies styles from the containing element into thsi one..
20732         // not sure why we need all of this..
20733         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20734         
20735         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20736         //ss['background-attachment'] = 'fixed'; // w3c
20737         dbody.bgProperties = 'fixed'; // ie
20738         //Roo.DomHelper.applyStyles(dbody, ss);
20739         Roo.EventManager.on(this.doc, {
20740             //'mousedown': this.onEditorEvent,
20741             'mouseup': this.onEditorEvent,
20742             'dblclick': this.onEditorEvent,
20743             'click': this.onEditorEvent,
20744             'keyup': this.onEditorEvent,
20745             buffer:100,
20746             scope: this
20747         });
20748         if(Roo.isGecko){
20749             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20750         }
20751         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20752             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20753         }
20754         this.initialized = true;
20755
20756         this.owner.fireEvent('initialize', this);
20757         this.pushValue();
20758     },
20759
20760     // private
20761     onDestroy : function(){
20762         
20763         
20764         
20765         if(this.rendered){
20766             
20767             //for (var i =0; i < this.toolbars.length;i++) {
20768             //    // fixme - ask toolbars for heights?
20769             //    this.toolbars[i].onDestroy();
20770            // }
20771             
20772             //this.wrap.dom.innerHTML = '';
20773             //this.wrap.remove();
20774         }
20775     },
20776
20777     // private
20778     onFirstFocus : function(){
20779         
20780         this.assignDocWin();
20781         
20782         
20783         this.activated = true;
20784          
20785     
20786         if(Roo.isGecko){ // prevent silly gecko errors
20787             this.win.focus();
20788             var s = this.win.getSelection();
20789             if(!s.focusNode || s.focusNode.nodeType != 3){
20790                 var r = s.getRangeAt(0);
20791                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20792                 r.collapse(true);
20793                 this.deferFocus();
20794             }
20795             try{
20796                 this.execCmd('useCSS', true);
20797                 this.execCmd('styleWithCSS', false);
20798             }catch(e){}
20799         }
20800         this.owner.fireEvent('activate', this);
20801     },
20802
20803     // private
20804     adjustFont: function(btn){
20805         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20806         //if(Roo.isSafari){ // safari
20807         //    adjust *= 2;
20808        // }
20809         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20810         if(Roo.isSafari){ // safari
20811             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20812             v =  (v < 10) ? 10 : v;
20813             v =  (v > 48) ? 48 : v;
20814             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20815             
20816         }
20817         
20818         
20819         v = Math.max(1, v+adjust);
20820         
20821         this.execCmd('FontSize', v  );
20822     },
20823
20824     onEditorEvent : function(e)
20825     {
20826         this.owner.fireEvent('editorevent', this, e);
20827       //  this.updateToolbar();
20828         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20829     },
20830
20831     insertTag : function(tg)
20832     {
20833         // could be a bit smarter... -> wrap the current selected tRoo..
20834         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20835             
20836             range = this.createRange(this.getSelection());
20837             var wrappingNode = this.doc.createElement(tg.toLowerCase());
20838             wrappingNode.appendChild(range.extractContents());
20839             range.insertNode(wrappingNode);
20840
20841             return;
20842             
20843             
20844             
20845         }
20846         this.execCmd("formatblock",   tg);
20847         
20848     },
20849     
20850     insertText : function(txt)
20851     {
20852         
20853         
20854         var range = this.createRange();
20855         range.deleteContents();
20856                //alert(Sender.getAttribute('label'));
20857                
20858         range.insertNode(this.doc.createTextNode(txt));
20859     } ,
20860     
20861      
20862
20863     /**
20864      * Executes a Midas editor command on the editor document and performs necessary focus and
20865      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20866      * @param {String} cmd The Midas command
20867      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20868      */
20869     relayCmd : function(cmd, value){
20870         this.win.focus();
20871         this.execCmd(cmd, value);
20872         this.owner.fireEvent('editorevent', this);
20873         //this.updateToolbar();
20874         this.owner.deferFocus();
20875     },
20876
20877     /**
20878      * Executes a Midas editor command directly on the editor document.
20879      * For visual commands, you should use {@link #relayCmd} instead.
20880      * <b>This should only be called after the editor is initialized.</b>
20881      * @param {String} cmd The Midas command
20882      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20883      */
20884     execCmd : function(cmd, value){
20885         this.doc.execCommand(cmd, false, value === undefined ? null : value);
20886         this.syncValue();
20887     },
20888  
20889  
20890    
20891     /**
20892      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20893      * to insert tRoo.
20894      * @param {String} text | dom node.. 
20895      */
20896     insertAtCursor : function(text)
20897     {
20898         
20899         
20900         
20901         if(!this.activated){
20902             return;
20903         }
20904         /*
20905         if(Roo.isIE){
20906             this.win.focus();
20907             var r = this.doc.selection.createRange();
20908             if(r){
20909                 r.collapse(true);
20910                 r.pasteHTML(text);
20911                 this.syncValue();
20912                 this.deferFocus();
20913             
20914             }
20915             return;
20916         }
20917         */
20918         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20919             this.win.focus();
20920             
20921             
20922             // from jquery ui (MIT licenced)
20923             var range, node;
20924             var win = this.win;
20925             
20926             if (win.getSelection && win.getSelection().getRangeAt) {
20927                 range = win.getSelection().getRangeAt(0);
20928                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20929                 range.insertNode(node);
20930             } else if (win.document.selection && win.document.selection.createRange) {
20931                 // no firefox support
20932                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20933                 win.document.selection.createRange().pasteHTML(txt);
20934             } else {
20935                 // no firefox support
20936                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20937                 this.execCmd('InsertHTML', txt);
20938             } 
20939             
20940             this.syncValue();
20941             
20942             this.deferFocus();
20943         }
20944     },
20945  // private
20946     mozKeyPress : function(e){
20947         if(e.ctrlKey){
20948             var c = e.getCharCode(), cmd;
20949           
20950             if(c > 0){
20951                 c = String.fromCharCode(c).toLowerCase();
20952                 switch(c){
20953                     case 'b':
20954                         cmd = 'bold';
20955                         break;
20956                     case 'i':
20957                         cmd = 'italic';
20958                         break;
20959                     
20960                     case 'u':
20961                         cmd = 'underline';
20962                         break;
20963                     
20964                     case 'v':
20965                         this.cleanUpPaste.defer(100, this);
20966                         return;
20967                         
20968                 }
20969                 if(cmd){
20970                     this.win.focus();
20971                     this.execCmd(cmd);
20972                     this.deferFocus();
20973                     e.preventDefault();
20974                 }
20975                 
20976             }
20977         }
20978     },
20979
20980     // private
20981     fixKeys : function(){ // load time branching for fastest keydown performance
20982         if(Roo.isIE){
20983             return function(e){
20984                 var k = e.getKey(), r;
20985                 if(k == e.TAB){
20986                     e.stopEvent();
20987                     r = this.doc.selection.createRange();
20988                     if(r){
20989                         r.collapse(true);
20990                         r.pasteHTML('&#160;&#160;&#160;&#160;');
20991                         this.deferFocus();
20992                     }
20993                     return;
20994                 }
20995                 
20996                 if(k == e.ENTER){
20997                     r = this.doc.selection.createRange();
20998                     if(r){
20999                         var target = r.parentElement();
21000                         if(!target || target.tagName.toLowerCase() != 'li'){
21001                             e.stopEvent();
21002                             r.pasteHTML('<br />');
21003                             r.collapse(false);
21004                             r.select();
21005                         }
21006                     }
21007                 }
21008                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21009                     this.cleanUpPaste.defer(100, this);
21010                     return;
21011                 }
21012                 
21013                 
21014             };
21015         }else if(Roo.isOpera){
21016             return function(e){
21017                 var k = e.getKey();
21018                 if(k == e.TAB){
21019                     e.stopEvent();
21020                     this.win.focus();
21021                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21022                     this.deferFocus();
21023                 }
21024                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21025                     this.cleanUpPaste.defer(100, this);
21026                     return;
21027                 }
21028                 
21029             };
21030         }else if(Roo.isSafari){
21031             return function(e){
21032                 var k = e.getKey();
21033                 
21034                 if(k == e.TAB){
21035                     e.stopEvent();
21036                     this.execCmd('InsertText','\t');
21037                     this.deferFocus();
21038                     return;
21039                 }
21040                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21041                     this.cleanUpPaste.defer(100, this);
21042                     return;
21043                 }
21044                 
21045              };
21046         }
21047     }(),
21048     
21049     getAllAncestors: function()
21050     {
21051         var p = this.getSelectedNode();
21052         var a = [];
21053         if (!p) {
21054             a.push(p); // push blank onto stack..
21055             p = this.getParentElement();
21056         }
21057         
21058         
21059         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21060             a.push(p);
21061             p = p.parentNode;
21062         }
21063         a.push(this.doc.body);
21064         return a;
21065     },
21066     lastSel : false,
21067     lastSelNode : false,
21068     
21069     
21070     getSelection : function() 
21071     {
21072         this.assignDocWin();
21073         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21074     },
21075     
21076     getSelectedNode: function() 
21077     {
21078         // this may only work on Gecko!!!
21079         
21080         // should we cache this!!!!
21081         
21082         
21083         
21084          
21085         var range = this.createRange(this.getSelection()).cloneRange();
21086         
21087         if (Roo.isIE) {
21088             var parent = range.parentElement();
21089             while (true) {
21090                 var testRange = range.duplicate();
21091                 testRange.moveToElementText(parent);
21092                 if (testRange.inRange(range)) {
21093                     break;
21094                 }
21095                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21096                     break;
21097                 }
21098                 parent = parent.parentElement;
21099             }
21100             return parent;
21101         }
21102         
21103         // is ancestor a text element.
21104         var ac =  range.commonAncestorContainer;
21105         if (ac.nodeType == 3) {
21106             ac = ac.parentNode;
21107         }
21108         
21109         var ar = ac.childNodes;
21110          
21111         var nodes = [];
21112         var other_nodes = [];
21113         var has_other_nodes = false;
21114         for (var i=0;i<ar.length;i++) {
21115             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21116                 continue;
21117             }
21118             // fullly contained node.
21119             
21120             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21121                 nodes.push(ar[i]);
21122                 continue;
21123             }
21124             
21125             // probably selected..
21126             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21127                 other_nodes.push(ar[i]);
21128                 continue;
21129             }
21130             // outer..
21131             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21132                 continue;
21133             }
21134             
21135             
21136             has_other_nodes = true;
21137         }
21138         if (!nodes.length && other_nodes.length) {
21139             nodes= other_nodes;
21140         }
21141         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21142             return false;
21143         }
21144         
21145         return nodes[0];
21146     },
21147     createRange: function(sel)
21148     {
21149         // this has strange effects when using with 
21150         // top toolbar - not sure if it's a great idea.
21151         //this.editor.contentWindow.focus();
21152         if (typeof sel != "undefined") {
21153             try {
21154                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21155             } catch(e) {
21156                 return this.doc.createRange();
21157             }
21158         } else {
21159             return this.doc.createRange();
21160         }
21161     },
21162     getParentElement: function()
21163     {
21164         
21165         this.assignDocWin();
21166         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21167         
21168         var range = this.createRange(sel);
21169          
21170         try {
21171             var p = range.commonAncestorContainer;
21172             while (p.nodeType == 3) { // text node
21173                 p = p.parentNode;
21174             }
21175             return p;
21176         } catch (e) {
21177             return null;
21178         }
21179     
21180     },
21181     /***
21182      *
21183      * Range intersection.. the hard stuff...
21184      *  '-1' = before
21185      *  '0' = hits..
21186      *  '1' = after.
21187      *         [ -- selected range --- ]
21188      *   [fail]                        [fail]
21189      *
21190      *    basically..
21191      *      if end is before start or  hits it. fail.
21192      *      if start is after end or hits it fail.
21193      *
21194      *   if either hits (but other is outside. - then it's not 
21195      *   
21196      *    
21197      **/
21198     
21199     
21200     // @see http://www.thismuchiknow.co.uk/?p=64.
21201     rangeIntersectsNode : function(range, node)
21202     {
21203         var nodeRange = node.ownerDocument.createRange();
21204         try {
21205             nodeRange.selectNode(node);
21206         } catch (e) {
21207             nodeRange.selectNodeContents(node);
21208         }
21209     
21210         var rangeStartRange = range.cloneRange();
21211         rangeStartRange.collapse(true);
21212     
21213         var rangeEndRange = range.cloneRange();
21214         rangeEndRange.collapse(false);
21215     
21216         var nodeStartRange = nodeRange.cloneRange();
21217         nodeStartRange.collapse(true);
21218     
21219         var nodeEndRange = nodeRange.cloneRange();
21220         nodeEndRange.collapse(false);
21221     
21222         return rangeStartRange.compareBoundaryPoints(
21223                  Range.START_TO_START, nodeEndRange) == -1 &&
21224                rangeEndRange.compareBoundaryPoints(
21225                  Range.START_TO_START, nodeStartRange) == 1;
21226         
21227          
21228     },
21229     rangeCompareNode : function(range, node)
21230     {
21231         var nodeRange = node.ownerDocument.createRange();
21232         try {
21233             nodeRange.selectNode(node);
21234         } catch (e) {
21235             nodeRange.selectNodeContents(node);
21236         }
21237         
21238         
21239         range.collapse(true);
21240     
21241         nodeRange.collapse(true);
21242      
21243         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21244         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21245          
21246         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21247         
21248         var nodeIsBefore   =  ss == 1;
21249         var nodeIsAfter    = ee == -1;
21250         
21251         if (nodeIsBefore && nodeIsAfter) {
21252             return 0; // outer
21253         }
21254         if (!nodeIsBefore && nodeIsAfter) {
21255             return 1; //right trailed.
21256         }
21257         
21258         if (nodeIsBefore && !nodeIsAfter) {
21259             return 2;  // left trailed.
21260         }
21261         // fully contined.
21262         return 3;
21263     },
21264
21265     // private? - in a new class?
21266     cleanUpPaste :  function()
21267     {
21268         // cleans up the whole document..
21269         Roo.log('cleanuppaste');
21270         
21271         this.cleanUpChildren(this.doc.body);
21272         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21273         if (clean != this.doc.body.innerHTML) {
21274             this.doc.body.innerHTML = clean;
21275         }
21276         
21277     },
21278     
21279     cleanWordChars : function(input) {// change the chars to hex code
21280         var he = Roo.HtmlEditorCore;
21281         
21282         var output = input;
21283         Roo.each(he.swapCodes, function(sw) { 
21284             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21285             
21286             output = output.replace(swapper, sw[1]);
21287         });
21288         
21289         return output;
21290     },
21291     
21292     
21293     cleanUpChildren : function (n)
21294     {
21295         if (!n.childNodes.length) {
21296             return;
21297         }
21298         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21299            this.cleanUpChild(n.childNodes[i]);
21300         }
21301     },
21302     
21303     
21304         
21305     
21306     cleanUpChild : function (node)
21307     {
21308         var ed = this;
21309         //console.log(node);
21310         if (node.nodeName == "#text") {
21311             // clean up silly Windows -- stuff?
21312             return; 
21313         }
21314         if (node.nodeName == "#comment") {
21315             node.parentNode.removeChild(node);
21316             // clean up silly Windows -- stuff?
21317             return; 
21318         }
21319         var lcname = node.tagName.toLowerCase();
21320         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21321         // whitelist of tags..
21322         
21323         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21324             // remove node.
21325             node.parentNode.removeChild(node);
21326             return;
21327             
21328         }
21329         
21330         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21331         
21332         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21333         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21334         
21335         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21336         //    remove_keep_children = true;
21337         //}
21338         
21339         if (remove_keep_children) {
21340             this.cleanUpChildren(node);
21341             // inserts everything just before this node...
21342             while (node.childNodes.length) {
21343                 var cn = node.childNodes[0];
21344                 node.removeChild(cn);
21345                 node.parentNode.insertBefore(cn, node);
21346             }
21347             node.parentNode.removeChild(node);
21348             return;
21349         }
21350         
21351         if (!node.attributes || !node.attributes.length) {
21352             this.cleanUpChildren(node);
21353             return;
21354         }
21355         
21356         function cleanAttr(n,v)
21357         {
21358             
21359             if (v.match(/^\./) || v.match(/^\//)) {
21360                 return;
21361             }
21362             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
21363                 return;
21364             }
21365             if (v.match(/^#/)) {
21366                 return;
21367             }
21368 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21369             node.removeAttribute(n);
21370             
21371         }
21372         
21373         var cwhite = this.cwhite;
21374         var cblack = this.cblack;
21375             
21376         function cleanStyle(n,v)
21377         {
21378             if (v.match(/expression/)) { //XSS?? should we even bother..
21379                 node.removeAttribute(n);
21380                 return;
21381             }
21382             
21383             var parts = v.split(/;/);
21384             var clean = [];
21385             
21386             Roo.each(parts, function(p) {
21387                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21388                 if (!p.length) {
21389                     return true;
21390                 }
21391                 var l = p.split(':').shift().replace(/\s+/g,'');
21392                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21393                 
21394                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21395 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21396                     //node.removeAttribute(n);
21397                     return true;
21398                 }
21399                 //Roo.log()
21400                 // only allow 'c whitelisted system attributes'
21401                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21402 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21403                     //node.removeAttribute(n);
21404                     return true;
21405                 }
21406                 
21407                 
21408                  
21409                 
21410                 clean.push(p);
21411                 return true;
21412             });
21413             if (clean.length) { 
21414                 node.setAttribute(n, clean.join(';'));
21415             } else {
21416                 node.removeAttribute(n);
21417             }
21418             
21419         }
21420         
21421         
21422         for (var i = node.attributes.length-1; i > -1 ; i--) {
21423             var a = node.attributes[i];
21424             //console.log(a);
21425             
21426             if (a.name.toLowerCase().substr(0,2)=='on')  {
21427                 node.removeAttribute(a.name);
21428                 continue;
21429             }
21430             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21431                 node.removeAttribute(a.name);
21432                 continue;
21433             }
21434             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21435                 cleanAttr(a.name,a.value); // fixme..
21436                 continue;
21437             }
21438             if (a.name == 'style') {
21439                 cleanStyle(a.name,a.value);
21440                 continue;
21441             }
21442             /// clean up MS crap..
21443             // tecnically this should be a list of valid class'es..
21444             
21445             
21446             if (a.name == 'class') {
21447                 if (a.value.match(/^Mso/)) {
21448                     node.className = '';
21449                 }
21450                 
21451                 if (a.value.match(/body/)) {
21452                     node.className = '';
21453                 }
21454                 continue;
21455             }
21456             
21457             // style cleanup!?
21458             // class cleanup?
21459             
21460         }
21461         
21462         
21463         this.cleanUpChildren(node);
21464         
21465         
21466     },
21467     
21468     /**
21469      * Clean up MS wordisms...
21470      */
21471     cleanWord : function(node)
21472     {
21473         
21474         
21475         if (!node) {
21476             this.cleanWord(this.doc.body);
21477             return;
21478         }
21479         if (node.nodeName == "#text") {
21480             // clean up silly Windows -- stuff?
21481             return; 
21482         }
21483         if (node.nodeName == "#comment") {
21484             node.parentNode.removeChild(node);
21485             // clean up silly Windows -- stuff?
21486             return; 
21487         }
21488         
21489         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21490             node.parentNode.removeChild(node);
21491             return;
21492         }
21493         
21494         // remove - but keep children..
21495         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21496             while (node.childNodes.length) {
21497                 var cn = node.childNodes[0];
21498                 node.removeChild(cn);
21499                 node.parentNode.insertBefore(cn, node);
21500             }
21501             node.parentNode.removeChild(node);
21502             this.iterateChildren(node, this.cleanWord);
21503             return;
21504         }
21505         // clean styles
21506         if (node.className.length) {
21507             
21508             var cn = node.className.split(/\W+/);
21509             var cna = [];
21510             Roo.each(cn, function(cls) {
21511                 if (cls.match(/Mso[a-zA-Z]+/)) {
21512                     return;
21513                 }
21514                 cna.push(cls);
21515             });
21516             node.className = cna.length ? cna.join(' ') : '';
21517             if (!cna.length) {
21518                 node.removeAttribute("class");
21519             }
21520         }
21521         
21522         if (node.hasAttribute("lang")) {
21523             node.removeAttribute("lang");
21524         }
21525         
21526         if (node.hasAttribute("style")) {
21527             
21528             var styles = node.getAttribute("style").split(";");
21529             var nstyle = [];
21530             Roo.each(styles, function(s) {
21531                 if (!s.match(/:/)) {
21532                     return;
21533                 }
21534                 var kv = s.split(":");
21535                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21536                     return;
21537                 }
21538                 // what ever is left... we allow.
21539                 nstyle.push(s);
21540             });
21541             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21542             if (!nstyle.length) {
21543                 node.removeAttribute('style');
21544             }
21545         }
21546         this.iterateChildren(node, this.cleanWord);
21547         
21548         
21549         
21550     },
21551     /**
21552      * iterateChildren of a Node, calling fn each time, using this as the scole..
21553      * @param {DomNode} node node to iterate children of.
21554      * @param {Function} fn method of this class to call on each item.
21555      */
21556     iterateChildren : function(node, fn)
21557     {
21558         if (!node.childNodes.length) {
21559                 return;
21560         }
21561         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21562            fn.call(this, node.childNodes[i])
21563         }
21564     },
21565     
21566     
21567     /**
21568      * cleanTableWidths.
21569      *
21570      * Quite often pasting from word etc.. results in tables with column and widths.
21571      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21572      *
21573      */
21574     cleanTableWidths : function(node)
21575     {
21576          
21577          
21578         if (!node) {
21579             this.cleanTableWidths(this.doc.body);
21580             return;
21581         }
21582         
21583         // ignore list...
21584         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21585             return; 
21586         }
21587         Roo.log(node.tagName);
21588         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21589             this.iterateChildren(node, this.cleanTableWidths);
21590             return;
21591         }
21592         if (node.hasAttribute('width')) {
21593             node.removeAttribute('width');
21594         }
21595         
21596          
21597         if (node.hasAttribute("style")) {
21598             // pretty basic...
21599             
21600             var styles = node.getAttribute("style").split(";");
21601             var nstyle = [];
21602             Roo.each(styles, function(s) {
21603                 if (!s.match(/:/)) {
21604                     return;
21605                 }
21606                 var kv = s.split(":");
21607                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21608                     return;
21609                 }
21610                 // what ever is left... we allow.
21611                 nstyle.push(s);
21612             });
21613             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21614             if (!nstyle.length) {
21615                 node.removeAttribute('style');
21616             }
21617         }
21618         
21619         this.iterateChildren(node, this.cleanTableWidths);
21620         
21621         
21622     },
21623     
21624     
21625     
21626     
21627     domToHTML : function(currentElement, depth, nopadtext) {
21628         
21629         depth = depth || 0;
21630         nopadtext = nopadtext || false;
21631     
21632         if (!currentElement) {
21633             return this.domToHTML(this.doc.body);
21634         }
21635         
21636         //Roo.log(currentElement);
21637         var j;
21638         var allText = false;
21639         var nodeName = currentElement.nodeName;
21640         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21641         
21642         if  (nodeName == '#text') {
21643             
21644             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21645         }
21646         
21647         
21648         var ret = '';
21649         if (nodeName != 'BODY') {
21650              
21651             var i = 0;
21652             // Prints the node tagName, such as <A>, <IMG>, etc
21653             if (tagName) {
21654                 var attr = [];
21655                 for(i = 0; i < currentElement.attributes.length;i++) {
21656                     // quoting?
21657                     var aname = currentElement.attributes.item(i).name;
21658                     if (!currentElement.attributes.item(i).value.length) {
21659                         continue;
21660                     }
21661                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21662                 }
21663                 
21664                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21665             } 
21666             else {
21667                 
21668                 // eack
21669             }
21670         } else {
21671             tagName = false;
21672         }
21673         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21674             return ret;
21675         }
21676         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21677             nopadtext = true;
21678         }
21679         
21680         
21681         // Traverse the tree
21682         i = 0;
21683         var currentElementChild = currentElement.childNodes.item(i);
21684         var allText = true;
21685         var innerHTML  = '';
21686         lastnode = '';
21687         while (currentElementChild) {
21688             // Formatting code (indent the tree so it looks nice on the screen)
21689             var nopad = nopadtext;
21690             if (lastnode == 'SPAN') {
21691                 nopad  = true;
21692             }
21693             // text
21694             if  (currentElementChild.nodeName == '#text') {
21695                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21696                 toadd = nopadtext ? toadd : toadd.trim();
21697                 if (!nopad && toadd.length > 80) {
21698                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21699                 }
21700                 innerHTML  += toadd;
21701                 
21702                 i++;
21703                 currentElementChild = currentElement.childNodes.item(i);
21704                 lastNode = '';
21705                 continue;
21706             }
21707             allText = false;
21708             
21709             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21710                 
21711             // Recursively traverse the tree structure of the child node
21712             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21713             lastnode = currentElementChild.nodeName;
21714             i++;
21715             currentElementChild=currentElement.childNodes.item(i);
21716         }
21717         
21718         ret += innerHTML;
21719         
21720         if (!allText) {
21721                 // The remaining code is mostly for formatting the tree
21722             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21723         }
21724         
21725         
21726         if (tagName) {
21727             ret+= "</"+tagName+">";
21728         }
21729         return ret;
21730         
21731     },
21732         
21733     applyBlacklists : function()
21734     {
21735         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
21736         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
21737         
21738         this.white = [];
21739         this.black = [];
21740         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21741             if (b.indexOf(tag) > -1) {
21742                 return;
21743             }
21744             this.white.push(tag);
21745             
21746         }, this);
21747         
21748         Roo.each(w, function(tag) {
21749             if (b.indexOf(tag) > -1) {
21750                 return;
21751             }
21752             if (this.white.indexOf(tag) > -1) {
21753                 return;
21754             }
21755             this.white.push(tag);
21756             
21757         }, this);
21758         
21759         
21760         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21761             if (w.indexOf(tag) > -1) {
21762                 return;
21763             }
21764             this.black.push(tag);
21765             
21766         }, this);
21767         
21768         Roo.each(b, function(tag) {
21769             if (w.indexOf(tag) > -1) {
21770                 return;
21771             }
21772             if (this.black.indexOf(tag) > -1) {
21773                 return;
21774             }
21775             this.black.push(tag);
21776             
21777         }, this);
21778         
21779         
21780         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
21781         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
21782         
21783         this.cwhite = [];
21784         this.cblack = [];
21785         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21786             if (b.indexOf(tag) > -1) {
21787                 return;
21788             }
21789             this.cwhite.push(tag);
21790             
21791         }, this);
21792         
21793         Roo.each(w, function(tag) {
21794             if (b.indexOf(tag) > -1) {
21795                 return;
21796             }
21797             if (this.cwhite.indexOf(tag) > -1) {
21798                 return;
21799             }
21800             this.cwhite.push(tag);
21801             
21802         }, this);
21803         
21804         
21805         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21806             if (w.indexOf(tag) > -1) {
21807                 return;
21808             }
21809             this.cblack.push(tag);
21810             
21811         }, this);
21812         
21813         Roo.each(b, function(tag) {
21814             if (w.indexOf(tag) > -1) {
21815                 return;
21816             }
21817             if (this.cblack.indexOf(tag) > -1) {
21818                 return;
21819             }
21820             this.cblack.push(tag);
21821             
21822         }, this);
21823     },
21824     
21825     setStylesheets : function(stylesheets)
21826     {
21827         if(typeof(stylesheets) == 'string'){
21828             Roo.get(this.iframe.contentDocument.head).createChild({
21829                 tag : 'link',
21830                 rel : 'stylesheet',
21831                 type : 'text/css',
21832                 href : stylesheets
21833             });
21834             
21835             return;
21836         }
21837         var _this = this;
21838      
21839         Roo.each(stylesheets, function(s) {
21840             if(!s.length){
21841                 return;
21842             }
21843             
21844             Roo.get(_this.iframe.contentDocument.head).createChild({
21845                 tag : 'link',
21846                 rel : 'stylesheet',
21847                 type : 'text/css',
21848                 href : s
21849             });
21850         });
21851
21852         
21853     },
21854     
21855     removeStylesheets : function()
21856     {
21857         var _this = this;
21858         
21859         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21860             s.remove();
21861         });
21862     }
21863     
21864     // hide stuff that is not compatible
21865     /**
21866      * @event blur
21867      * @hide
21868      */
21869     /**
21870      * @event change
21871      * @hide
21872      */
21873     /**
21874      * @event focus
21875      * @hide
21876      */
21877     /**
21878      * @event specialkey
21879      * @hide
21880      */
21881     /**
21882      * @cfg {String} fieldClass @hide
21883      */
21884     /**
21885      * @cfg {String} focusClass @hide
21886      */
21887     /**
21888      * @cfg {String} autoCreate @hide
21889      */
21890     /**
21891      * @cfg {String} inputType @hide
21892      */
21893     /**
21894      * @cfg {String} invalidClass @hide
21895      */
21896     /**
21897      * @cfg {String} invalidText @hide
21898      */
21899     /**
21900      * @cfg {String} msgFx @hide
21901      */
21902     /**
21903      * @cfg {String} validateOnBlur @hide
21904      */
21905 });
21906
21907 Roo.HtmlEditorCore.white = [
21908         'area', 'br', 'img', 'input', 'hr', 'wbr',
21909         
21910        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
21911        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
21912        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
21913        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
21914        'table',   'ul',         'xmp', 
21915        
21916        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
21917       'thead',   'tr', 
21918      
21919       'dir', 'menu', 'ol', 'ul', 'dl',
21920        
21921       'embed',  'object'
21922 ];
21923
21924
21925 Roo.HtmlEditorCore.black = [
21926     //    'embed',  'object', // enable - backend responsiblity to clean thiese
21927         'applet', // 
21928         'base',   'basefont', 'bgsound', 'blink',  'body', 
21929         'frame',  'frameset', 'head',    'html',   'ilayer', 
21930         'iframe', 'layer',  'link',     'meta',    'object',   
21931         'script', 'style' ,'title',  'xml' // clean later..
21932 ];
21933 Roo.HtmlEditorCore.clean = [
21934     'script', 'style', 'title', 'xml'
21935 ];
21936 Roo.HtmlEditorCore.remove = [
21937     'font'
21938 ];
21939 // attributes..
21940
21941 Roo.HtmlEditorCore.ablack = [
21942     'on'
21943 ];
21944     
21945 Roo.HtmlEditorCore.aclean = [ 
21946     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
21947 ];
21948
21949 // protocols..
21950 Roo.HtmlEditorCore.pwhite= [
21951         'http',  'https',  'mailto'
21952 ];
21953
21954 // white listed style attributes.
21955 Roo.HtmlEditorCore.cwhite= [
21956       //  'text-align', /// default is to allow most things..
21957       
21958          
21959 //        'font-size'//??
21960 ];
21961
21962 // black listed style attributes.
21963 Roo.HtmlEditorCore.cblack= [
21964       //  'font-size' -- this can be set by the project 
21965 ];
21966
21967
21968 Roo.HtmlEditorCore.swapCodes   =[ 
21969     [    8211, "--" ], 
21970     [    8212, "--" ], 
21971     [    8216,  "'" ],  
21972     [    8217, "'" ],  
21973     [    8220, '"' ],  
21974     [    8221, '"' ],  
21975     [    8226, "*" ],  
21976     [    8230, "..." ]
21977 ]; 
21978
21979     /*
21980  * - LGPL
21981  *
21982  * HtmlEditor
21983  * 
21984  */
21985
21986 /**
21987  * @class Roo.bootstrap.HtmlEditor
21988  * @extends Roo.bootstrap.TextArea
21989  * Bootstrap HtmlEditor class
21990
21991  * @constructor
21992  * Create a new HtmlEditor
21993  * @param {Object} config The config object
21994  */
21995
21996 Roo.bootstrap.HtmlEditor = function(config){
21997     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
21998     if (!this.toolbars) {
21999         this.toolbars = [];
22000     }
22001     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22002     this.addEvents({
22003             /**
22004              * @event initialize
22005              * Fires when the editor is fully initialized (including the iframe)
22006              * @param {HtmlEditor} this
22007              */
22008             initialize: true,
22009             /**
22010              * @event activate
22011              * Fires when the editor is first receives the focus. Any insertion must wait
22012              * until after this event.
22013              * @param {HtmlEditor} this
22014              */
22015             activate: true,
22016              /**
22017              * @event beforesync
22018              * Fires before the textarea is updated with content from the editor iframe. Return false
22019              * to cancel the sync.
22020              * @param {HtmlEditor} this
22021              * @param {String} html
22022              */
22023             beforesync: true,
22024              /**
22025              * @event beforepush
22026              * Fires before the iframe editor is updated with content from the textarea. Return false
22027              * to cancel the push.
22028              * @param {HtmlEditor} this
22029              * @param {String} html
22030              */
22031             beforepush: true,
22032              /**
22033              * @event sync
22034              * Fires when the textarea is updated with content from the editor iframe.
22035              * @param {HtmlEditor} this
22036              * @param {String} html
22037              */
22038             sync: true,
22039              /**
22040              * @event push
22041              * Fires when the iframe editor is updated with content from the textarea.
22042              * @param {HtmlEditor} this
22043              * @param {String} html
22044              */
22045             push: true,
22046              /**
22047              * @event editmodechange
22048              * Fires when the editor switches edit modes
22049              * @param {HtmlEditor} this
22050              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22051              */
22052             editmodechange: true,
22053             /**
22054              * @event editorevent
22055              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22056              * @param {HtmlEditor} this
22057              */
22058             editorevent: true,
22059             /**
22060              * @event firstfocus
22061              * Fires when on first focus - needed by toolbars..
22062              * @param {HtmlEditor} this
22063              */
22064             firstfocus: true,
22065             /**
22066              * @event autosave
22067              * Auto save the htmlEditor value as a file into Events
22068              * @param {HtmlEditor} this
22069              */
22070             autosave: true,
22071             /**
22072              * @event savedpreview
22073              * preview the saved version of htmlEditor
22074              * @param {HtmlEditor} this
22075              */
22076             savedpreview: true
22077         });
22078 };
22079
22080
22081 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
22082     
22083     
22084       /**
22085      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22086      */
22087     toolbars : false,
22088    
22089      /**
22090      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22091      *                        Roo.resizable.
22092      */
22093     resizable : false,
22094      /**
22095      * @cfg {Number} height (in pixels)
22096      */   
22097     height: 300,
22098    /**
22099      * @cfg {Number} width (in pixels)
22100      */   
22101     width: false,
22102     
22103     /**
22104      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22105      * 
22106      */
22107     stylesheets: false,
22108     
22109     // id of frame..
22110     frameId: false,
22111     
22112     // private properties
22113     validationEvent : false,
22114     deferHeight: true,
22115     initialized : false,
22116     activated : false,
22117     
22118     onFocus : Roo.emptyFn,
22119     iframePad:3,
22120     hideMode:'offsets',
22121     
22122     
22123     tbContainer : false,
22124     
22125     toolbarContainer :function() {
22126         return this.wrap.select('.x-html-editor-tb',true).first();
22127     },
22128
22129     /**
22130      * Protected method that will not generally be called directly. It
22131      * is called when the editor creates its toolbar. Override this method if you need to
22132      * add custom toolbar buttons.
22133      * @param {HtmlEditor} editor
22134      */
22135     createToolbar : function(){
22136         
22137         Roo.log("create toolbars");
22138         
22139         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
22140         this.toolbars[0].render(this.toolbarContainer());
22141         
22142         return;
22143         
22144 //        if (!editor.toolbars || !editor.toolbars.length) {
22145 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
22146 //        }
22147 //        
22148 //        for (var i =0 ; i < editor.toolbars.length;i++) {
22149 //            editor.toolbars[i] = Roo.factory(
22150 //                    typeof(editor.toolbars[i]) == 'string' ?
22151 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
22152 //                Roo.bootstrap.HtmlEditor);
22153 //            editor.toolbars[i].init(editor);
22154 //        }
22155     },
22156
22157      
22158     // private
22159     onRender : function(ct, position)
22160     {
22161        // Roo.log("Call onRender: " + this.xtype);
22162         var _t = this;
22163         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
22164       
22165         this.wrap = this.inputEl().wrap({
22166             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22167         });
22168         
22169         this.editorcore.onRender(ct, position);
22170          
22171         if (this.resizable) {
22172             this.resizeEl = new Roo.Resizable(this.wrap, {
22173                 pinned : true,
22174                 wrap: true,
22175                 dynamic : true,
22176                 minHeight : this.height,
22177                 height: this.height,
22178                 handles : this.resizable,
22179                 width: this.width,
22180                 listeners : {
22181                     resize : function(r, w, h) {
22182                         _t.onResize(w,h); // -something
22183                     }
22184                 }
22185             });
22186             
22187         }
22188         this.createToolbar(this);
22189        
22190         
22191         if(!this.width && this.resizable){
22192             this.setSize(this.wrap.getSize());
22193         }
22194         if (this.resizeEl) {
22195             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22196             // should trigger onReize..
22197         }
22198         
22199     },
22200
22201     // private
22202     onResize : function(w, h)
22203     {
22204         Roo.log('resize: ' +w + ',' + h );
22205         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
22206         var ew = false;
22207         var eh = false;
22208         
22209         if(this.inputEl() ){
22210             if(typeof w == 'number'){
22211                 var aw = w - this.wrap.getFrameWidth('lr');
22212                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
22213                 ew = aw;
22214             }
22215             if(typeof h == 'number'){
22216                  var tbh = -11;  // fixme it needs to tool bar size!
22217                 for (var i =0; i < this.toolbars.length;i++) {
22218                     // fixme - ask toolbars for heights?
22219                     tbh += this.toolbars[i].el.getHeight();
22220                     //if (this.toolbars[i].footer) {
22221                     //    tbh += this.toolbars[i].footer.el.getHeight();
22222                     //}
22223                 }
22224               
22225                 
22226                 
22227                 
22228                 
22229                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22230                 ah -= 5; // knock a few pixes off for look..
22231                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
22232                 var eh = ah;
22233             }
22234         }
22235         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22236         this.editorcore.onResize(ew,eh);
22237         
22238     },
22239
22240     /**
22241      * Toggles the editor between standard and source edit mode.
22242      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22243      */
22244     toggleSourceEdit : function(sourceEditMode)
22245     {
22246         this.editorcore.toggleSourceEdit(sourceEditMode);
22247         
22248         if(this.editorcore.sourceEditMode){
22249             Roo.log('editor - showing textarea');
22250             
22251 //            Roo.log('in');
22252 //            Roo.log(this.syncValue());
22253             this.syncValue();
22254             this.inputEl().removeClass(['hide', 'x-hidden']);
22255             this.inputEl().dom.removeAttribute('tabIndex');
22256             this.inputEl().focus();
22257         }else{
22258             Roo.log('editor - hiding textarea');
22259 //            Roo.log('out')
22260 //            Roo.log(this.pushValue()); 
22261             this.pushValue();
22262             
22263             this.inputEl().addClass(['hide', 'x-hidden']);
22264             this.inputEl().dom.setAttribute('tabIndex', -1);
22265             //this.deferFocus();
22266         }
22267          
22268         if(this.resizable){
22269             this.setSize(this.wrap.getSize());
22270         }
22271         
22272         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22273     },
22274  
22275     // private (for BoxComponent)
22276     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22277
22278     // private (for BoxComponent)
22279     getResizeEl : function(){
22280         return this.wrap;
22281     },
22282
22283     // private (for BoxComponent)
22284     getPositionEl : function(){
22285         return this.wrap;
22286     },
22287
22288     // private
22289     initEvents : function(){
22290         this.originalValue = this.getValue();
22291     },
22292
22293 //    /**
22294 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22295 //     * @method
22296 //     */
22297 //    markInvalid : Roo.emptyFn,
22298 //    /**
22299 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22300 //     * @method
22301 //     */
22302 //    clearInvalid : Roo.emptyFn,
22303
22304     setValue : function(v){
22305         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
22306         this.editorcore.pushValue();
22307     },
22308
22309      
22310     // private
22311     deferFocus : function(){
22312         this.focus.defer(10, this);
22313     },
22314
22315     // doc'ed in Field
22316     focus : function(){
22317         this.editorcore.focus();
22318         
22319     },
22320       
22321
22322     // private
22323     onDestroy : function(){
22324         
22325         
22326         
22327         if(this.rendered){
22328             
22329             for (var i =0; i < this.toolbars.length;i++) {
22330                 // fixme - ask toolbars for heights?
22331                 this.toolbars[i].onDestroy();
22332             }
22333             
22334             this.wrap.dom.innerHTML = '';
22335             this.wrap.remove();
22336         }
22337     },
22338
22339     // private
22340     onFirstFocus : function(){
22341         //Roo.log("onFirstFocus");
22342         this.editorcore.onFirstFocus();
22343          for (var i =0; i < this.toolbars.length;i++) {
22344             this.toolbars[i].onFirstFocus();
22345         }
22346         
22347     },
22348     
22349     // private
22350     syncValue : function()
22351     {   
22352         this.editorcore.syncValue();
22353     },
22354     
22355     pushValue : function()
22356     {   
22357         this.editorcore.pushValue();
22358     }
22359      
22360     
22361     // hide stuff that is not compatible
22362     /**
22363      * @event blur
22364      * @hide
22365      */
22366     /**
22367      * @event change
22368      * @hide
22369      */
22370     /**
22371      * @event focus
22372      * @hide
22373      */
22374     /**
22375      * @event specialkey
22376      * @hide
22377      */
22378     /**
22379      * @cfg {String} fieldClass @hide
22380      */
22381     /**
22382      * @cfg {String} focusClass @hide
22383      */
22384     /**
22385      * @cfg {String} autoCreate @hide
22386      */
22387     /**
22388      * @cfg {String} inputType @hide
22389      */
22390     /**
22391      * @cfg {String} invalidClass @hide
22392      */
22393     /**
22394      * @cfg {String} invalidText @hide
22395      */
22396     /**
22397      * @cfg {String} msgFx @hide
22398      */
22399     /**
22400      * @cfg {String} validateOnBlur @hide
22401      */
22402 });
22403  
22404     
22405    
22406    
22407    
22408       
22409 Roo.namespace('Roo.bootstrap.htmleditor');
22410 /**
22411  * @class Roo.bootstrap.HtmlEditorToolbar1
22412  * Basic Toolbar
22413  * 
22414  * Usage:
22415  *
22416  new Roo.bootstrap.HtmlEditor({
22417     ....
22418     toolbars : [
22419         new Roo.bootstrap.HtmlEditorToolbar1({
22420             disable : { fonts: 1 , format: 1, ..., ... , ...],
22421             btns : [ .... ]
22422         })
22423     }
22424      
22425  * 
22426  * @cfg {Object} disable List of elements to disable..
22427  * @cfg {Array} btns List of additional buttons.
22428  * 
22429  * 
22430  * NEEDS Extra CSS? 
22431  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22432  */
22433  
22434 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
22435 {
22436     
22437     Roo.apply(this, config);
22438     
22439     // default disabled, based on 'good practice'..
22440     this.disable = this.disable || {};
22441     Roo.applyIf(this.disable, {
22442         fontSize : true,
22443         colors : true,
22444         specialElements : true
22445     });
22446     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
22447     
22448     this.editor = config.editor;
22449     this.editorcore = config.editor.editorcore;
22450     
22451     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
22452     
22453     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22454     // dont call parent... till later.
22455 }
22456 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
22457      
22458     bar : true,
22459     
22460     editor : false,
22461     editorcore : false,
22462     
22463     
22464     formats : [
22465         "p" ,  
22466         "h1","h2","h3","h4","h5","h6", 
22467         "pre", "code", 
22468         "abbr", "acronym", "address", "cite", "samp", "var",
22469         'div','span'
22470     ],
22471     
22472     onRender : function(ct, position)
22473     {
22474        // Roo.log("Call onRender: " + this.xtype);
22475         
22476        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
22477        Roo.log(this.el);
22478        this.el.dom.style.marginBottom = '0';
22479        var _this = this;
22480        var editorcore = this.editorcore;
22481        var editor= this.editor;
22482        
22483        var children = [];
22484        var btn = function(id,cmd , toggle, handler){
22485        
22486             var  event = toggle ? 'toggle' : 'click';
22487        
22488             var a = {
22489                 size : 'sm',
22490                 xtype: 'Button',
22491                 xns: Roo.bootstrap,
22492                 glyphicon : id,
22493                 cmd : id || cmd,
22494                 enableToggle:toggle !== false,
22495                 //html : 'submit'
22496                 pressed : toggle ? false : null,
22497                 listeners : {}
22498             };
22499             a.listeners[toggle ? 'toggle' : 'click'] = function() {
22500                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
22501             };
22502             children.push(a);
22503             return a;
22504        }
22505         
22506         var style = {
22507                 xtype: 'Button',
22508                 size : 'sm',
22509                 xns: Roo.bootstrap,
22510                 glyphicon : 'font',
22511                 //html : 'submit'
22512                 menu : {
22513                     xtype: 'Menu',
22514                     xns: Roo.bootstrap,
22515                     items:  []
22516                 }
22517         };
22518         Roo.each(this.formats, function(f) {
22519             style.menu.items.push({
22520                 xtype :'MenuItem',
22521                 xns: Roo.bootstrap,
22522                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
22523                 tagname : f,
22524                 listeners : {
22525                     click : function()
22526                     {
22527                         editorcore.insertTag(this.tagname);
22528                         editor.focus();
22529                     }
22530                 }
22531                 
22532             });
22533         });
22534          children.push(style);   
22535             
22536             
22537         btn('bold',false,true);
22538         btn('italic',false,true);
22539         btn('align-left', 'justifyleft',true);
22540         btn('align-center', 'justifycenter',true);
22541         btn('align-right' , 'justifyright',true);
22542         btn('link', false, false, function(btn) {
22543             //Roo.log("create link?");
22544             var url = prompt(this.createLinkText, this.defaultLinkValue);
22545             if(url && url != 'http:/'+'/'){
22546                 this.editorcore.relayCmd('createlink', url);
22547             }
22548         }),
22549         btn('list','insertunorderedlist',true);
22550         btn('pencil', false,true, function(btn){
22551                 Roo.log(this);
22552                 
22553                 this.toggleSourceEdit(btn.pressed);
22554         });
22555         /*
22556         var cog = {
22557                 xtype: 'Button',
22558                 size : 'sm',
22559                 xns: Roo.bootstrap,
22560                 glyphicon : 'cog',
22561                 //html : 'submit'
22562                 menu : {
22563                     xtype: 'Menu',
22564                     xns: Roo.bootstrap,
22565                     items:  []
22566                 }
22567         };
22568         
22569         cog.menu.items.push({
22570             xtype :'MenuItem',
22571             xns: Roo.bootstrap,
22572             html : Clean styles,
22573             tagname : f,
22574             listeners : {
22575                 click : function()
22576                 {
22577                     editorcore.insertTag(this.tagname);
22578                     editor.focus();
22579                 }
22580             }
22581             
22582         });
22583        */
22584         
22585          
22586        this.xtype = 'NavSimplebar';
22587         
22588         for(var i=0;i< children.length;i++) {
22589             
22590             this.buttons.add(this.addxtypeChild(children[i]));
22591             
22592         }
22593         
22594         editor.on('editorevent', this.updateToolbar, this);
22595     },
22596     onBtnClick : function(id)
22597     {
22598        this.editorcore.relayCmd(id);
22599        this.editorcore.focus();
22600     },
22601     
22602     /**
22603      * Protected method that will not generally be called directly. It triggers
22604      * a toolbar update by reading the markup state of the current selection in the editor.
22605      */
22606     updateToolbar: function(){
22607
22608         if(!this.editorcore.activated){
22609             this.editor.onFirstFocus(); // is this neeed?
22610             return;
22611         }
22612
22613         var btns = this.buttons; 
22614         var doc = this.editorcore.doc;
22615         btns.get('bold').setActive(doc.queryCommandState('bold'));
22616         btns.get('italic').setActive(doc.queryCommandState('italic'));
22617         //btns.get('underline').setActive(doc.queryCommandState('underline'));
22618         
22619         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
22620         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
22621         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
22622         
22623         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
22624         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
22625          /*
22626         
22627         var ans = this.editorcore.getAllAncestors();
22628         if (this.formatCombo) {
22629             
22630             
22631             var store = this.formatCombo.store;
22632             this.formatCombo.setValue("");
22633             for (var i =0; i < ans.length;i++) {
22634                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
22635                     // select it..
22636                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
22637                     break;
22638                 }
22639             }
22640         }
22641         
22642         
22643         
22644         // hides menus... - so this cant be on a menu...
22645         Roo.bootstrap.MenuMgr.hideAll();
22646         */
22647         Roo.bootstrap.MenuMgr.hideAll();
22648         //this.editorsyncValue();
22649     },
22650     onFirstFocus: function() {
22651         this.buttons.each(function(item){
22652            item.enable();
22653         });
22654     },
22655     toggleSourceEdit : function(sourceEditMode){
22656         
22657           
22658         if(sourceEditMode){
22659             Roo.log("disabling buttons");
22660            this.buttons.each( function(item){
22661                 if(item.cmd != 'pencil'){
22662                     item.disable();
22663                 }
22664             });
22665           
22666         }else{
22667             Roo.log("enabling buttons");
22668             if(this.editorcore.initialized){
22669                 this.buttons.each( function(item){
22670                     item.enable();
22671                 });
22672             }
22673             
22674         }
22675         Roo.log("calling toggole on editor");
22676         // tell the editor that it's been pressed..
22677         this.editor.toggleSourceEdit(sourceEditMode);
22678        
22679     }
22680 });
22681
22682
22683
22684
22685
22686 /**
22687  * @class Roo.bootstrap.Table.AbstractSelectionModel
22688  * @extends Roo.util.Observable
22689  * Abstract base class for grid SelectionModels.  It provides the interface that should be
22690  * implemented by descendant classes.  This class should not be directly instantiated.
22691  * @constructor
22692  */
22693 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22694     this.locked = false;
22695     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22696 };
22697
22698
22699 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
22700     /** @ignore Called by the grid automatically. Do not call directly. */
22701     init : function(grid){
22702         this.grid = grid;
22703         this.initEvents();
22704     },
22705
22706     /**
22707      * Locks the selections.
22708      */
22709     lock : function(){
22710         this.locked = true;
22711     },
22712
22713     /**
22714      * Unlocks the selections.
22715      */
22716     unlock : function(){
22717         this.locked = false;
22718     },
22719
22720     /**
22721      * Returns true if the selections are locked.
22722      * @return {Boolean}
22723      */
22724     isLocked : function(){
22725         return this.locked;
22726     }
22727 });
22728 /**
22729  * @extends Roo.bootstrap.Table.AbstractSelectionModel
22730  * @class Roo.bootstrap.Table.RowSelectionModel
22731  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22732  * It supports multiple selections and keyboard selection/navigation. 
22733  * @constructor
22734  * @param {Object} config
22735  */
22736
22737 Roo.bootstrap.Table.RowSelectionModel = function(config){
22738     Roo.apply(this, config);
22739     this.selections = new Roo.util.MixedCollection(false, function(o){
22740         return o.id;
22741     });
22742
22743     this.last = false;
22744     this.lastActive = false;
22745
22746     this.addEvents({
22747         /**
22748              * @event selectionchange
22749              * Fires when the selection changes
22750              * @param {SelectionModel} this
22751              */
22752             "selectionchange" : true,
22753         /**
22754              * @event afterselectionchange
22755              * Fires after the selection changes (eg. by key press or clicking)
22756              * @param {SelectionModel} this
22757              */
22758             "afterselectionchange" : true,
22759         /**
22760              * @event beforerowselect
22761              * Fires when a row is selected being selected, return false to cancel.
22762              * @param {SelectionModel} this
22763              * @param {Number} rowIndex The selected index
22764              * @param {Boolean} keepExisting False if other selections will be cleared
22765              */
22766             "beforerowselect" : true,
22767         /**
22768              * @event rowselect
22769              * Fires when a row is selected.
22770              * @param {SelectionModel} this
22771              * @param {Number} rowIndex The selected index
22772              * @param {Roo.data.Record} r The record
22773              */
22774             "rowselect" : true,
22775         /**
22776              * @event rowdeselect
22777              * Fires when a row is deselected.
22778              * @param {SelectionModel} this
22779              * @param {Number} rowIndex The selected index
22780              */
22781         "rowdeselect" : true
22782     });
22783     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22784     this.locked = false;
22785  };
22786
22787 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
22788     /**
22789      * @cfg {Boolean} singleSelect
22790      * True to allow selection of only one row at a time (defaults to false)
22791      */
22792     singleSelect : false,
22793
22794     // private
22795     initEvents : function()
22796     {
22797
22798         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22799         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
22800         //}else{ // allow click to work like normal
22801          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
22802         //}
22803         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
22804         this.grid.on("rowclick", this.handleMouseDown, this);
22805         
22806         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22807             "up" : function(e){
22808                 if(!e.shiftKey){
22809                     this.selectPrevious(e.shiftKey);
22810                 }else if(this.last !== false && this.lastActive !== false){
22811                     var last = this.last;
22812                     this.selectRange(this.last,  this.lastActive-1);
22813                     this.grid.getView().focusRow(this.lastActive);
22814                     if(last !== false){
22815                         this.last = last;
22816                     }
22817                 }else{
22818                     this.selectFirstRow();
22819                 }
22820                 this.fireEvent("afterselectionchange", this);
22821             },
22822             "down" : function(e){
22823                 if(!e.shiftKey){
22824                     this.selectNext(e.shiftKey);
22825                 }else if(this.last !== false && this.lastActive !== false){
22826                     var last = this.last;
22827                     this.selectRange(this.last,  this.lastActive+1);
22828                     this.grid.getView().focusRow(this.lastActive);
22829                     if(last !== false){
22830                         this.last = last;
22831                     }
22832                 }else{
22833                     this.selectFirstRow();
22834                 }
22835                 this.fireEvent("afterselectionchange", this);
22836             },
22837             scope: this
22838         });
22839         this.grid.store.on('load', function(){
22840             this.selections.clear();
22841         },this);
22842         /*
22843         var view = this.grid.view;
22844         view.on("refresh", this.onRefresh, this);
22845         view.on("rowupdated", this.onRowUpdated, this);
22846         view.on("rowremoved", this.onRemove, this);
22847         */
22848     },
22849
22850     // private
22851     onRefresh : function()
22852     {
22853         var ds = this.grid.store, i, v = this.grid.view;
22854         var s = this.selections;
22855         s.each(function(r){
22856             if((i = ds.indexOfId(r.id)) != -1){
22857                 v.onRowSelect(i);
22858             }else{
22859                 s.remove(r);
22860             }
22861         });
22862     },
22863
22864     // private
22865     onRemove : function(v, index, r){
22866         this.selections.remove(r);
22867     },
22868
22869     // private
22870     onRowUpdated : function(v, index, r){
22871         if(this.isSelected(r)){
22872             v.onRowSelect(index);
22873         }
22874     },
22875
22876     /**
22877      * Select records.
22878      * @param {Array} records The records to select
22879      * @param {Boolean} keepExisting (optional) True to keep existing selections
22880      */
22881     selectRecords : function(records, keepExisting)
22882     {
22883         if(!keepExisting){
22884             this.clearSelections();
22885         }
22886             var ds = this.grid.store;
22887         for(var i = 0, len = records.length; i < len; i++){
22888             this.selectRow(ds.indexOf(records[i]), true);
22889         }
22890     },
22891
22892     /**
22893      * Gets the number of selected rows.
22894      * @return {Number}
22895      */
22896     getCount : function(){
22897         return this.selections.length;
22898     },
22899
22900     /**
22901      * Selects the first row in the grid.
22902      */
22903     selectFirstRow : function(){
22904         this.selectRow(0);
22905     },
22906
22907     /**
22908      * Select the last row.
22909      * @param {Boolean} keepExisting (optional) True to keep existing selections
22910      */
22911     selectLastRow : function(keepExisting){
22912         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22913         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
22914     },
22915
22916     /**
22917      * Selects the row immediately following the last selected row.
22918      * @param {Boolean} keepExisting (optional) True to keep existing selections
22919      */
22920     selectNext : function(keepExisting)
22921     {
22922             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
22923             this.selectRow(this.last+1, keepExisting);
22924             this.grid.getView().focusRow(this.last);
22925         }
22926     },
22927
22928     /**
22929      * Selects the row that precedes the last selected row.
22930      * @param {Boolean} keepExisting (optional) True to keep existing selections
22931      */
22932     selectPrevious : function(keepExisting){
22933         if(this.last){
22934             this.selectRow(this.last-1, keepExisting);
22935             this.grid.getView().focusRow(this.last);
22936         }
22937     },
22938
22939     /**
22940      * Returns the selected records
22941      * @return {Array} Array of selected records
22942      */
22943     getSelections : function(){
22944         return [].concat(this.selections.items);
22945     },
22946
22947     /**
22948      * Returns the first selected record.
22949      * @return {Record}
22950      */
22951     getSelected : function(){
22952         return this.selections.itemAt(0);
22953     },
22954
22955
22956     /**
22957      * Clears all selections.
22958      */
22959     clearSelections : function(fast)
22960     {
22961         if(this.locked) {
22962             return;
22963         }
22964         if(fast !== true){
22965                 var ds = this.grid.store;
22966             var s = this.selections;
22967             s.each(function(r){
22968                 this.deselectRow(ds.indexOfId(r.id));
22969             }, this);
22970             s.clear();
22971         }else{
22972             this.selections.clear();
22973         }
22974         this.last = false;
22975     },
22976
22977
22978     /**
22979      * Selects all rows.
22980      */
22981     selectAll : function(){
22982         if(this.locked) {
22983             return;
22984         }
22985         this.selections.clear();
22986         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
22987             this.selectRow(i, true);
22988         }
22989     },
22990
22991     /**
22992      * Returns True if there is a selection.
22993      * @return {Boolean}
22994      */
22995     hasSelection : function(){
22996         return this.selections.length > 0;
22997     },
22998
22999     /**
23000      * Returns True if the specified row is selected.
23001      * @param {Number/Record} record The record or index of the record to check
23002      * @return {Boolean}
23003      */
23004     isSelected : function(index){
23005             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23006         return (r && this.selections.key(r.id) ? true : false);
23007     },
23008
23009     /**
23010      * Returns True if the specified record id is selected.
23011      * @param {String} id The id of record to check
23012      * @return {Boolean}
23013      */
23014     isIdSelected : function(id){
23015         return (this.selections.key(id) ? true : false);
23016     },
23017
23018
23019     // private
23020     handleMouseDBClick : function(e, t){
23021         
23022     },
23023     // private
23024     handleMouseDown : function(e, t)
23025     {
23026             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23027         if(this.isLocked() || rowIndex < 0 ){
23028             return;
23029         };
23030         if(e.shiftKey && this.last !== false){
23031             var last = this.last;
23032             this.selectRange(last, rowIndex, e.ctrlKey);
23033             this.last = last; // reset the last
23034             t.focus();
23035     
23036         }else{
23037             var isSelected = this.isSelected(rowIndex);
23038             //Roo.log("select row:" + rowIndex);
23039             if(isSelected){
23040                 this.deselectRow(rowIndex);
23041             } else {
23042                         this.selectRow(rowIndex, true);
23043             }
23044     
23045             /*
23046                 if(e.button !== 0 && isSelected){
23047                 alert('rowIndex 2: ' + rowIndex);
23048                     view.focusRow(rowIndex);
23049                 }else if(e.ctrlKey && isSelected){
23050                     this.deselectRow(rowIndex);
23051                 }else if(!isSelected){
23052                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23053                     view.focusRow(rowIndex);
23054                 }
23055             */
23056         }
23057         this.fireEvent("afterselectionchange", this);
23058     },
23059     // private
23060     handleDragableRowClick :  function(grid, rowIndex, e) 
23061     {
23062         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23063             this.selectRow(rowIndex, false);
23064             grid.view.focusRow(rowIndex);
23065              this.fireEvent("afterselectionchange", this);
23066         }
23067     },
23068     
23069     /**
23070      * Selects multiple rows.
23071      * @param {Array} rows Array of the indexes of the row to select
23072      * @param {Boolean} keepExisting (optional) True to keep existing selections
23073      */
23074     selectRows : function(rows, keepExisting){
23075         if(!keepExisting){
23076             this.clearSelections();
23077         }
23078         for(var i = 0, len = rows.length; i < len; i++){
23079             this.selectRow(rows[i], true);
23080         }
23081     },
23082
23083     /**
23084      * Selects a range of rows. All rows in between startRow and endRow are also selected.
23085      * @param {Number} startRow The index of the first row in the range
23086      * @param {Number} endRow The index of the last row in the range
23087      * @param {Boolean} keepExisting (optional) True to retain existing selections
23088      */
23089     selectRange : function(startRow, endRow, keepExisting){
23090         if(this.locked) {
23091             return;
23092         }
23093         if(!keepExisting){
23094             this.clearSelections();
23095         }
23096         if(startRow <= endRow){
23097             for(var i = startRow; i <= endRow; i++){
23098                 this.selectRow(i, true);
23099             }
23100         }else{
23101             for(var i = startRow; i >= endRow; i--){
23102                 this.selectRow(i, true);
23103             }
23104         }
23105     },
23106
23107     /**
23108      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
23109      * @param {Number} startRow The index of the first row in the range
23110      * @param {Number} endRow The index of the last row in the range
23111      */
23112     deselectRange : function(startRow, endRow, preventViewNotify){
23113         if(this.locked) {
23114             return;
23115         }
23116         for(var i = startRow; i <= endRow; i++){
23117             this.deselectRow(i, preventViewNotify);
23118         }
23119     },
23120
23121     /**
23122      * Selects a row.
23123      * @param {Number} row The index of the row to select
23124      * @param {Boolean} keepExisting (optional) True to keep existing selections
23125      */
23126     selectRow : function(index, keepExisting, preventViewNotify)
23127     {
23128             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
23129             return;
23130         }
23131         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
23132             if(!keepExisting || this.singleSelect){
23133                 this.clearSelections();
23134             }
23135             
23136             var r = this.grid.store.getAt(index);
23137             //console.log('selectRow - record id :' + r.id);
23138             
23139             this.selections.add(r);
23140             this.last = this.lastActive = index;
23141             if(!preventViewNotify){
23142                 var proxy = new Roo.Element(
23143                                 this.grid.getRowDom(index)
23144                 );
23145                 proxy.addClass('bg-info info');
23146             }
23147             this.fireEvent("rowselect", this, index, r);
23148             this.fireEvent("selectionchange", this);
23149         }
23150     },
23151
23152     /**
23153      * Deselects a row.
23154      * @param {Number} row The index of the row to deselect
23155      */
23156     deselectRow : function(index, preventViewNotify)
23157     {
23158         if(this.locked) {
23159             return;
23160         }
23161         if(this.last == index){
23162             this.last = false;
23163         }
23164         if(this.lastActive == index){
23165             this.lastActive = false;
23166         }
23167         
23168         var r = this.grid.store.getAt(index);
23169         if (!r) {
23170             return;
23171         }
23172         
23173         this.selections.remove(r);
23174         //.console.log('deselectRow - record id :' + r.id);
23175         if(!preventViewNotify){
23176         
23177             var proxy = new Roo.Element(
23178                 this.grid.getRowDom(index)
23179             );
23180             proxy.removeClass('bg-info info');
23181         }
23182         this.fireEvent("rowdeselect", this, index);
23183         this.fireEvent("selectionchange", this);
23184     },
23185
23186     // private
23187     restoreLast : function(){
23188         if(this._last){
23189             this.last = this._last;
23190         }
23191     },
23192
23193     // private
23194     acceptsNav : function(row, col, cm){
23195         return !cm.isHidden(col) && cm.isCellEditable(col, row);
23196     },
23197
23198     // private
23199     onEditorKey : function(field, e){
23200         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
23201         if(k == e.TAB){
23202             e.stopEvent();
23203             ed.completeEdit();
23204             if(e.shiftKey){
23205                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
23206             }else{
23207                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
23208             }
23209         }else if(k == e.ENTER && !e.ctrlKey){
23210             e.stopEvent();
23211             ed.completeEdit();
23212             if(e.shiftKey){
23213                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
23214             }else{
23215                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
23216             }
23217         }else if(k == e.ESC){
23218             ed.cancelEdit();
23219         }
23220         if(newCell){
23221             g.startEditing(newCell[0], newCell[1]);
23222         }
23223     }
23224 });
23225 /*
23226  * Based on:
23227  * Ext JS Library 1.1.1
23228  * Copyright(c) 2006-2007, Ext JS, LLC.
23229  *
23230  * Originally Released Under LGPL - original licence link has changed is not relivant.
23231  *
23232  * Fork - LGPL
23233  * <script type="text/javascript">
23234  */
23235  
23236 /**
23237  * @class Roo.bootstrap.PagingToolbar
23238  * @extends Roo.bootstrap.NavSimplebar
23239  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
23240  * @constructor
23241  * Create a new PagingToolbar
23242  * @param {Object} config The config object
23243  * @param {Roo.data.Store} store
23244  */
23245 Roo.bootstrap.PagingToolbar = function(config)
23246 {
23247     // old args format still supported... - xtype is prefered..
23248         // created from xtype...
23249     
23250     this.ds = config.dataSource;
23251     
23252     if (config.store && !this.ds) {
23253         this.store= Roo.factory(config.store, Roo.data);
23254         this.ds = this.store;
23255         this.ds.xmodule = this.xmodule || false;
23256     }
23257     
23258     this.toolbarItems = [];
23259     if (config.items) {
23260         this.toolbarItems = config.items;
23261     }
23262     
23263     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
23264     
23265     this.cursor = 0;
23266     
23267     if (this.ds) { 
23268         this.bind(this.ds);
23269     }
23270     
23271     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
23272     
23273 };
23274
23275 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
23276     /**
23277      * @cfg {Roo.data.Store} dataSource
23278      * The underlying data store providing the paged data
23279      */
23280     /**
23281      * @cfg {String/HTMLElement/Element} container
23282      * container The id or element that will contain the toolbar
23283      */
23284     /**
23285      * @cfg {Boolean} displayInfo
23286      * True to display the displayMsg (defaults to false)
23287      */
23288     /**
23289      * @cfg {Number} pageSize
23290      * The number of records to display per page (defaults to 20)
23291      */
23292     pageSize: 20,
23293     /**
23294      * @cfg {String} displayMsg
23295      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
23296      */
23297     displayMsg : 'Displaying {0} - {1} of {2}',
23298     /**
23299      * @cfg {String} emptyMsg
23300      * The message to display when no records are found (defaults to "No data to display")
23301      */
23302     emptyMsg : 'No data to display',
23303     /**
23304      * Customizable piece of the default paging text (defaults to "Page")
23305      * @type String
23306      */
23307     beforePageText : "Page",
23308     /**
23309      * Customizable piece of the default paging text (defaults to "of %0")
23310      * @type String
23311      */
23312     afterPageText : "of {0}",
23313     /**
23314      * Customizable piece of the default paging text (defaults to "First Page")
23315      * @type String
23316      */
23317     firstText : "First Page",
23318     /**
23319      * Customizable piece of the default paging text (defaults to "Previous Page")
23320      * @type String
23321      */
23322     prevText : "Previous Page",
23323     /**
23324      * Customizable piece of the default paging text (defaults to "Next Page")
23325      * @type String
23326      */
23327     nextText : "Next Page",
23328     /**
23329      * Customizable piece of the default paging text (defaults to "Last Page")
23330      * @type String
23331      */
23332     lastText : "Last Page",
23333     /**
23334      * Customizable piece of the default paging text (defaults to "Refresh")
23335      * @type String
23336      */
23337     refreshText : "Refresh",
23338
23339     buttons : false,
23340     // private
23341     onRender : function(ct, position) 
23342     {
23343         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
23344         this.navgroup.parentId = this.id;
23345         this.navgroup.onRender(this.el, null);
23346         // add the buttons to the navgroup
23347         
23348         if(this.displayInfo){
23349             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
23350             this.displayEl = this.el.select('.x-paging-info', true).first();
23351 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
23352 //            this.displayEl = navel.el.select('span',true).first();
23353         }
23354         
23355         var _this = this;
23356         
23357         if(this.buttons){
23358             Roo.each(_this.buttons, function(e){ // this might need to use render????
23359                Roo.factory(e).onRender(_this.el, null);
23360             });
23361         }
23362             
23363         Roo.each(_this.toolbarItems, function(e) {
23364             _this.navgroup.addItem(e);
23365         });
23366         
23367         
23368         this.first = this.navgroup.addItem({
23369             tooltip: this.firstText,
23370             cls: "prev",
23371             icon : 'fa fa-backward',
23372             disabled: true,
23373             preventDefault: true,
23374             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
23375         });
23376         
23377         this.prev =  this.navgroup.addItem({
23378             tooltip: this.prevText,
23379             cls: "prev",
23380             icon : 'fa fa-step-backward',
23381             disabled: true,
23382             preventDefault: true,
23383             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
23384         });
23385     //this.addSeparator();
23386         
23387         
23388         var field = this.navgroup.addItem( {
23389             tagtype : 'span',
23390             cls : 'x-paging-position',
23391             
23392             html : this.beforePageText  +
23393                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
23394                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
23395          } ); //?? escaped?
23396         
23397         this.field = field.el.select('input', true).first();
23398         this.field.on("keydown", this.onPagingKeydown, this);
23399         this.field.on("focus", function(){this.dom.select();});
23400     
23401     
23402         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
23403         //this.field.setHeight(18);
23404         //this.addSeparator();
23405         this.next = this.navgroup.addItem({
23406             tooltip: this.nextText,
23407             cls: "next",
23408             html : ' <i class="fa fa-step-forward">',
23409             disabled: true,
23410             preventDefault: true,
23411             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
23412         });
23413         this.last = this.navgroup.addItem({
23414             tooltip: this.lastText,
23415             icon : 'fa fa-forward',
23416             cls: "next",
23417             disabled: true,
23418             preventDefault: true,
23419             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
23420         });
23421     //this.addSeparator();
23422         this.loading = this.navgroup.addItem({
23423             tooltip: this.refreshText,
23424             icon: 'fa fa-refresh',
23425             preventDefault: true,
23426             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
23427         });
23428         
23429     },
23430
23431     // private
23432     updateInfo : function(){
23433         if(this.displayEl){
23434             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
23435             var msg = count == 0 ?
23436                 this.emptyMsg :
23437                 String.format(
23438                     this.displayMsg,
23439                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
23440                 );
23441             this.displayEl.update(msg);
23442         }
23443     },
23444
23445     // private
23446     onLoad : function(ds, r, o){
23447        this.cursor = o.params ? o.params.start : 0;
23448        var d = this.getPageData(),
23449             ap = d.activePage,
23450             ps = d.pages;
23451         
23452        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
23453        this.field.dom.value = ap;
23454        this.first.setDisabled(ap == 1);
23455        this.prev.setDisabled(ap == 1);
23456        this.next.setDisabled(ap == ps);
23457        this.last.setDisabled(ap == ps);
23458        this.loading.enable();
23459        this.updateInfo();
23460     },
23461
23462     // private
23463     getPageData : function(){
23464         var total = this.ds.getTotalCount();
23465         return {
23466             total : total,
23467             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
23468             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
23469         };
23470     },
23471
23472     // private
23473     onLoadError : function(){
23474         this.loading.enable();
23475     },
23476
23477     // private
23478     onPagingKeydown : function(e){
23479         var k = e.getKey();
23480         var d = this.getPageData();
23481         if(k == e.RETURN){
23482             var v = this.field.dom.value, pageNum;
23483             if(!v || isNaN(pageNum = parseInt(v, 10))){
23484                 this.field.dom.value = d.activePage;
23485                 return;
23486             }
23487             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
23488             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23489             e.stopEvent();
23490         }
23491         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))
23492         {
23493           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
23494           this.field.dom.value = pageNum;
23495           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
23496           e.stopEvent();
23497         }
23498         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
23499         {
23500           var v = this.field.dom.value, pageNum; 
23501           var increment = (e.shiftKey) ? 10 : 1;
23502           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
23503                 increment *= -1;
23504           }
23505           if(!v || isNaN(pageNum = parseInt(v, 10))) {
23506             this.field.dom.value = d.activePage;
23507             return;
23508           }
23509           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
23510           {
23511             this.field.dom.value = parseInt(v, 10) + increment;
23512             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
23513             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23514           }
23515           e.stopEvent();
23516         }
23517     },
23518
23519     // private
23520     beforeLoad : function(){
23521         if(this.loading){
23522             this.loading.disable();
23523         }
23524     },
23525
23526     // private
23527     onClick : function(which){
23528         
23529         var ds = this.ds;
23530         if (!ds) {
23531             return;
23532         }
23533         
23534         switch(which){
23535             case "first":
23536                 ds.load({params:{start: 0, limit: this.pageSize}});
23537             break;
23538             case "prev":
23539                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
23540             break;
23541             case "next":
23542                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
23543             break;
23544             case "last":
23545                 var total = ds.getTotalCount();
23546                 var extra = total % this.pageSize;
23547                 var lastStart = extra ? (total - extra) : total-this.pageSize;
23548                 ds.load({params:{start: lastStart, limit: this.pageSize}});
23549             break;
23550             case "refresh":
23551                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
23552             break;
23553         }
23554     },
23555
23556     /**
23557      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
23558      * @param {Roo.data.Store} store The data store to unbind
23559      */
23560     unbind : function(ds){
23561         ds.un("beforeload", this.beforeLoad, this);
23562         ds.un("load", this.onLoad, this);
23563         ds.un("loadexception", this.onLoadError, this);
23564         ds.un("remove", this.updateInfo, this);
23565         ds.un("add", this.updateInfo, this);
23566         this.ds = undefined;
23567     },
23568
23569     /**
23570      * Binds the paging toolbar to the specified {@link Roo.data.Store}
23571      * @param {Roo.data.Store} store The data store to bind
23572      */
23573     bind : function(ds){
23574         ds.on("beforeload", this.beforeLoad, this);
23575         ds.on("load", this.onLoad, this);
23576         ds.on("loadexception", this.onLoadError, this);
23577         ds.on("remove", this.updateInfo, this);
23578         ds.on("add", this.updateInfo, this);
23579         this.ds = ds;
23580     }
23581 });/*
23582  * - LGPL
23583  *
23584  * element
23585  * 
23586  */
23587
23588 /**
23589  * @class Roo.bootstrap.MessageBar
23590  * @extends Roo.bootstrap.Component
23591  * Bootstrap MessageBar class
23592  * @cfg {String} html contents of the MessageBar
23593  * @cfg {String} weight (info | success | warning | danger) default info
23594  * @cfg {String} beforeClass insert the bar before the given class
23595  * @cfg {Boolean} closable (true | false) default false
23596  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
23597  * 
23598  * @constructor
23599  * Create a new Element
23600  * @param {Object} config The config object
23601  */
23602
23603 Roo.bootstrap.MessageBar = function(config){
23604     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
23605 };
23606
23607 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
23608     
23609     html: '',
23610     weight: 'info',
23611     closable: false,
23612     fixed: false,
23613     beforeClass: 'bootstrap-sticky-wrap',
23614     
23615     getAutoCreate : function(){
23616         
23617         var cfg = {
23618             tag: 'div',
23619             cls: 'alert alert-dismissable alert-' + this.weight,
23620             cn: [
23621                 {
23622                     tag: 'span',
23623                     cls: 'message',
23624                     html: this.html || ''
23625                 }
23626             ]
23627         };
23628         
23629         if(this.fixed){
23630             cfg.cls += ' alert-messages-fixed';
23631         }
23632         
23633         if(this.closable){
23634             cfg.cn.push({
23635                 tag: 'button',
23636                 cls: 'close',
23637                 html: 'x'
23638             });
23639         }
23640         
23641         return cfg;
23642     },
23643     
23644     onRender : function(ct, position)
23645     {
23646         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
23647         
23648         if(!this.el){
23649             var cfg = Roo.apply({},  this.getAutoCreate());
23650             cfg.id = Roo.id();
23651             
23652             if (this.cls) {
23653                 cfg.cls += ' ' + this.cls;
23654             }
23655             if (this.style) {
23656                 cfg.style = this.style;
23657             }
23658             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
23659             
23660             this.el.setVisibilityMode(Roo.Element.DISPLAY);
23661         }
23662         
23663         this.el.select('>button.close').on('click', this.hide, this);
23664         
23665     },
23666     
23667     show : function()
23668     {
23669         if (!this.rendered) {
23670             this.render();
23671         }
23672         
23673         this.el.show();
23674         
23675         this.fireEvent('show', this);
23676         
23677     },
23678     
23679     hide : function()
23680     {
23681         if (!this.rendered) {
23682             this.render();
23683         }
23684         
23685         this.el.hide();
23686         
23687         this.fireEvent('hide', this);
23688     },
23689     
23690     update : function()
23691     {
23692 //        var e = this.el.dom.firstChild;
23693 //        
23694 //        if(this.closable){
23695 //            e = e.nextSibling;
23696 //        }
23697 //        
23698 //        e.data = this.html || '';
23699
23700         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
23701     }
23702    
23703 });
23704
23705  
23706
23707      /*
23708  * - LGPL
23709  *
23710  * Graph
23711  * 
23712  */
23713
23714
23715 /**
23716  * @class Roo.bootstrap.Graph
23717  * @extends Roo.bootstrap.Component
23718  * Bootstrap Graph class
23719 > Prameters
23720  -sm {number} sm 4
23721  -md {number} md 5
23722  @cfg {String} graphtype  bar | vbar | pie
23723  @cfg {number} g_x coodinator | centre x (pie)
23724  @cfg {number} g_y coodinator | centre y (pie)
23725  @cfg {number} g_r radius (pie)
23726  @cfg {number} g_height height of the chart (respected by all elements in the set)
23727  @cfg {number} g_width width of the chart (respected by all elements in the set)
23728  @cfg {Object} title The title of the chart
23729     
23730  -{Array}  values
23731  -opts (object) options for the chart 
23732      o {
23733      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
23734      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
23735      o vgutter (number)
23736      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.
23737      o stacked (boolean) whether or not to tread values as in a stacked bar chart
23738      o to
23739      o stretch (boolean)
23740      o }
23741  -opts (object) options for the pie
23742      o{
23743      o cut
23744      o startAngle (number)
23745      o endAngle (number)
23746      } 
23747  *
23748  * @constructor
23749  * Create a new Input
23750  * @param {Object} config The config object
23751  */
23752
23753 Roo.bootstrap.Graph = function(config){
23754     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
23755     
23756     this.addEvents({
23757         // img events
23758         /**
23759          * @event click
23760          * The img click event for the img.
23761          * @param {Roo.EventObject} e
23762          */
23763         "click" : true
23764     });
23765 };
23766
23767 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
23768     
23769     sm: 4,
23770     md: 5,
23771     graphtype: 'bar',
23772     g_height: 250,
23773     g_width: 400,
23774     g_x: 50,
23775     g_y: 50,
23776     g_r: 30,
23777     opts:{
23778         //g_colors: this.colors,
23779         g_type: 'soft',
23780         g_gutter: '20%'
23781
23782     },
23783     title : false,
23784
23785     getAutoCreate : function(){
23786         
23787         var cfg = {
23788             tag: 'div',
23789             html : null
23790         };
23791         
23792         
23793         return  cfg;
23794     },
23795
23796     onRender : function(ct,position){
23797         
23798         
23799         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23800         
23801         if (typeof(Raphael) == 'undefined') {
23802             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23803             return;
23804         }
23805         
23806         this.raphael = Raphael(this.el.dom);
23807         
23808                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23809                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23810                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23811                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23812                 /*
23813                 r.text(160, 10, "Single Series Chart").attr(txtattr);
23814                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23815                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23816                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23817                 
23818                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23819                 r.barchart(330, 10, 300, 220, data1);
23820                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23821                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23822                 */
23823                 
23824                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23825                 // r.barchart(30, 30, 560, 250,  xdata, {
23826                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23827                 //     axis : "0 0 1 1",
23828                 //     axisxlabels :  xdata
23829                 //     //yvalues : cols,
23830                    
23831                 // });
23832 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23833 //        
23834 //        this.load(null,xdata,{
23835 //                axis : "0 0 1 1",
23836 //                axisxlabels :  xdata
23837 //                });
23838
23839     },
23840
23841     load : function(graphtype,xdata,opts)
23842     {
23843         this.raphael.clear();
23844         if(!graphtype) {
23845             graphtype = this.graphtype;
23846         }
23847         if(!opts){
23848             opts = this.opts;
23849         }
23850         var r = this.raphael,
23851             fin = function () {
23852                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23853             },
23854             fout = function () {
23855                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23856             },
23857             pfin = function() {
23858                 this.sector.stop();
23859                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23860
23861                 if (this.label) {
23862                     this.label[0].stop();
23863                     this.label[0].attr({ r: 7.5 });
23864                     this.label[1].attr({ "font-weight": 800 });
23865                 }
23866             },
23867             pfout = function() {
23868                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23869
23870                 if (this.label) {
23871                     this.label[0].animate({ r: 5 }, 500, "bounce");
23872                     this.label[1].attr({ "font-weight": 400 });
23873                 }
23874             };
23875
23876         switch(graphtype){
23877             case 'bar':
23878                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23879                 break;
23880             case 'hbar':
23881                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23882                 break;
23883             case 'pie':
23884 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
23885 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23886 //            
23887                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23888                 
23889                 break;
23890
23891         }
23892         
23893         if(this.title){
23894             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23895         }
23896         
23897     },
23898     
23899     setTitle: function(o)
23900     {
23901         this.title = o;
23902     },
23903     
23904     initEvents: function() {
23905         
23906         if(!this.href){
23907             this.el.on('click', this.onClick, this);
23908         }
23909     },
23910     
23911     onClick : function(e)
23912     {
23913         Roo.log('img onclick');
23914         this.fireEvent('click', this, e);
23915     }
23916    
23917 });
23918
23919  
23920 /*
23921  * - LGPL
23922  *
23923  * numberBox
23924  * 
23925  */
23926 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23927
23928 /**
23929  * @class Roo.bootstrap.dash.NumberBox
23930  * @extends Roo.bootstrap.Component
23931  * Bootstrap NumberBox class
23932  * @cfg {String} headline Box headline
23933  * @cfg {String} content Box content
23934  * @cfg {String} icon Box icon
23935  * @cfg {String} footer Footer text
23936  * @cfg {String} fhref Footer href
23937  * 
23938  * @constructor
23939  * Create a new NumberBox
23940  * @param {Object} config The config object
23941  */
23942
23943
23944 Roo.bootstrap.dash.NumberBox = function(config){
23945     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23946     
23947 };
23948
23949 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
23950     
23951     headline : '',
23952     content : '',
23953     icon : '',
23954     footer : '',
23955     fhref : '',
23956     ficon : '',
23957     
23958     getAutoCreate : function(){
23959         
23960         var cfg = {
23961             tag : 'div',
23962             cls : 'small-box ',
23963             cn : [
23964                 {
23965                     tag : 'div',
23966                     cls : 'inner',
23967                     cn :[
23968                         {
23969                             tag : 'h3',
23970                             cls : 'roo-headline',
23971                             html : this.headline
23972                         },
23973                         {
23974                             tag : 'p',
23975                             cls : 'roo-content',
23976                             html : this.content
23977                         }
23978                     ]
23979                 }
23980             ]
23981         };
23982         
23983         if(this.icon){
23984             cfg.cn.push({
23985                 tag : 'div',
23986                 cls : 'icon',
23987                 cn :[
23988                     {
23989                         tag : 'i',
23990                         cls : 'ion ' + this.icon
23991                     }
23992                 ]
23993             });
23994         }
23995         
23996         if(this.footer){
23997             var footer = {
23998                 tag : 'a',
23999                 cls : 'small-box-footer',
24000                 href : this.fhref || '#',
24001                 html : this.footer
24002             };
24003             
24004             cfg.cn.push(footer);
24005             
24006         }
24007         
24008         return  cfg;
24009     },
24010
24011     onRender : function(ct,position){
24012         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24013
24014
24015        
24016                 
24017     },
24018
24019     setHeadline: function (value)
24020     {
24021         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24022     },
24023     
24024     setFooter: function (value, href)
24025     {
24026         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24027         
24028         if(href){
24029             this.el.select('a.small-box-footer',true).first().attr('href', href);
24030         }
24031         
24032     },
24033
24034     setContent: function (value)
24035     {
24036         this.el.select('.roo-content',true).first().dom.innerHTML = value;
24037     },
24038
24039     initEvents: function() 
24040     {   
24041         
24042     }
24043     
24044 });
24045
24046  
24047 /*
24048  * - LGPL
24049  *
24050  * TabBox
24051  * 
24052  */
24053 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24054
24055 /**
24056  * @class Roo.bootstrap.dash.TabBox
24057  * @extends Roo.bootstrap.Component
24058  * Bootstrap TabBox class
24059  * @cfg {String} title Title of the TabBox
24060  * @cfg {String} icon Icon of the TabBox
24061  * @cfg {Boolean} showtabs (true|false) show the tabs default true
24062  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24063  * 
24064  * @constructor
24065  * Create a new TabBox
24066  * @param {Object} config The config object
24067  */
24068
24069
24070 Roo.bootstrap.dash.TabBox = function(config){
24071     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24072     this.addEvents({
24073         // raw events
24074         /**
24075          * @event addpane
24076          * When a pane is added
24077          * @param {Roo.bootstrap.dash.TabPane} pane
24078          */
24079         "addpane" : true,
24080         /**
24081          * @event activatepane
24082          * When a pane is activated
24083          * @param {Roo.bootstrap.dash.TabPane} pane
24084          */
24085         "activatepane" : true
24086         
24087          
24088     });
24089     
24090     this.panes = [];
24091 };
24092
24093 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
24094
24095     title : '',
24096     icon : false,
24097     showtabs : true,
24098     tabScrollable : false,
24099     
24100     getChildContainer : function()
24101     {
24102         return this.el.select('.tab-content', true).first();
24103     },
24104     
24105     getAutoCreate : function(){
24106         
24107         var header = {
24108             tag: 'li',
24109             cls: 'pull-left header',
24110             html: this.title,
24111             cn : []
24112         };
24113         
24114         if(this.icon){
24115             header.cn.push({
24116                 tag: 'i',
24117                 cls: 'fa ' + this.icon
24118             });
24119         }
24120         
24121         var h = {
24122             tag: 'ul',
24123             cls: 'nav nav-tabs pull-right',
24124             cn: [
24125                 header
24126             ]
24127         };
24128         
24129         if(this.tabScrollable){
24130             h = {
24131                 tag: 'div',
24132                 cls: 'tab-header',
24133                 cn: [
24134                     {
24135                         tag: 'ul',
24136                         cls: 'nav nav-tabs pull-right',
24137                         cn: [
24138                             header
24139                         ]
24140                     }
24141                 ]
24142             };
24143         }
24144         
24145         var cfg = {
24146             tag: 'div',
24147             cls: 'nav-tabs-custom',
24148             cn: [
24149                 h,
24150                 {
24151                     tag: 'div',
24152                     cls: 'tab-content no-padding',
24153                     cn: []
24154                 }
24155             ]
24156         };
24157
24158         return  cfg;
24159     },
24160     initEvents : function()
24161     {
24162         //Roo.log('add add pane handler');
24163         this.on('addpane', this.onAddPane, this);
24164     },
24165      /**
24166      * Updates the box title
24167      * @param {String} html to set the title to.
24168      */
24169     setTitle : function(value)
24170     {
24171         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
24172     },
24173     onAddPane : function(pane)
24174     {
24175         this.panes.push(pane);
24176         //Roo.log('addpane');
24177         //Roo.log(pane);
24178         // tabs are rendere left to right..
24179         if(!this.showtabs){
24180             return;
24181         }
24182         
24183         var ctr = this.el.select('.nav-tabs', true).first();
24184          
24185          
24186         var existing = ctr.select('.nav-tab',true);
24187         var qty = existing.getCount();;
24188         
24189         
24190         var tab = ctr.createChild({
24191             tag : 'li',
24192             cls : 'nav-tab' + (qty ? '' : ' active'),
24193             cn : [
24194                 {
24195                     tag : 'a',
24196                     href:'#',
24197                     html : pane.title
24198                 }
24199             ]
24200         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
24201         pane.tab = tab;
24202         
24203         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
24204         if (!qty) {
24205             pane.el.addClass('active');
24206         }
24207         
24208                 
24209     },
24210     onTabClick : function(ev,un,ob,pane)
24211     {
24212         //Roo.log('tab - prev default');
24213         ev.preventDefault();
24214         
24215         
24216         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
24217         pane.tab.addClass('active');
24218         //Roo.log(pane.title);
24219         this.getChildContainer().select('.tab-pane',true).removeClass('active');
24220         // technically we should have a deactivate event.. but maybe add later.
24221         // and it should not de-activate the selected tab...
24222         this.fireEvent('activatepane', pane);
24223         pane.el.addClass('active');
24224         pane.fireEvent('activate');
24225         
24226         
24227     },
24228     
24229     getActivePane : function()
24230     {
24231         var r = false;
24232         Roo.each(this.panes, function(p) {
24233             if(p.el.hasClass('active')){
24234                 r = p;
24235                 return false;
24236             }
24237             
24238             return;
24239         });
24240         
24241         return r;
24242     }
24243     
24244     
24245 });
24246
24247  
24248 /*
24249  * - LGPL
24250  *
24251  * Tab pane
24252  * 
24253  */
24254 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24255 /**
24256  * @class Roo.bootstrap.TabPane
24257  * @extends Roo.bootstrap.Component
24258  * Bootstrap TabPane class
24259  * @cfg {Boolean} active (false | true) Default false
24260  * @cfg {String} title title of panel
24261
24262  * 
24263  * @constructor
24264  * Create a new TabPane
24265  * @param {Object} config The config object
24266  */
24267
24268 Roo.bootstrap.dash.TabPane = function(config){
24269     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
24270     
24271     this.addEvents({
24272         // raw events
24273         /**
24274          * @event activate
24275          * When a pane is activated
24276          * @param {Roo.bootstrap.dash.TabPane} pane
24277          */
24278         "activate" : true
24279          
24280     });
24281 };
24282
24283 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
24284     
24285     active : false,
24286     title : '',
24287     
24288     // the tabBox that this is attached to.
24289     tab : false,
24290      
24291     getAutoCreate : function() 
24292     {
24293         var cfg = {
24294             tag: 'div',
24295             cls: 'tab-pane'
24296         };
24297         
24298         if(this.active){
24299             cfg.cls += ' active';
24300         }
24301         
24302         return cfg;
24303     },
24304     initEvents  : function()
24305     {
24306         //Roo.log('trigger add pane handler');
24307         this.parent().fireEvent('addpane', this)
24308     },
24309     
24310      /**
24311      * Updates the tab title 
24312      * @param {String} html to set the title to.
24313      */
24314     setTitle: function(str)
24315     {
24316         if (!this.tab) {
24317             return;
24318         }
24319         this.title = str;
24320         this.tab.select('a', true).first().dom.innerHTML = str;
24321         
24322     }
24323     
24324     
24325     
24326 });
24327
24328  
24329
24330
24331  /*
24332  * - LGPL
24333  *
24334  * menu
24335  * 
24336  */
24337 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24338
24339 /**
24340  * @class Roo.bootstrap.menu.Menu
24341  * @extends Roo.bootstrap.Component
24342  * Bootstrap Menu class - container for Menu
24343  * @cfg {String} html Text of the menu
24344  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
24345  * @cfg {String} icon Font awesome icon
24346  * @cfg {String} pos Menu align to (top | bottom) default bottom
24347  * 
24348  * 
24349  * @constructor
24350  * Create a new Menu
24351  * @param {Object} config The config object
24352  */
24353
24354
24355 Roo.bootstrap.menu.Menu = function(config){
24356     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
24357     
24358     this.addEvents({
24359         /**
24360          * @event beforeshow
24361          * Fires before this menu is displayed
24362          * @param {Roo.bootstrap.menu.Menu} this
24363          */
24364         beforeshow : true,
24365         /**
24366          * @event beforehide
24367          * Fires before this menu is hidden
24368          * @param {Roo.bootstrap.menu.Menu} this
24369          */
24370         beforehide : true,
24371         /**
24372          * @event show
24373          * Fires after this menu is displayed
24374          * @param {Roo.bootstrap.menu.Menu} this
24375          */
24376         show : true,
24377         /**
24378          * @event hide
24379          * Fires after this menu is hidden
24380          * @param {Roo.bootstrap.menu.Menu} this
24381          */
24382         hide : true,
24383         /**
24384          * @event click
24385          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
24386          * @param {Roo.bootstrap.menu.Menu} this
24387          * @param {Roo.EventObject} e
24388          */
24389         click : true
24390     });
24391     
24392 };
24393
24394 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
24395     
24396     submenu : false,
24397     html : '',
24398     weight : 'default',
24399     icon : false,
24400     pos : 'bottom',
24401     
24402     
24403     getChildContainer : function() {
24404         if(this.isSubMenu){
24405             return this.el;
24406         }
24407         
24408         return this.el.select('ul.dropdown-menu', true).first();  
24409     },
24410     
24411     getAutoCreate : function()
24412     {
24413         var text = [
24414             {
24415                 tag : 'span',
24416                 cls : 'roo-menu-text',
24417                 html : this.html
24418             }
24419         ];
24420         
24421         if(this.icon){
24422             text.unshift({
24423                 tag : 'i',
24424                 cls : 'fa ' + this.icon
24425             })
24426         }
24427         
24428         
24429         var cfg = {
24430             tag : 'div',
24431             cls : 'btn-group',
24432             cn : [
24433                 {
24434                     tag : 'button',
24435                     cls : 'dropdown-button btn btn-' + this.weight,
24436                     cn : text
24437                 },
24438                 {
24439                     tag : 'button',
24440                     cls : 'dropdown-toggle btn btn-' + this.weight,
24441                     cn : [
24442                         {
24443                             tag : 'span',
24444                             cls : 'caret'
24445                         }
24446                     ]
24447                 },
24448                 {
24449                     tag : 'ul',
24450                     cls : 'dropdown-menu'
24451                 }
24452             ]
24453             
24454         };
24455         
24456         if(this.pos == 'top'){
24457             cfg.cls += ' dropup';
24458         }
24459         
24460         if(this.isSubMenu){
24461             cfg = {
24462                 tag : 'ul',
24463                 cls : 'dropdown-menu'
24464             }
24465         }
24466         
24467         return cfg;
24468     },
24469     
24470     onRender : function(ct, position)
24471     {
24472         this.isSubMenu = ct.hasClass('dropdown-submenu');
24473         
24474         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
24475     },
24476     
24477     initEvents : function() 
24478     {
24479         if(this.isSubMenu){
24480             return;
24481         }
24482         
24483         this.hidden = true;
24484         
24485         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
24486         this.triggerEl.on('click', this.onTriggerPress, this);
24487         
24488         this.buttonEl = this.el.select('button.dropdown-button', true).first();
24489         this.buttonEl.on('click', this.onClick, this);
24490         
24491     },
24492     
24493     list : function()
24494     {
24495         if(this.isSubMenu){
24496             return this.el;
24497         }
24498         
24499         return this.el.select('ul.dropdown-menu', true).first();
24500     },
24501     
24502     onClick : function(e)
24503     {
24504         this.fireEvent("click", this, e);
24505     },
24506     
24507     onTriggerPress  : function(e)
24508     {   
24509         if (this.isVisible()) {
24510             this.hide();
24511         } else {
24512             this.show();
24513         }
24514     },
24515     
24516     isVisible : function(){
24517         return !this.hidden;
24518     },
24519     
24520     show : function()
24521     {
24522         this.fireEvent("beforeshow", this);
24523         
24524         this.hidden = false;
24525         this.el.addClass('open');
24526         
24527         Roo.get(document).on("mouseup", this.onMouseUp, this);
24528         
24529         this.fireEvent("show", this);
24530         
24531         
24532     },
24533     
24534     hide : function()
24535     {
24536         this.fireEvent("beforehide", this);
24537         
24538         this.hidden = true;
24539         this.el.removeClass('open');
24540         
24541         Roo.get(document).un("mouseup", this.onMouseUp);
24542         
24543         this.fireEvent("hide", this);
24544     },
24545     
24546     onMouseUp : function()
24547     {
24548         this.hide();
24549     }
24550     
24551 });
24552
24553  
24554  /*
24555  * - LGPL
24556  *
24557  * menu item
24558  * 
24559  */
24560 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24561
24562 /**
24563  * @class Roo.bootstrap.menu.Item
24564  * @extends Roo.bootstrap.Component
24565  * Bootstrap MenuItem class
24566  * @cfg {Boolean} submenu (true | false) default false
24567  * @cfg {String} html text of the item
24568  * @cfg {String} href the link
24569  * @cfg {Boolean} disable (true | false) default false
24570  * @cfg {Boolean} preventDefault (true | false) default true
24571  * @cfg {String} icon Font awesome icon
24572  * @cfg {String} pos Submenu align to (left | right) default right 
24573  * 
24574  * 
24575  * @constructor
24576  * Create a new Item
24577  * @param {Object} config The config object
24578  */
24579
24580
24581 Roo.bootstrap.menu.Item = function(config){
24582     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
24583     this.addEvents({
24584         /**
24585          * @event mouseover
24586          * Fires when the mouse is hovering over this menu
24587          * @param {Roo.bootstrap.menu.Item} this
24588          * @param {Roo.EventObject} e
24589          */
24590         mouseover : true,
24591         /**
24592          * @event mouseout
24593          * Fires when the mouse exits this menu
24594          * @param {Roo.bootstrap.menu.Item} this
24595          * @param {Roo.EventObject} e
24596          */
24597         mouseout : true,
24598         // raw events
24599         /**
24600          * @event click
24601          * The raw click event for the entire grid.
24602          * @param {Roo.EventObject} e
24603          */
24604         click : true
24605     });
24606 };
24607
24608 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
24609     
24610     submenu : false,
24611     href : '',
24612     html : '',
24613     preventDefault: true,
24614     disable : false,
24615     icon : false,
24616     pos : 'right',
24617     
24618     getAutoCreate : function()
24619     {
24620         var text = [
24621             {
24622                 tag : 'span',
24623                 cls : 'roo-menu-item-text',
24624                 html : this.html
24625             }
24626         ];
24627         
24628         if(this.icon){
24629             text.unshift({
24630                 tag : 'i',
24631                 cls : 'fa ' + this.icon
24632             })
24633         }
24634         
24635         var cfg = {
24636             tag : 'li',
24637             cn : [
24638                 {
24639                     tag : 'a',
24640                     href : this.href || '#',
24641                     cn : text
24642                 }
24643             ]
24644         };
24645         
24646         if(this.disable){
24647             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
24648         }
24649         
24650         if(this.submenu){
24651             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
24652             
24653             if(this.pos == 'left'){
24654                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
24655             }
24656         }
24657         
24658         return cfg;
24659     },
24660     
24661     initEvents : function() 
24662     {
24663         this.el.on('mouseover', this.onMouseOver, this);
24664         this.el.on('mouseout', this.onMouseOut, this);
24665         
24666         this.el.select('a', true).first().on('click', this.onClick, this);
24667         
24668     },
24669     
24670     onClick : function(e)
24671     {
24672         if(this.preventDefault){
24673             e.preventDefault();
24674         }
24675         
24676         this.fireEvent("click", this, e);
24677     },
24678     
24679     onMouseOver : function(e)
24680     {
24681         if(this.submenu && this.pos == 'left'){
24682             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
24683         }
24684         
24685         this.fireEvent("mouseover", this, e);
24686     },
24687     
24688     onMouseOut : function(e)
24689     {
24690         this.fireEvent("mouseout", this, e);
24691     }
24692 });
24693
24694  
24695
24696  /*
24697  * - LGPL
24698  *
24699  * menu separator
24700  * 
24701  */
24702 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24703
24704 /**
24705  * @class Roo.bootstrap.menu.Separator
24706  * @extends Roo.bootstrap.Component
24707  * Bootstrap Separator class
24708  * 
24709  * @constructor
24710  * Create a new Separator
24711  * @param {Object} config The config object
24712  */
24713
24714
24715 Roo.bootstrap.menu.Separator = function(config){
24716     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
24717 };
24718
24719 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
24720     
24721     getAutoCreate : function(){
24722         var cfg = {
24723             tag : 'li',
24724             cls: 'divider'
24725         };
24726         
24727         return cfg;
24728     }
24729    
24730 });
24731
24732  
24733
24734  /*
24735  * - LGPL
24736  *
24737  * Tooltip
24738  * 
24739  */
24740
24741 /**
24742  * @class Roo.bootstrap.Tooltip
24743  * Bootstrap Tooltip class
24744  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
24745  * to determine which dom element triggers the tooltip.
24746  * 
24747  * It needs to add support for additional attributes like tooltip-position
24748  * 
24749  * @constructor
24750  * Create a new Toolti
24751  * @param {Object} config The config object
24752  */
24753
24754 Roo.bootstrap.Tooltip = function(config){
24755     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
24756 };
24757
24758 Roo.apply(Roo.bootstrap.Tooltip, {
24759     /**
24760      * @function init initialize tooltip monitoring.
24761      * @static
24762      */
24763     currentEl : false,
24764     currentTip : false,
24765     currentRegion : false,
24766     
24767     //  init : delay?
24768     
24769     init : function()
24770     {
24771         Roo.get(document).on('mouseover', this.enter ,this);
24772         Roo.get(document).on('mouseout', this.leave, this);
24773          
24774         
24775         this.currentTip = new Roo.bootstrap.Tooltip();
24776     },
24777     
24778     enter : function(ev)
24779     {
24780         var dom = ev.getTarget();
24781         
24782         //Roo.log(['enter',dom]);
24783         var el = Roo.fly(dom);
24784         if (this.currentEl) {
24785             //Roo.log(dom);
24786             //Roo.log(this.currentEl);
24787             //Roo.log(this.currentEl.contains(dom));
24788             if (this.currentEl == el) {
24789                 return;
24790             }
24791             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24792                 return;
24793             }
24794
24795         }
24796         
24797         if (this.currentTip.el) {
24798             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24799         }    
24800         //Roo.log(ev);
24801         
24802         if(!el || el.dom == document){
24803             return;
24804         }
24805         
24806         var bindEl = el;
24807         
24808         // you can not look for children, as if el is the body.. then everythign is the child..
24809         if (!el.attr('tooltip')) { //
24810             if (!el.select("[tooltip]").elements.length) {
24811                 return;
24812             }
24813             // is the mouse over this child...?
24814             bindEl = el.select("[tooltip]").first();
24815             var xy = ev.getXY();
24816             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24817                 //Roo.log("not in region.");
24818                 return;
24819             }
24820             //Roo.log("child element over..");
24821             
24822         }
24823         this.currentEl = bindEl;
24824         this.currentTip.bind(bindEl);
24825         this.currentRegion = Roo.lib.Region.getRegion(dom);
24826         this.currentTip.enter();
24827         
24828     },
24829     leave : function(ev)
24830     {
24831         var dom = ev.getTarget();
24832         //Roo.log(['leave',dom]);
24833         if (!this.currentEl) {
24834             return;
24835         }
24836         
24837         
24838         if (dom != this.currentEl.dom) {
24839             return;
24840         }
24841         var xy = ev.getXY();
24842         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
24843             return;
24844         }
24845         // only activate leave if mouse cursor is outside... bounding box..
24846         
24847         
24848         
24849         
24850         if (this.currentTip) {
24851             this.currentTip.leave();
24852         }
24853         //Roo.log('clear currentEl');
24854         this.currentEl = false;
24855         
24856         
24857     },
24858     alignment : {
24859         'left' : ['r-l', [-2,0], 'right'],
24860         'right' : ['l-r', [2,0], 'left'],
24861         'bottom' : ['t-b', [0,2], 'top'],
24862         'top' : [ 'b-t', [0,-2], 'bottom']
24863     }
24864     
24865 });
24866
24867
24868 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
24869     
24870     
24871     bindEl : false,
24872     
24873     delay : null, // can be { show : 300 , hide: 500}
24874     
24875     timeout : null,
24876     
24877     hoverState : null, //???
24878     
24879     placement : 'bottom', 
24880     
24881     getAutoCreate : function(){
24882     
24883         var cfg = {
24884            cls : 'tooltip',
24885            role : 'tooltip',
24886            cn : [
24887                 {
24888                     cls : 'tooltip-arrow'
24889                 },
24890                 {
24891                     cls : 'tooltip-inner'
24892                 }
24893            ]
24894         };
24895         
24896         return cfg;
24897     },
24898     bind : function(el)
24899     {
24900         this.bindEl = el;
24901     },
24902       
24903     
24904     enter : function () {
24905        
24906         if (this.timeout != null) {
24907             clearTimeout(this.timeout);
24908         }
24909         
24910         this.hoverState = 'in';
24911          //Roo.log("enter - show");
24912         if (!this.delay || !this.delay.show) {
24913             this.show();
24914             return;
24915         }
24916         var _t = this;
24917         this.timeout = setTimeout(function () {
24918             if (_t.hoverState == 'in') {
24919                 _t.show();
24920             }
24921         }, this.delay.show);
24922     },
24923     leave : function()
24924     {
24925         clearTimeout(this.timeout);
24926     
24927         this.hoverState = 'out';
24928          if (!this.delay || !this.delay.hide) {
24929             this.hide();
24930             return;
24931         }
24932        
24933         var _t = this;
24934         this.timeout = setTimeout(function () {
24935             //Roo.log("leave - timeout");
24936             
24937             if (_t.hoverState == 'out') {
24938                 _t.hide();
24939                 Roo.bootstrap.Tooltip.currentEl = false;
24940             }
24941         }, delay);
24942     },
24943     
24944     show : function ()
24945     {
24946         if (!this.el) {
24947             this.render(document.body);
24948         }
24949         // set content.
24950         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24951         
24952         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24953         
24954         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24955         
24956         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24957         
24958         var placement = typeof this.placement == 'function' ?
24959             this.placement.call(this, this.el, on_el) :
24960             this.placement;
24961             
24962         var autoToken = /\s?auto?\s?/i;
24963         var autoPlace = autoToken.test(placement);
24964         if (autoPlace) {
24965             placement = placement.replace(autoToken, '') || 'top';
24966         }
24967         
24968         //this.el.detach()
24969         //this.el.setXY([0,0]);
24970         this.el.show();
24971         //this.el.dom.style.display='block';
24972         
24973         //this.el.appendTo(on_el);
24974         
24975         var p = this.getPosition();
24976         var box = this.el.getBox();
24977         
24978         if (autoPlace) {
24979             // fixme..
24980         }
24981         
24982         var align = Roo.bootstrap.Tooltip.alignment[placement];
24983         
24984         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
24985         
24986         if(placement == 'top' || placement == 'bottom'){
24987             if(xy[0] < 0){
24988                 placement = 'right';
24989             }
24990             
24991             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
24992                 placement = 'left';
24993             }
24994             
24995             var scroll = Roo.select('body', true).first().getScroll();
24996             
24997             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
24998                 placement = 'top';
24999             }
25000             
25001         }
25002         
25003         align = Roo.bootstrap.Tooltip.alignment[placement];
25004         
25005         this.el.alignTo(this.bindEl, align[0],align[1]);
25006         //var arrow = this.el.select('.arrow',true).first();
25007         //arrow.set(align[2], 
25008         
25009         this.el.addClass(placement);
25010         
25011         this.el.addClass('in fade');
25012         
25013         this.hoverState = null;
25014         
25015         if (this.el.hasClass('fade')) {
25016             // fade it?
25017         }
25018         
25019     },
25020     hide : function()
25021     {
25022          
25023         if (!this.el) {
25024             return;
25025         }
25026         //this.el.setXY([0,0]);
25027         this.el.removeClass('in');
25028         //this.el.hide();
25029         
25030     }
25031     
25032 });
25033  
25034
25035  /*
25036  * - LGPL
25037  *
25038  * Location Picker
25039  * 
25040  */
25041
25042 /**
25043  * @class Roo.bootstrap.LocationPicker
25044  * @extends Roo.bootstrap.Component
25045  * Bootstrap LocationPicker class
25046  * @cfg {Number} latitude Position when init default 0
25047  * @cfg {Number} longitude Position when init default 0
25048  * @cfg {Number} zoom default 15
25049  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25050  * @cfg {Boolean} mapTypeControl default false
25051  * @cfg {Boolean} disableDoubleClickZoom default false
25052  * @cfg {Boolean} scrollwheel default true
25053  * @cfg {Boolean} streetViewControl default false
25054  * @cfg {Number} radius default 0
25055  * @cfg {String} locationName
25056  * @cfg {Boolean} draggable default true
25057  * @cfg {Boolean} enableAutocomplete default false
25058  * @cfg {Boolean} enableReverseGeocode default true
25059  * @cfg {String} markerTitle
25060  * 
25061  * @constructor
25062  * Create a new LocationPicker
25063  * @param {Object} config The config object
25064  */
25065
25066
25067 Roo.bootstrap.LocationPicker = function(config){
25068     
25069     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25070     
25071     this.addEvents({
25072         /**
25073          * @event initial
25074          * Fires when the picker initialized.
25075          * @param {Roo.bootstrap.LocationPicker} this
25076          * @param {Google Location} location
25077          */
25078         initial : true,
25079         /**
25080          * @event positionchanged
25081          * Fires when the picker position changed.
25082          * @param {Roo.bootstrap.LocationPicker} this
25083          * @param {Google Location} location
25084          */
25085         positionchanged : true,
25086         /**
25087          * @event resize
25088          * Fires when the map resize.
25089          * @param {Roo.bootstrap.LocationPicker} this
25090          */
25091         resize : true,
25092         /**
25093          * @event show
25094          * Fires when the map show.
25095          * @param {Roo.bootstrap.LocationPicker} this
25096          */
25097         show : true,
25098         /**
25099          * @event hide
25100          * Fires when the map hide.
25101          * @param {Roo.bootstrap.LocationPicker} this
25102          */
25103         hide : true,
25104         /**
25105          * @event mapClick
25106          * Fires when click the map.
25107          * @param {Roo.bootstrap.LocationPicker} this
25108          * @param {Map event} e
25109          */
25110         mapClick : true,
25111         /**
25112          * @event mapRightClick
25113          * Fires when right click the map.
25114          * @param {Roo.bootstrap.LocationPicker} this
25115          * @param {Map event} e
25116          */
25117         mapRightClick : true,
25118         /**
25119          * @event markerClick
25120          * Fires when click the marker.
25121          * @param {Roo.bootstrap.LocationPicker} this
25122          * @param {Map event} e
25123          */
25124         markerClick : true,
25125         /**
25126          * @event markerRightClick
25127          * Fires when right click the marker.
25128          * @param {Roo.bootstrap.LocationPicker} this
25129          * @param {Map event} e
25130          */
25131         markerRightClick : true,
25132         /**
25133          * @event OverlayViewDraw
25134          * Fires when OverlayView Draw
25135          * @param {Roo.bootstrap.LocationPicker} this
25136          */
25137         OverlayViewDraw : true,
25138         /**
25139          * @event OverlayViewOnAdd
25140          * Fires when OverlayView Draw
25141          * @param {Roo.bootstrap.LocationPicker} this
25142          */
25143         OverlayViewOnAdd : true,
25144         /**
25145          * @event OverlayViewOnRemove
25146          * Fires when OverlayView Draw
25147          * @param {Roo.bootstrap.LocationPicker} this
25148          */
25149         OverlayViewOnRemove : true,
25150         /**
25151          * @event OverlayViewShow
25152          * Fires when OverlayView Draw
25153          * @param {Roo.bootstrap.LocationPicker} this
25154          * @param {Pixel} cpx
25155          */
25156         OverlayViewShow : true,
25157         /**
25158          * @event OverlayViewHide
25159          * Fires when OverlayView Draw
25160          * @param {Roo.bootstrap.LocationPicker} this
25161          */
25162         OverlayViewHide : true,
25163         /**
25164          * @event loadexception
25165          * Fires when load google lib failed.
25166          * @param {Roo.bootstrap.LocationPicker} this
25167          */
25168         loadexception : true
25169     });
25170         
25171 };
25172
25173 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
25174     
25175     gMapContext: false,
25176     
25177     latitude: 0,
25178     longitude: 0,
25179     zoom: 15,
25180     mapTypeId: false,
25181     mapTypeControl: false,
25182     disableDoubleClickZoom: false,
25183     scrollwheel: true,
25184     streetViewControl: false,
25185     radius: 0,
25186     locationName: '',
25187     draggable: true,
25188     enableAutocomplete: false,
25189     enableReverseGeocode: true,
25190     markerTitle: '',
25191     
25192     getAutoCreate: function()
25193     {
25194
25195         var cfg = {
25196             tag: 'div',
25197             cls: 'roo-location-picker'
25198         };
25199         
25200         return cfg
25201     },
25202     
25203     initEvents: function(ct, position)
25204     {       
25205         if(!this.el.getWidth() || this.isApplied()){
25206             return;
25207         }
25208         
25209         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25210         
25211         this.initial();
25212     },
25213     
25214     initial: function()
25215     {
25216         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
25217             this.fireEvent('loadexception', this);
25218             return;
25219         }
25220         
25221         if(!this.mapTypeId){
25222             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
25223         }
25224         
25225         this.gMapContext = this.GMapContext();
25226         
25227         this.initOverlayView();
25228         
25229         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
25230         
25231         var _this = this;
25232                 
25233         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
25234             _this.setPosition(_this.gMapContext.marker.position);
25235         });
25236         
25237         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
25238             _this.fireEvent('mapClick', this, event);
25239             
25240         });
25241
25242         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
25243             _this.fireEvent('mapRightClick', this, event);
25244             
25245         });
25246         
25247         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
25248             _this.fireEvent('markerClick', this, event);
25249             
25250         });
25251
25252         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
25253             _this.fireEvent('markerRightClick', this, event);
25254             
25255         });
25256         
25257         this.setPosition(this.gMapContext.location);
25258         
25259         this.fireEvent('initial', this, this.gMapContext.location);
25260     },
25261     
25262     initOverlayView: function()
25263     {
25264         var _this = this;
25265         
25266         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
25267             
25268             draw: function()
25269             {
25270                 _this.fireEvent('OverlayViewDraw', _this);
25271             },
25272             
25273             onAdd: function()
25274             {
25275                 _this.fireEvent('OverlayViewOnAdd', _this);
25276             },
25277             
25278             onRemove: function()
25279             {
25280                 _this.fireEvent('OverlayViewOnRemove', _this);
25281             },
25282             
25283             show: function(cpx)
25284             {
25285                 _this.fireEvent('OverlayViewShow', _this, cpx);
25286             },
25287             
25288             hide: function()
25289             {
25290                 _this.fireEvent('OverlayViewHide', _this);
25291             }
25292             
25293         });
25294     },
25295     
25296     fromLatLngToContainerPixel: function(event)
25297     {
25298         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
25299     },
25300     
25301     isApplied: function() 
25302     {
25303         return this.getGmapContext() == false ? false : true;
25304     },
25305     
25306     getGmapContext: function() 
25307     {
25308         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
25309     },
25310     
25311     GMapContext: function() 
25312     {
25313         var position = new google.maps.LatLng(this.latitude, this.longitude);
25314         
25315         var _map = new google.maps.Map(this.el.dom, {
25316             center: position,
25317             zoom: this.zoom,
25318             mapTypeId: this.mapTypeId,
25319             mapTypeControl: this.mapTypeControl,
25320             disableDoubleClickZoom: this.disableDoubleClickZoom,
25321             scrollwheel: this.scrollwheel,
25322             streetViewControl: this.streetViewControl,
25323             locationName: this.locationName,
25324             draggable: this.draggable,
25325             enableAutocomplete: this.enableAutocomplete,
25326             enableReverseGeocode: this.enableReverseGeocode
25327         });
25328         
25329         var _marker = new google.maps.Marker({
25330             position: position,
25331             map: _map,
25332             title: this.markerTitle,
25333             draggable: this.draggable
25334         });
25335         
25336         return {
25337             map: _map,
25338             marker: _marker,
25339             circle: null,
25340             location: position,
25341             radius: this.radius,
25342             locationName: this.locationName,
25343             addressComponents: {
25344                 formatted_address: null,
25345                 addressLine1: null,
25346                 addressLine2: null,
25347                 streetName: null,
25348                 streetNumber: null,
25349                 city: null,
25350                 district: null,
25351                 state: null,
25352                 stateOrProvince: null
25353             },
25354             settings: this,
25355             domContainer: this.el.dom,
25356             geodecoder: new google.maps.Geocoder()
25357         };
25358     },
25359     
25360     drawCircle: function(center, radius, options) 
25361     {
25362         if (this.gMapContext.circle != null) {
25363             this.gMapContext.circle.setMap(null);
25364         }
25365         if (radius > 0) {
25366             radius *= 1;
25367             options = Roo.apply({}, options, {
25368                 strokeColor: "#0000FF",
25369                 strokeOpacity: .35,
25370                 strokeWeight: 2,
25371                 fillColor: "#0000FF",
25372                 fillOpacity: .2
25373             });
25374             
25375             options.map = this.gMapContext.map;
25376             options.radius = radius;
25377             options.center = center;
25378             this.gMapContext.circle = new google.maps.Circle(options);
25379             return this.gMapContext.circle;
25380         }
25381         
25382         return null;
25383     },
25384     
25385     setPosition: function(location) 
25386     {
25387         this.gMapContext.location = location;
25388         this.gMapContext.marker.setPosition(location);
25389         this.gMapContext.map.panTo(location);
25390         this.drawCircle(location, this.gMapContext.radius, {});
25391         
25392         var _this = this;
25393         
25394         if (this.gMapContext.settings.enableReverseGeocode) {
25395             this.gMapContext.geodecoder.geocode({
25396                 latLng: this.gMapContext.location
25397             }, function(results, status) {
25398                 
25399                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
25400                     _this.gMapContext.locationName = results[0].formatted_address;
25401                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
25402                     
25403                     _this.fireEvent('positionchanged', this, location);
25404                 }
25405             });
25406             
25407             return;
25408         }
25409         
25410         this.fireEvent('positionchanged', this, location);
25411     },
25412     
25413     resize: function()
25414     {
25415         google.maps.event.trigger(this.gMapContext.map, "resize");
25416         
25417         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
25418         
25419         this.fireEvent('resize', this);
25420     },
25421     
25422     setPositionByLatLng: function(latitude, longitude)
25423     {
25424         this.setPosition(new google.maps.LatLng(latitude, longitude));
25425     },
25426     
25427     getCurrentPosition: function() 
25428     {
25429         return {
25430             latitude: this.gMapContext.location.lat(),
25431             longitude: this.gMapContext.location.lng()
25432         };
25433     },
25434     
25435     getAddressName: function() 
25436     {
25437         return this.gMapContext.locationName;
25438     },
25439     
25440     getAddressComponents: function() 
25441     {
25442         return this.gMapContext.addressComponents;
25443     },
25444     
25445     address_component_from_google_geocode: function(address_components) 
25446     {
25447         var result = {};
25448         
25449         for (var i = 0; i < address_components.length; i++) {
25450             var component = address_components[i];
25451             if (component.types.indexOf("postal_code") >= 0) {
25452                 result.postalCode = component.short_name;
25453             } else if (component.types.indexOf("street_number") >= 0) {
25454                 result.streetNumber = component.short_name;
25455             } else if (component.types.indexOf("route") >= 0) {
25456                 result.streetName = component.short_name;
25457             } else if (component.types.indexOf("neighborhood") >= 0) {
25458                 result.city = component.short_name;
25459             } else if (component.types.indexOf("locality") >= 0) {
25460                 result.city = component.short_name;
25461             } else if (component.types.indexOf("sublocality") >= 0) {
25462                 result.district = component.short_name;
25463             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
25464                 result.stateOrProvince = component.short_name;
25465             } else if (component.types.indexOf("country") >= 0) {
25466                 result.country = component.short_name;
25467             }
25468         }
25469         
25470         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
25471         result.addressLine2 = "";
25472         return result;
25473     },
25474     
25475     setZoomLevel: function(zoom)
25476     {
25477         this.gMapContext.map.setZoom(zoom);
25478     },
25479     
25480     show: function()
25481     {
25482         if(!this.el){
25483             return;
25484         }
25485         
25486         this.el.show();
25487         
25488         this.resize();
25489         
25490         this.fireEvent('show', this);
25491     },
25492     
25493     hide: function()
25494     {
25495         if(!this.el){
25496             return;
25497         }
25498         
25499         this.el.hide();
25500         
25501         this.fireEvent('hide', this);
25502     }
25503     
25504 });
25505
25506 Roo.apply(Roo.bootstrap.LocationPicker, {
25507     
25508     OverlayView : function(map, options)
25509     {
25510         options = options || {};
25511         
25512         this.setMap(map);
25513     }
25514     
25515     
25516 });/*
25517  * - LGPL
25518  *
25519  * Alert
25520  * 
25521  */
25522
25523 /**
25524  * @class Roo.bootstrap.Alert
25525  * @extends Roo.bootstrap.Component
25526  * Bootstrap Alert class
25527  * @cfg {String} title The title of alert
25528  * @cfg {String} html The content of alert
25529  * @cfg {String} weight (  success | info | warning | danger )
25530  * @cfg {String} faicon font-awesomeicon
25531  * 
25532  * @constructor
25533  * Create a new alert
25534  * @param {Object} config The config object
25535  */
25536
25537
25538 Roo.bootstrap.Alert = function(config){
25539     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
25540     
25541 };
25542
25543 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
25544     
25545     title: '',
25546     html: '',
25547     weight: false,
25548     faicon: false,
25549     
25550     getAutoCreate : function()
25551     {
25552         
25553         var cfg = {
25554             tag : 'div',
25555             cls : 'alert',
25556             cn : [
25557                 {
25558                     tag : 'i',
25559                     cls : 'roo-alert-icon'
25560                     
25561                 },
25562                 {
25563                     tag : 'b',
25564                     cls : 'roo-alert-title',
25565                     html : this.title
25566                 },
25567                 {
25568                     tag : 'span',
25569                     cls : 'roo-alert-text',
25570                     html : this.html
25571                 }
25572             ]
25573         };
25574         
25575         if(this.faicon){
25576             cfg.cn[0].cls += ' fa ' + this.faicon;
25577         }
25578         
25579         if(this.weight){
25580             cfg.cls += ' alert-' + this.weight;
25581         }
25582         
25583         return cfg;
25584     },
25585     
25586     initEvents: function() 
25587     {
25588         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25589     },
25590     
25591     setTitle : function(str)
25592     {
25593         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
25594     },
25595     
25596     setText : function(str)
25597     {
25598         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
25599     },
25600     
25601     setWeight : function(weight)
25602     {
25603         if(this.weight){
25604             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
25605         }
25606         
25607         this.weight = weight;
25608         
25609         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
25610     },
25611     
25612     setIcon : function(icon)
25613     {
25614         if(this.faicon){
25615             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
25616         }
25617         
25618         this.faicon = icon;
25619         
25620         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
25621     },
25622     
25623     hide: function() 
25624     {
25625         this.el.hide();   
25626     },
25627     
25628     show: function() 
25629     {  
25630         this.el.show();   
25631     }
25632     
25633 });
25634
25635  
25636 /*
25637 * Licence: LGPL
25638 */
25639
25640 /**
25641  * @class Roo.bootstrap.UploadCropbox
25642  * @extends Roo.bootstrap.Component
25643  * Bootstrap UploadCropbox class
25644  * @cfg {String} emptyText show when image has been loaded
25645  * @cfg {String} rotateNotify show when image too small to rotate
25646  * @cfg {Number} errorTimeout default 3000
25647  * @cfg {Number} minWidth default 300
25648  * @cfg {Number} minHeight default 300
25649  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
25650  * @cfg {Boolean} isDocument (true|false) default false
25651  * @cfg {String} url action url
25652  * @cfg {String} paramName default 'imageUpload'
25653  * @cfg {String} method default POST
25654  * @cfg {Boolean} loadMask (true|false) default true
25655  * @cfg {Boolean} loadingText default 'Loading...'
25656  * 
25657  * @constructor
25658  * Create a new UploadCropbox
25659  * @param {Object} config The config object
25660  */
25661
25662 Roo.bootstrap.UploadCropbox = function(config){
25663     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
25664     
25665     this.addEvents({
25666         /**
25667          * @event beforeselectfile
25668          * Fire before select file
25669          * @param {Roo.bootstrap.UploadCropbox} this
25670          */
25671         "beforeselectfile" : true,
25672         /**
25673          * @event initial
25674          * Fire after initEvent
25675          * @param {Roo.bootstrap.UploadCropbox} this
25676          */
25677         "initial" : true,
25678         /**
25679          * @event crop
25680          * Fire after initEvent
25681          * @param {Roo.bootstrap.UploadCropbox} this
25682          * @param {String} data
25683          */
25684         "crop" : true,
25685         /**
25686          * @event prepare
25687          * Fire when preparing the file data
25688          * @param {Roo.bootstrap.UploadCropbox} this
25689          * @param {Object} file
25690          */
25691         "prepare" : true,
25692         /**
25693          * @event exception
25694          * Fire when get exception
25695          * @param {Roo.bootstrap.UploadCropbox} this
25696          * @param {XMLHttpRequest} xhr
25697          */
25698         "exception" : true,
25699         /**
25700          * @event beforeloadcanvas
25701          * Fire before load the canvas
25702          * @param {Roo.bootstrap.UploadCropbox} this
25703          * @param {String} src
25704          */
25705         "beforeloadcanvas" : true,
25706         /**
25707          * @event trash
25708          * Fire when trash image
25709          * @param {Roo.bootstrap.UploadCropbox} this
25710          */
25711         "trash" : true,
25712         /**
25713          * @event download
25714          * Fire when download the image
25715          * @param {Roo.bootstrap.UploadCropbox} this
25716          */
25717         "download" : true,
25718         /**
25719          * @event footerbuttonclick
25720          * Fire when footerbuttonclick
25721          * @param {Roo.bootstrap.UploadCropbox} this
25722          * @param {String} type
25723          */
25724         "footerbuttonclick" : true,
25725         /**
25726          * @event resize
25727          * Fire when resize
25728          * @param {Roo.bootstrap.UploadCropbox} this
25729          */
25730         "resize" : true,
25731         /**
25732          * @event rotate
25733          * Fire when rotate the image
25734          * @param {Roo.bootstrap.UploadCropbox} this
25735          * @param {String} pos
25736          */
25737         "rotate" : true,
25738         /**
25739          * @event inspect
25740          * Fire when inspect the file
25741          * @param {Roo.bootstrap.UploadCropbox} this
25742          * @param {Object} file
25743          */
25744         "inspect" : true,
25745         /**
25746          * @event upload
25747          * Fire when xhr upload the file
25748          * @param {Roo.bootstrap.UploadCropbox} this
25749          * @param {Object} data
25750          */
25751         "upload" : true,
25752         /**
25753          * @event arrange
25754          * Fire when arrange the file data
25755          * @param {Roo.bootstrap.UploadCropbox} this
25756          * @param {Object} formData
25757          */
25758         "arrange" : true
25759     });
25760     
25761     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25762 };
25763
25764 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
25765     
25766     emptyText : 'Click to upload image',
25767     rotateNotify : 'Image is too small to rotate',
25768     errorTimeout : 3000,
25769     scale : 0,
25770     baseScale : 1,
25771     rotate : 0,
25772     dragable : false,
25773     pinching : false,
25774     mouseX : 0,
25775     mouseY : 0,
25776     cropData : false,
25777     minWidth : 300,
25778     minHeight : 300,
25779     file : false,
25780     exif : {},
25781     baseRotate : 1,
25782     cropType : 'image/jpeg',
25783     buttons : false,
25784     canvasLoaded : false,
25785     isDocument : false,
25786     method : 'POST',
25787     paramName : 'imageUpload',
25788     loadMask : true,
25789     loadingText : 'Loading...',
25790     maskEl : false,
25791     
25792     getAutoCreate : function()
25793     {
25794         var cfg = {
25795             tag : 'div',
25796             cls : 'roo-upload-cropbox',
25797             cn : [
25798                 {
25799                     tag : 'input',
25800                     cls : 'roo-upload-cropbox-selector',
25801                     type : 'file'
25802                 },
25803                 {
25804                     tag : 'div',
25805                     cls : 'roo-upload-cropbox-body',
25806                     style : 'cursor:pointer',
25807                     cn : [
25808                         {
25809                             tag : 'div',
25810                             cls : 'roo-upload-cropbox-preview'
25811                         },
25812                         {
25813                             tag : 'div',
25814                             cls : 'roo-upload-cropbox-thumb'
25815                         },
25816                         {
25817                             tag : 'div',
25818                             cls : 'roo-upload-cropbox-empty-notify',
25819                             html : this.emptyText
25820                         },
25821                         {
25822                             tag : 'div',
25823                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25824                             html : this.rotateNotify
25825                         }
25826                     ]
25827                 },
25828                 {
25829                     tag : 'div',
25830                     cls : 'roo-upload-cropbox-footer',
25831                     cn : {
25832                         tag : 'div',
25833                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25834                         cn : []
25835                     }
25836                 }
25837             ]
25838         };
25839         
25840         return cfg;
25841     },
25842     
25843     onRender : function(ct, position)
25844     {
25845         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25846         
25847         if (this.buttons.length) {
25848             
25849             Roo.each(this.buttons, function(bb) {
25850                 
25851                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25852                 
25853                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25854                 
25855             }, this);
25856         }
25857         
25858         if(this.loadMask){
25859             this.maskEl = this.el;
25860         }
25861     },
25862     
25863     initEvents : function()
25864     {
25865         this.urlAPI = (window.createObjectURL && window) || 
25866                                 (window.URL && URL.revokeObjectURL && URL) || 
25867                                 (window.webkitURL && webkitURL);
25868                         
25869         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25870         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25871         
25872         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25873         this.selectorEl.hide();
25874         
25875         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25876         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25877         
25878         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25879         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25880         this.thumbEl.hide();
25881         
25882         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25883         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25884         
25885         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25886         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25887         this.errorEl.hide();
25888         
25889         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25890         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25891         this.footerEl.hide();
25892         
25893         this.setThumbBoxSize();
25894         
25895         this.bind();
25896         
25897         this.resize();
25898         
25899         this.fireEvent('initial', this);
25900     },
25901
25902     bind : function()
25903     {
25904         var _this = this;
25905         
25906         window.addEventListener("resize", function() { _this.resize(); } );
25907         
25908         this.bodyEl.on('click', this.beforeSelectFile, this);
25909         
25910         if(Roo.isTouch){
25911             this.bodyEl.on('touchstart', this.onTouchStart, this);
25912             this.bodyEl.on('touchmove', this.onTouchMove, this);
25913             this.bodyEl.on('touchend', this.onTouchEnd, this);
25914         }
25915         
25916         if(!Roo.isTouch){
25917             this.bodyEl.on('mousedown', this.onMouseDown, this);
25918             this.bodyEl.on('mousemove', this.onMouseMove, this);
25919             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25920             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25921             Roo.get(document).on('mouseup', this.onMouseUp, this);
25922         }
25923         
25924         this.selectorEl.on('change', this.onFileSelected, this);
25925     },
25926     
25927     reset : function()
25928     {    
25929         this.scale = 0;
25930         this.baseScale = 1;
25931         this.rotate = 0;
25932         this.baseRotate = 1;
25933         this.dragable = false;
25934         this.pinching = false;
25935         this.mouseX = 0;
25936         this.mouseY = 0;
25937         this.cropData = false;
25938         this.notifyEl.dom.innerHTML = this.emptyText;
25939         
25940         this.selectorEl.dom.value = '';
25941         
25942     },
25943     
25944     resize : function()
25945     {
25946         if(this.fireEvent('resize', this) != false){
25947             this.setThumbBoxPosition();
25948             this.setCanvasPosition();
25949         }
25950     },
25951     
25952     onFooterButtonClick : function(e, el, o, type)
25953     {
25954         switch (type) {
25955             case 'rotate-left' :
25956                 this.onRotateLeft(e);
25957                 break;
25958             case 'rotate-right' :
25959                 this.onRotateRight(e);
25960                 break;
25961             case 'picture' :
25962                 this.beforeSelectFile(e);
25963                 break;
25964             case 'trash' :
25965                 this.trash(e);
25966                 break;
25967             case 'crop' :
25968                 this.crop(e);
25969                 break;
25970             case 'download' :
25971                 this.download(e);
25972                 break;
25973             default :
25974                 break;
25975         }
25976         
25977         this.fireEvent('footerbuttonclick', this, type);
25978     },
25979     
25980     beforeSelectFile : function(e)
25981     {
25982         e.preventDefault();
25983         
25984         if(this.fireEvent('beforeselectfile', this) != false){
25985             this.selectorEl.dom.click();
25986         }
25987     },
25988     
25989     onFileSelected : function(e)
25990     {
25991         e.preventDefault();
25992         
25993         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25994             return;
25995         }
25996         
25997         var file = this.selectorEl.dom.files[0];
25998         
25999         if(this.fireEvent('inspect', this, file) != false){
26000             this.prepare(file);
26001         }
26002         
26003     },
26004     
26005     trash : function(e)
26006     {
26007         this.fireEvent('trash', this);
26008     },
26009     
26010     download : function(e)
26011     {
26012         this.fireEvent('download', this);
26013     },
26014     
26015     loadCanvas : function(src)
26016     {   
26017         if(this.fireEvent('beforeloadcanvas', this, src) != false){
26018             
26019             this.reset();
26020             
26021             this.imageEl = document.createElement('img');
26022             
26023             var _this = this;
26024             
26025             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26026             
26027             this.imageEl.src = src;
26028         }
26029     },
26030     
26031     onLoadCanvas : function()
26032     {   
26033         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26034         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26035         
26036         this.bodyEl.un('click', this.beforeSelectFile, this);
26037         
26038         this.notifyEl.hide();
26039         this.thumbEl.show();
26040         this.footerEl.show();
26041         
26042         this.baseRotateLevel();
26043         
26044         if(this.isDocument){
26045             this.setThumbBoxSize();
26046         }
26047         
26048         this.setThumbBoxPosition();
26049         
26050         this.baseScaleLevel();
26051         
26052         this.draw();
26053         
26054         this.resize();
26055         
26056         this.canvasLoaded = true;
26057         
26058         if(this.loadMask){
26059             this.maskEl.unmask();
26060         }
26061         
26062     },
26063     
26064     setCanvasPosition : function()
26065     {   
26066         if(!this.canvasEl){
26067             return;
26068         }
26069         
26070         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26071         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26072         
26073         this.previewEl.setLeft(pw);
26074         this.previewEl.setTop(ph);
26075         
26076     },
26077     
26078     onMouseDown : function(e)
26079     {   
26080         e.stopEvent();
26081         
26082         this.dragable = true;
26083         this.pinching = false;
26084         
26085         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26086             this.dragable = false;
26087             return;
26088         }
26089         
26090         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26091         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26092         
26093     },
26094     
26095     onMouseMove : function(e)
26096     {   
26097         e.stopEvent();
26098         
26099         if(!this.canvasLoaded){
26100             return;
26101         }
26102         
26103         if (!this.dragable){
26104             return;
26105         }
26106         
26107         var minX = Math.ceil(this.thumbEl.getLeft(true));
26108         var minY = Math.ceil(this.thumbEl.getTop(true));
26109         
26110         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
26111         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
26112         
26113         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26114         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26115         
26116         x = x - this.mouseX;
26117         y = y - this.mouseY;
26118         
26119         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
26120         var bgY = Math.ceil(y + this.previewEl.getTop(true));
26121         
26122         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
26123         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
26124         
26125         this.previewEl.setLeft(bgX);
26126         this.previewEl.setTop(bgY);
26127         
26128         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26129         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26130     },
26131     
26132     onMouseUp : function(e)
26133     {   
26134         e.stopEvent();
26135         
26136         this.dragable = false;
26137     },
26138     
26139     onMouseWheel : function(e)
26140     {   
26141         e.stopEvent();
26142         
26143         this.startScale = this.scale;
26144         
26145         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
26146         
26147         if(!this.zoomable()){
26148             this.scale = this.startScale;
26149             return;
26150         }
26151         
26152         this.draw();
26153         
26154         return;
26155     },
26156     
26157     zoomable : function()
26158     {
26159         var minScale = this.thumbEl.getWidth() / this.minWidth;
26160         
26161         if(this.minWidth < this.minHeight){
26162             minScale = this.thumbEl.getHeight() / this.minHeight;
26163         }
26164         
26165         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
26166         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
26167         
26168         if(
26169                 this.isDocument &&
26170                 (this.rotate == 0 || this.rotate == 180) && 
26171                 (
26172                     width > this.imageEl.OriginWidth || 
26173                     height > this.imageEl.OriginHeight ||
26174                     (width < this.minWidth && height < this.minHeight)
26175                 )
26176         ){
26177             return false;
26178         }
26179         
26180         if(
26181                 this.isDocument &&
26182                 (this.rotate == 90 || this.rotate == 270) && 
26183                 (
26184                     width > this.imageEl.OriginWidth || 
26185                     height > this.imageEl.OriginHeight ||
26186                     (width < this.minHeight && height < this.minWidth)
26187                 )
26188         ){
26189             return false;
26190         }
26191         
26192         if(
26193                 !this.isDocument &&
26194                 (this.rotate == 0 || this.rotate == 180) && 
26195                 (
26196                     width < this.minWidth || 
26197                     width > this.imageEl.OriginWidth || 
26198                     height < this.minHeight || 
26199                     height > this.imageEl.OriginHeight
26200                 )
26201         ){
26202             return false;
26203         }
26204         
26205         if(
26206                 !this.isDocument &&
26207                 (this.rotate == 90 || this.rotate == 270) && 
26208                 (
26209                     width < this.minHeight || 
26210                     width > this.imageEl.OriginWidth || 
26211                     height < this.minWidth || 
26212                     height > this.imageEl.OriginHeight
26213                 )
26214         ){
26215             return false;
26216         }
26217         
26218         return true;
26219         
26220     },
26221     
26222     onRotateLeft : function(e)
26223     {   
26224         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26225             
26226             var minScale = this.thumbEl.getWidth() / this.minWidth;
26227             
26228             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26229             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26230             
26231             this.startScale = this.scale;
26232             
26233             while (this.getScaleLevel() < minScale){
26234             
26235                 this.scale = this.scale + 1;
26236                 
26237                 if(!this.zoomable()){
26238                     break;
26239                 }
26240                 
26241                 if(
26242                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26243                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26244                 ){
26245                     continue;
26246                 }
26247                 
26248                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26249
26250                 this.draw();
26251                 
26252                 return;
26253             }
26254             
26255             this.scale = this.startScale;
26256             
26257             this.onRotateFail();
26258             
26259             return false;
26260         }
26261         
26262         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26263
26264         if(this.isDocument){
26265             this.setThumbBoxSize();
26266             this.setThumbBoxPosition();
26267             this.setCanvasPosition();
26268         }
26269         
26270         this.draw();
26271         
26272         this.fireEvent('rotate', this, 'left');
26273         
26274     },
26275     
26276     onRotateRight : function(e)
26277     {
26278         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26279             
26280             var minScale = this.thumbEl.getWidth() / this.minWidth;
26281         
26282             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26283             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26284             
26285             this.startScale = this.scale;
26286             
26287             while (this.getScaleLevel() < minScale){
26288             
26289                 this.scale = this.scale + 1;
26290                 
26291                 if(!this.zoomable()){
26292                     break;
26293                 }
26294                 
26295                 if(
26296                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26297                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26298                 ){
26299                     continue;
26300                 }
26301                 
26302                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26303
26304                 this.draw();
26305                 
26306                 return;
26307             }
26308             
26309             this.scale = this.startScale;
26310             
26311             this.onRotateFail();
26312             
26313             return false;
26314         }
26315         
26316         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26317
26318         if(this.isDocument){
26319             this.setThumbBoxSize();
26320             this.setThumbBoxPosition();
26321             this.setCanvasPosition();
26322         }
26323         
26324         this.draw();
26325         
26326         this.fireEvent('rotate', this, 'right');
26327     },
26328     
26329     onRotateFail : function()
26330     {
26331         this.errorEl.show(true);
26332         
26333         var _this = this;
26334         
26335         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
26336     },
26337     
26338     draw : function()
26339     {
26340         this.previewEl.dom.innerHTML = '';
26341         
26342         var canvasEl = document.createElement("canvas");
26343         
26344         var contextEl = canvasEl.getContext("2d");
26345         
26346         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26347         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26348         var center = this.imageEl.OriginWidth / 2;
26349         
26350         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
26351             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26352             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26353             center = this.imageEl.OriginHeight / 2;
26354         }
26355         
26356         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
26357         
26358         contextEl.translate(center, center);
26359         contextEl.rotate(this.rotate * Math.PI / 180);
26360
26361         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26362         
26363         this.canvasEl = document.createElement("canvas");
26364         
26365         this.contextEl = this.canvasEl.getContext("2d");
26366         
26367         switch (this.rotate) {
26368             case 0 :
26369                 
26370                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26371                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26372                 
26373                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26374                 
26375                 break;
26376             case 90 : 
26377                 
26378                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26379                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26380                 
26381                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26382                     this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26383                     break;
26384                 }
26385                 
26386                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26387                 
26388                 break;
26389             case 180 :
26390                 
26391                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26392                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26393                 
26394                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26395                     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);
26396                     break;
26397                 }
26398                 
26399                 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);
26400                 
26401                 break;
26402             case 270 :
26403                 
26404                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26405                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26406         
26407                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26408                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26409                     break;
26410                 }
26411                 
26412                 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);
26413                 
26414                 break;
26415             default : 
26416                 break;
26417         }
26418         
26419         this.previewEl.appendChild(this.canvasEl);
26420         
26421         this.setCanvasPosition();
26422     },
26423     
26424     crop : function()
26425     {
26426         if(!this.canvasLoaded){
26427             return;
26428         }
26429         
26430         var imageCanvas = document.createElement("canvas");
26431         
26432         var imageContext = imageCanvas.getContext("2d");
26433         
26434         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26435         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26436         
26437         var center = imageCanvas.width / 2;
26438         
26439         imageContext.translate(center, center);
26440         
26441         imageContext.rotate(this.rotate * Math.PI / 180);
26442         
26443         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26444         
26445         var canvas = document.createElement("canvas");
26446         
26447         var context = canvas.getContext("2d");
26448                 
26449         canvas.width = this.minWidth;
26450         canvas.height = this.minHeight;
26451
26452         switch (this.rotate) {
26453             case 0 :
26454                 
26455                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26456                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26457                 
26458                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26459                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26460                 
26461                 var targetWidth = this.minWidth - 2 * x;
26462                 var targetHeight = this.minHeight - 2 * y;
26463                 
26464                 var scale = 1;
26465                 
26466                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26467                     scale = targetWidth / width;
26468                 }
26469                 
26470                 if(x > 0 && y == 0){
26471                     scale = targetHeight / height;
26472                 }
26473                 
26474                 if(x > 0 && y > 0){
26475                     scale = targetWidth / width;
26476                     
26477                     if(width < height){
26478                         scale = targetHeight / height;
26479                     }
26480                 }
26481                 
26482                 context.scale(scale, scale);
26483                 
26484                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26485                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26486
26487                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26488                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26489
26490                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26491                 
26492                 break;
26493             case 90 : 
26494                 
26495                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26496                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26497                 
26498                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26499                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26500                 
26501                 var targetWidth = this.minWidth - 2 * x;
26502                 var targetHeight = this.minHeight - 2 * y;
26503                 
26504                 var scale = 1;
26505                 
26506                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26507                     scale = targetWidth / width;
26508                 }
26509                 
26510                 if(x > 0 && y == 0){
26511                     scale = targetHeight / height;
26512                 }
26513                 
26514                 if(x > 0 && y > 0){
26515                     scale = targetWidth / width;
26516                     
26517                     if(width < height){
26518                         scale = targetHeight / height;
26519                     }
26520                 }
26521                 
26522                 context.scale(scale, scale);
26523                 
26524                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26525                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26526
26527                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26528                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26529                 
26530                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26531                 
26532                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26533                 
26534                 break;
26535             case 180 :
26536                 
26537                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26538                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26539                 
26540                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26541                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26542                 
26543                 var targetWidth = this.minWidth - 2 * x;
26544                 var targetHeight = this.minHeight - 2 * y;
26545                 
26546                 var scale = 1;
26547                 
26548                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26549                     scale = targetWidth / width;
26550                 }
26551                 
26552                 if(x > 0 && y == 0){
26553                     scale = targetHeight / height;
26554                 }
26555                 
26556                 if(x > 0 && y > 0){
26557                     scale = targetWidth / width;
26558                     
26559                     if(width < height){
26560                         scale = targetHeight / height;
26561                     }
26562                 }
26563                 
26564                 context.scale(scale, scale);
26565                 
26566                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26567                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26568
26569                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26570                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26571
26572                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26573                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26574                 
26575                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26576                 
26577                 break;
26578             case 270 :
26579                 
26580                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26581                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26582                 
26583                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26584                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26585                 
26586                 var targetWidth = this.minWidth - 2 * x;
26587                 var targetHeight = this.minHeight - 2 * y;
26588                 
26589                 var scale = 1;
26590                 
26591                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26592                     scale = targetWidth / width;
26593                 }
26594                 
26595                 if(x > 0 && y == 0){
26596                     scale = targetHeight / height;
26597                 }
26598                 
26599                 if(x > 0 && y > 0){
26600                     scale = targetWidth / width;
26601                     
26602                     if(width < height){
26603                         scale = targetHeight / height;
26604                     }
26605                 }
26606                 
26607                 context.scale(scale, scale);
26608                 
26609                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26610                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26611
26612                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26613                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26614                 
26615                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26616                 
26617                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26618                 
26619                 break;
26620             default : 
26621                 break;
26622         }
26623         
26624         this.cropData = canvas.toDataURL(this.cropType);
26625         
26626         if(this.fireEvent('crop', this, this.cropData) !== false){
26627             this.process(this.file, this.cropData);
26628         }
26629         
26630         return;
26631         
26632     },
26633     
26634     setThumbBoxSize : function()
26635     {
26636         var width, height;
26637         
26638         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
26639             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
26640             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
26641             
26642             this.minWidth = width;
26643             this.minHeight = height;
26644             
26645             if(this.rotate == 90 || this.rotate == 270){
26646                 this.minWidth = height;
26647                 this.minHeight = width;
26648             }
26649         }
26650         
26651         height = 300;
26652         width = Math.ceil(this.minWidth * height / this.minHeight);
26653         
26654         if(this.minWidth > this.minHeight){
26655             width = 300;
26656             height = Math.ceil(this.minHeight * width / this.minWidth);
26657         }
26658         
26659         this.thumbEl.setStyle({
26660             width : width + 'px',
26661             height : height + 'px'
26662         });
26663
26664         return;
26665             
26666     },
26667     
26668     setThumbBoxPosition : function()
26669     {
26670         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
26671         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
26672         
26673         this.thumbEl.setLeft(x);
26674         this.thumbEl.setTop(y);
26675         
26676     },
26677     
26678     baseRotateLevel : function()
26679     {
26680         this.baseRotate = 1;
26681         
26682         if(
26683                 typeof(this.exif) != 'undefined' &&
26684                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
26685                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
26686         ){
26687             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
26688         }
26689         
26690         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
26691         
26692     },
26693     
26694     baseScaleLevel : function()
26695     {
26696         var width, height;
26697         
26698         if(this.isDocument){
26699             
26700             if(this.baseRotate == 6 || this.baseRotate == 8){
26701             
26702                 height = this.thumbEl.getHeight();
26703                 this.baseScale = height / this.imageEl.OriginWidth;
26704
26705                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
26706                     width = this.thumbEl.getWidth();
26707                     this.baseScale = width / this.imageEl.OriginHeight;
26708                 }
26709
26710                 return;
26711             }
26712
26713             height = this.thumbEl.getHeight();
26714             this.baseScale = height / this.imageEl.OriginHeight;
26715
26716             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
26717                 width = this.thumbEl.getWidth();
26718                 this.baseScale = width / this.imageEl.OriginWidth;
26719             }
26720
26721             return;
26722         }
26723         
26724         if(this.baseRotate == 6 || this.baseRotate == 8){
26725             
26726             width = this.thumbEl.getHeight();
26727             this.baseScale = width / this.imageEl.OriginHeight;
26728             
26729             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
26730                 height = this.thumbEl.getWidth();
26731                 this.baseScale = height / this.imageEl.OriginHeight;
26732             }
26733             
26734             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26735                 height = this.thumbEl.getWidth();
26736                 this.baseScale = height / this.imageEl.OriginHeight;
26737                 
26738                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
26739                     width = this.thumbEl.getHeight();
26740                     this.baseScale = width / this.imageEl.OriginWidth;
26741                 }
26742             }
26743             
26744             return;
26745         }
26746         
26747         width = this.thumbEl.getWidth();
26748         this.baseScale = width / this.imageEl.OriginWidth;
26749         
26750         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
26751             height = this.thumbEl.getHeight();
26752             this.baseScale = height / this.imageEl.OriginHeight;
26753         }
26754         
26755         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26756             
26757             height = this.thumbEl.getHeight();
26758             this.baseScale = height / this.imageEl.OriginHeight;
26759             
26760             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26761                 width = this.thumbEl.getWidth();
26762                 this.baseScale = width / this.imageEl.OriginWidth;
26763             }
26764             
26765         }
26766         
26767         return;
26768     },
26769     
26770     getScaleLevel : function()
26771     {
26772         return this.baseScale * Math.pow(1.1, this.scale);
26773     },
26774     
26775     onTouchStart : function(e)
26776     {
26777         if(!this.canvasLoaded){
26778             this.beforeSelectFile(e);
26779             return;
26780         }
26781         
26782         var touches = e.browserEvent.touches;
26783         
26784         if(!touches){
26785             return;
26786         }
26787         
26788         if(touches.length == 1){
26789             this.onMouseDown(e);
26790             return;
26791         }
26792         
26793         if(touches.length != 2){
26794             return;
26795         }
26796         
26797         var coords = [];
26798         
26799         for(var i = 0, finger; finger = touches[i]; i++){
26800             coords.push(finger.pageX, finger.pageY);
26801         }
26802         
26803         var x = Math.pow(coords[0] - coords[2], 2);
26804         var y = Math.pow(coords[1] - coords[3], 2);
26805         
26806         this.startDistance = Math.sqrt(x + y);
26807         
26808         this.startScale = this.scale;
26809         
26810         this.pinching = true;
26811         this.dragable = false;
26812         
26813     },
26814     
26815     onTouchMove : function(e)
26816     {
26817         if(!this.pinching && !this.dragable){
26818             return;
26819         }
26820         
26821         var touches = e.browserEvent.touches;
26822         
26823         if(!touches){
26824             return;
26825         }
26826         
26827         if(this.dragable){
26828             this.onMouseMove(e);
26829             return;
26830         }
26831         
26832         var coords = [];
26833         
26834         for(var i = 0, finger; finger = touches[i]; i++){
26835             coords.push(finger.pageX, finger.pageY);
26836         }
26837         
26838         var x = Math.pow(coords[0] - coords[2], 2);
26839         var y = Math.pow(coords[1] - coords[3], 2);
26840         
26841         this.endDistance = Math.sqrt(x + y);
26842         
26843         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26844         
26845         if(!this.zoomable()){
26846             this.scale = this.startScale;
26847             return;
26848         }
26849         
26850         this.draw();
26851         
26852     },
26853     
26854     onTouchEnd : function(e)
26855     {
26856         this.pinching = false;
26857         this.dragable = false;
26858         
26859     },
26860     
26861     process : function(file, crop)
26862     {
26863         if(this.loadMask){
26864             this.maskEl.mask(this.loadingText);
26865         }
26866         
26867         this.xhr = new XMLHttpRequest();
26868         
26869         file.xhr = this.xhr;
26870
26871         this.xhr.open(this.method, this.url, true);
26872         
26873         var headers = {
26874             "Accept": "application/json",
26875             "Cache-Control": "no-cache",
26876             "X-Requested-With": "XMLHttpRequest"
26877         };
26878         
26879         for (var headerName in headers) {
26880             var headerValue = headers[headerName];
26881             if (headerValue) {
26882                 this.xhr.setRequestHeader(headerName, headerValue);
26883             }
26884         }
26885         
26886         var _this = this;
26887         
26888         this.xhr.onload = function()
26889         {
26890             _this.xhrOnLoad(_this.xhr);
26891         }
26892         
26893         this.xhr.onerror = function()
26894         {
26895             _this.xhrOnError(_this.xhr);
26896         }
26897         
26898         var formData = new FormData();
26899
26900         formData.append('returnHTML', 'NO');
26901         
26902         if(crop){
26903             formData.append('crop', crop);
26904         }
26905         
26906         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26907             formData.append(this.paramName, file, file.name);
26908         }
26909         
26910         if(typeof(file.filename) != 'undefined'){
26911             formData.append('filename', file.filename);
26912         }
26913         
26914         if(typeof(file.mimetype) != 'undefined'){
26915             formData.append('mimetype', file.mimetype);
26916         }
26917         
26918         if(this.fireEvent('arrange', this, formData) != false){
26919             this.xhr.send(formData);
26920         };
26921     },
26922     
26923     xhrOnLoad : function(xhr)
26924     {
26925         if(this.loadMask){
26926             this.maskEl.unmask();
26927         }
26928         
26929         if (xhr.readyState !== 4) {
26930             this.fireEvent('exception', this, xhr);
26931             return;
26932         }
26933
26934         var response = Roo.decode(xhr.responseText);
26935         
26936         if(!response.success){
26937             this.fireEvent('exception', this, xhr);
26938             return;
26939         }
26940         
26941         var response = Roo.decode(xhr.responseText);
26942         
26943         this.fireEvent('upload', this, response);
26944         
26945     },
26946     
26947     xhrOnError : function()
26948     {
26949         if(this.loadMask){
26950             this.maskEl.unmask();
26951         }
26952         
26953         Roo.log('xhr on error');
26954         
26955         var response = Roo.decode(xhr.responseText);
26956           
26957         Roo.log(response);
26958         
26959     },
26960     
26961     prepare : function(file)
26962     {   
26963         if(this.loadMask){
26964             this.maskEl.mask(this.loadingText);
26965         }
26966         
26967         this.file = false;
26968         this.exif = {};
26969         
26970         if(typeof(file) === 'string'){
26971             this.loadCanvas(file);
26972             return;
26973         }
26974         
26975         if(!file || !this.urlAPI){
26976             return;
26977         }
26978         
26979         this.file = file;
26980         this.cropType = file.type;
26981         
26982         var _this = this;
26983         
26984         if(this.fireEvent('prepare', this, this.file) != false){
26985             
26986             var reader = new FileReader();
26987             
26988             reader.onload = function (e) {
26989                 if (e.target.error) {
26990                     Roo.log(e.target.error);
26991                     return;
26992                 }
26993                 
26994                 var buffer = e.target.result,
26995                     dataView = new DataView(buffer),
26996                     offset = 2,
26997                     maxOffset = dataView.byteLength - 4,
26998                     markerBytes,
26999                     markerLength;
27000                 
27001                 if (dataView.getUint16(0) === 0xffd8) {
27002                     while (offset < maxOffset) {
27003                         markerBytes = dataView.getUint16(offset);
27004                         
27005                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27006                             markerLength = dataView.getUint16(offset + 2) + 2;
27007                             if (offset + markerLength > dataView.byteLength) {
27008                                 Roo.log('Invalid meta data: Invalid segment size.');
27009                                 break;
27010                             }
27011                             
27012                             if(markerBytes == 0xffe1){
27013                                 _this.parseExifData(
27014                                     dataView,
27015                                     offset,
27016                                     markerLength
27017                                 );
27018                             }
27019                             
27020                             offset += markerLength;
27021                             
27022                             continue;
27023                         }
27024                         
27025                         break;
27026                     }
27027                     
27028                 }
27029                 
27030                 var url = _this.urlAPI.createObjectURL(_this.file);
27031                 
27032                 _this.loadCanvas(url);
27033                 
27034                 return;
27035             }
27036             
27037             reader.readAsArrayBuffer(this.file);
27038             
27039         }
27040         
27041     },
27042     
27043     parseExifData : function(dataView, offset, length)
27044     {
27045         var tiffOffset = offset + 10,
27046             littleEndian,
27047             dirOffset;
27048     
27049         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27050             // No Exif data, might be XMP data instead
27051             return;
27052         }
27053         
27054         // Check for the ASCII code for "Exif" (0x45786966):
27055         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27056             // No Exif data, might be XMP data instead
27057             return;
27058         }
27059         if (tiffOffset + 8 > dataView.byteLength) {
27060             Roo.log('Invalid Exif data: Invalid segment size.');
27061             return;
27062         }
27063         // Check for the two null bytes:
27064         if (dataView.getUint16(offset + 8) !== 0x0000) {
27065             Roo.log('Invalid Exif data: Missing byte alignment offset.');
27066             return;
27067         }
27068         // Check the byte alignment:
27069         switch (dataView.getUint16(tiffOffset)) {
27070         case 0x4949:
27071             littleEndian = true;
27072             break;
27073         case 0x4D4D:
27074             littleEndian = false;
27075             break;
27076         default:
27077             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27078             return;
27079         }
27080         // Check for the TIFF tag marker (0x002A):
27081         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27082             Roo.log('Invalid Exif data: Missing TIFF marker.');
27083             return;
27084         }
27085         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27086         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27087         
27088         this.parseExifTags(
27089             dataView,
27090             tiffOffset,
27091             tiffOffset + dirOffset,
27092             littleEndian
27093         );
27094     },
27095     
27096     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27097     {
27098         var tagsNumber,
27099             dirEndOffset,
27100             i;
27101         if (dirOffset + 6 > dataView.byteLength) {
27102             Roo.log('Invalid Exif data: Invalid directory offset.');
27103             return;
27104         }
27105         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
27106         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
27107         if (dirEndOffset + 4 > dataView.byteLength) {
27108             Roo.log('Invalid Exif data: Invalid directory size.');
27109             return;
27110         }
27111         for (i = 0; i < tagsNumber; i += 1) {
27112             this.parseExifTag(
27113                 dataView,
27114                 tiffOffset,
27115                 dirOffset + 2 + 12 * i, // tag offset
27116                 littleEndian
27117             );
27118         }
27119         // Return the offset to the next directory:
27120         return dataView.getUint32(dirEndOffset, littleEndian);
27121     },
27122     
27123     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
27124     {
27125         var tag = dataView.getUint16(offset, littleEndian);
27126         
27127         this.exif[tag] = this.getExifValue(
27128             dataView,
27129             tiffOffset,
27130             offset,
27131             dataView.getUint16(offset + 2, littleEndian), // tag type
27132             dataView.getUint32(offset + 4, littleEndian), // tag length
27133             littleEndian
27134         );
27135     },
27136     
27137     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
27138     {
27139         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
27140             tagSize,
27141             dataOffset,
27142             values,
27143             i,
27144             str,
27145             c;
27146     
27147         if (!tagType) {
27148             Roo.log('Invalid Exif data: Invalid tag type.');
27149             return;
27150         }
27151         
27152         tagSize = tagType.size * length;
27153         // Determine if the value is contained in the dataOffset bytes,
27154         // or if the value at the dataOffset is a pointer to the actual data:
27155         dataOffset = tagSize > 4 ?
27156                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
27157         if (dataOffset + tagSize > dataView.byteLength) {
27158             Roo.log('Invalid Exif data: Invalid data offset.');
27159             return;
27160         }
27161         if (length === 1) {
27162             return tagType.getValue(dataView, dataOffset, littleEndian);
27163         }
27164         values = [];
27165         for (i = 0; i < length; i += 1) {
27166             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
27167         }
27168         
27169         if (tagType.ascii) {
27170             str = '';
27171             // Concatenate the chars:
27172             for (i = 0; i < values.length; i += 1) {
27173                 c = values[i];
27174                 // Ignore the terminating NULL byte(s):
27175                 if (c === '\u0000') {
27176                     break;
27177                 }
27178                 str += c;
27179             }
27180             return str;
27181         }
27182         return values;
27183     }
27184     
27185 });
27186
27187 Roo.apply(Roo.bootstrap.UploadCropbox, {
27188     tags : {
27189         'Orientation': 0x0112
27190     },
27191     
27192     Orientation: {
27193             1: 0, //'top-left',
27194 //            2: 'top-right',
27195             3: 180, //'bottom-right',
27196 //            4: 'bottom-left',
27197 //            5: 'left-top',
27198             6: 90, //'right-top',
27199 //            7: 'right-bottom',
27200             8: 270 //'left-bottom'
27201     },
27202     
27203     exifTagTypes : {
27204         // byte, 8-bit unsigned int:
27205         1: {
27206             getValue: function (dataView, dataOffset) {
27207                 return dataView.getUint8(dataOffset);
27208             },
27209             size: 1
27210         },
27211         // ascii, 8-bit byte:
27212         2: {
27213             getValue: function (dataView, dataOffset) {
27214                 return String.fromCharCode(dataView.getUint8(dataOffset));
27215             },
27216             size: 1,
27217             ascii: true
27218         },
27219         // short, 16 bit int:
27220         3: {
27221             getValue: function (dataView, dataOffset, littleEndian) {
27222                 return dataView.getUint16(dataOffset, littleEndian);
27223             },
27224             size: 2
27225         },
27226         // long, 32 bit int:
27227         4: {
27228             getValue: function (dataView, dataOffset, littleEndian) {
27229                 return dataView.getUint32(dataOffset, littleEndian);
27230             },
27231             size: 4
27232         },
27233         // rational = two long values, first is numerator, second is denominator:
27234         5: {
27235             getValue: function (dataView, dataOffset, littleEndian) {
27236                 return dataView.getUint32(dataOffset, littleEndian) /
27237                     dataView.getUint32(dataOffset + 4, littleEndian);
27238             },
27239             size: 8
27240         },
27241         // slong, 32 bit signed int:
27242         9: {
27243             getValue: function (dataView, dataOffset, littleEndian) {
27244                 return dataView.getInt32(dataOffset, littleEndian);
27245             },
27246             size: 4
27247         },
27248         // srational, two slongs, first is numerator, second is denominator:
27249         10: {
27250             getValue: function (dataView, dataOffset, littleEndian) {
27251                 return dataView.getInt32(dataOffset, littleEndian) /
27252                     dataView.getInt32(dataOffset + 4, littleEndian);
27253             },
27254             size: 8
27255         }
27256     },
27257     
27258     footer : {
27259         STANDARD : [
27260             {
27261                 tag : 'div',
27262                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27263                 action : 'rotate-left',
27264                 cn : [
27265                     {
27266                         tag : 'button',
27267                         cls : 'btn btn-default',
27268                         html : '<i class="fa fa-undo"></i>'
27269                     }
27270                 ]
27271             },
27272             {
27273                 tag : 'div',
27274                 cls : 'btn-group roo-upload-cropbox-picture',
27275                 action : 'picture',
27276                 cn : [
27277                     {
27278                         tag : 'button',
27279                         cls : 'btn btn-default',
27280                         html : '<i class="fa fa-picture-o"></i>'
27281                     }
27282                 ]
27283             },
27284             {
27285                 tag : 'div',
27286                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27287                 action : 'rotate-right',
27288                 cn : [
27289                     {
27290                         tag : 'button',
27291                         cls : 'btn btn-default',
27292                         html : '<i class="fa fa-repeat"></i>'
27293                     }
27294                 ]
27295             }
27296         ],
27297         DOCUMENT : [
27298             {
27299                 tag : 'div',
27300                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27301                 action : 'rotate-left',
27302                 cn : [
27303                     {
27304                         tag : 'button',
27305                         cls : 'btn btn-default',
27306                         html : '<i class="fa fa-undo"></i>'
27307                     }
27308                 ]
27309             },
27310             {
27311                 tag : 'div',
27312                 cls : 'btn-group roo-upload-cropbox-download',
27313                 action : 'download',
27314                 cn : [
27315                     {
27316                         tag : 'button',
27317                         cls : 'btn btn-default',
27318                         html : '<i class="fa fa-download"></i>'
27319                     }
27320                 ]
27321             },
27322             {
27323                 tag : 'div',
27324                 cls : 'btn-group roo-upload-cropbox-crop',
27325                 action : 'crop',
27326                 cn : [
27327                     {
27328                         tag : 'button',
27329                         cls : 'btn btn-default',
27330                         html : '<i class="fa fa-crop"></i>'
27331                     }
27332                 ]
27333             },
27334             {
27335                 tag : 'div',
27336                 cls : 'btn-group roo-upload-cropbox-trash',
27337                 action : 'trash',
27338                 cn : [
27339                     {
27340                         tag : 'button',
27341                         cls : 'btn btn-default',
27342                         html : '<i class="fa fa-trash"></i>'
27343                     }
27344                 ]
27345             },
27346             {
27347                 tag : 'div',
27348                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27349                 action : 'rotate-right',
27350                 cn : [
27351                     {
27352                         tag : 'button',
27353                         cls : 'btn btn-default',
27354                         html : '<i class="fa fa-repeat"></i>'
27355                     }
27356                 ]
27357             }
27358         ],
27359         ROTATOR : [
27360             {
27361                 tag : 'div',
27362                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27363                 action : 'rotate-left',
27364                 cn : [
27365                     {
27366                         tag : 'button',
27367                         cls : 'btn btn-default',
27368                         html : '<i class="fa fa-undo"></i>'
27369                     }
27370                 ]
27371             },
27372             {
27373                 tag : 'div',
27374                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27375                 action : 'rotate-right',
27376                 cn : [
27377                     {
27378                         tag : 'button',
27379                         cls : 'btn btn-default',
27380                         html : '<i class="fa fa-repeat"></i>'
27381                     }
27382                 ]
27383             }
27384         ]
27385     }
27386 });
27387
27388 /*
27389 * Licence: LGPL
27390 */
27391
27392 /**
27393  * @class Roo.bootstrap.DocumentManager
27394  * @extends Roo.bootstrap.Component
27395  * Bootstrap DocumentManager class
27396  * @cfg {String} paramName default 'imageUpload'
27397  * @cfg {String} method default POST
27398  * @cfg {String} url action url
27399  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
27400  * @cfg {Boolean} multiple multiple upload default true
27401  * @cfg {Number} thumbSize default 300
27402  * @cfg {String} fieldLabel
27403  * @cfg {Number} labelWidth default 4
27404  * @cfg {String} labelAlign (left|top) default left
27405  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
27406  * 
27407  * @constructor
27408  * Create a new DocumentManager
27409  * @param {Object} config The config object
27410  */
27411
27412 Roo.bootstrap.DocumentManager = function(config){
27413     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
27414     
27415     this.files = [];
27416     this.delegates = [];
27417     
27418     this.addEvents({
27419         /**
27420          * @event initial
27421          * Fire when initial the DocumentManager
27422          * @param {Roo.bootstrap.DocumentManager} this
27423          */
27424         "initial" : true,
27425         /**
27426          * @event inspect
27427          * inspect selected file
27428          * @param {Roo.bootstrap.DocumentManager} this
27429          * @param {File} file
27430          */
27431         "inspect" : true,
27432         /**
27433          * @event exception
27434          * Fire when xhr load exception
27435          * @param {Roo.bootstrap.DocumentManager} this
27436          * @param {XMLHttpRequest} xhr
27437          */
27438         "exception" : true,
27439         /**
27440          * @event afterupload
27441          * Fire when xhr load exception
27442          * @param {Roo.bootstrap.DocumentManager} this
27443          * @param {XMLHttpRequest} xhr
27444          */
27445         "afterupload" : true,
27446         /**
27447          * @event prepare
27448          * prepare the form data
27449          * @param {Roo.bootstrap.DocumentManager} this
27450          * @param {Object} formData
27451          */
27452         "prepare" : true,
27453         /**
27454          * @event remove
27455          * Fire when remove the file
27456          * @param {Roo.bootstrap.DocumentManager} this
27457          * @param {Object} file
27458          */
27459         "remove" : true,
27460         /**
27461          * @event refresh
27462          * Fire after refresh the file
27463          * @param {Roo.bootstrap.DocumentManager} this
27464          */
27465         "refresh" : true,
27466         /**
27467          * @event click
27468          * Fire after click the image
27469          * @param {Roo.bootstrap.DocumentManager} this
27470          * @param {Object} file
27471          */
27472         "click" : true,
27473         /**
27474          * @event edit
27475          * Fire when upload a image and editable set to true
27476          * @param {Roo.bootstrap.DocumentManager} this
27477          * @param {Object} file
27478          */
27479         "edit" : true,
27480         /**
27481          * @event beforeselectfile
27482          * Fire before select file
27483          * @param {Roo.bootstrap.DocumentManager} this
27484          */
27485         "beforeselectfile" : true,
27486         /**
27487          * @event process
27488          * Fire before process file
27489          * @param {Roo.bootstrap.DocumentManager} this
27490          * @param {Object} file
27491          */
27492         "process" : true
27493         
27494     });
27495 };
27496
27497 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
27498     
27499     boxes : 0,
27500     inputName : '',
27501     thumbSize : 300,
27502     multiple : true,
27503     files : false,
27504     method : 'POST',
27505     url : '',
27506     paramName : 'imageUpload',
27507     fieldLabel : '',
27508     labelWidth : 4,
27509     labelAlign : 'left',
27510     editable : true,
27511     delegates : false,
27512     
27513     
27514     xhr : false, 
27515     
27516     getAutoCreate : function()
27517     {   
27518         var managerWidget = {
27519             tag : 'div',
27520             cls : 'roo-document-manager',
27521             cn : [
27522                 {
27523                     tag : 'input',
27524                     cls : 'roo-document-manager-selector',
27525                     type : 'file'
27526                 },
27527                 {
27528                     tag : 'div',
27529                     cls : 'roo-document-manager-uploader',
27530                     cn : [
27531                         {
27532                             tag : 'div',
27533                             cls : 'roo-document-manager-upload-btn',
27534                             html : '<i class="fa fa-plus"></i>'
27535                         }
27536                     ]
27537                     
27538                 }
27539             ]
27540         };
27541         
27542         var content = [
27543             {
27544                 tag : 'div',
27545                 cls : 'column col-md-12',
27546                 cn : managerWidget
27547             }
27548         ];
27549         
27550         if(this.fieldLabel.length){
27551             
27552             content = [
27553                 {
27554                     tag : 'div',
27555                     cls : 'column col-md-12',
27556                     html : this.fieldLabel
27557                 },
27558                 {
27559                     tag : 'div',
27560                     cls : 'column col-md-12',
27561                     cn : managerWidget
27562                 }
27563             ];
27564
27565             if(this.labelAlign == 'left'){
27566                 content = [
27567                     {
27568                         tag : 'div',
27569                         cls : 'column col-md-' + this.labelWidth,
27570                         html : this.fieldLabel
27571                     },
27572                     {
27573                         tag : 'div',
27574                         cls : 'column col-md-' + (12 - this.labelWidth),
27575                         cn : managerWidget
27576                     }
27577                 ];
27578                 
27579             }
27580         }
27581         
27582         var cfg = {
27583             tag : 'div',
27584             cls : 'row clearfix',
27585             cn : content
27586         };
27587         
27588         return cfg;
27589         
27590     },
27591     
27592     initEvents : function()
27593     {
27594         this.managerEl = this.el.select('.roo-document-manager', true).first();
27595         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27596         
27597         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
27598         this.selectorEl.hide();
27599         
27600         if(this.multiple){
27601             this.selectorEl.attr('multiple', 'multiple');
27602         }
27603         
27604         this.selectorEl.on('change', this.onFileSelected, this);
27605         
27606         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
27607         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27608         
27609         this.uploader.on('click', this.onUploaderClick, this);
27610         
27611         this.renderProgressDialog();
27612         
27613         var _this = this;
27614         
27615         window.addEventListener("resize", function() { _this.refresh(); } );
27616         
27617         this.fireEvent('initial', this);
27618     },
27619     
27620     renderProgressDialog : function()
27621     {
27622         var _this = this;
27623         
27624         this.progressDialog = new Roo.bootstrap.Modal({
27625             cls : 'roo-document-manager-progress-dialog',
27626             allow_close : false,
27627             title : '',
27628             buttons : [
27629                 {
27630                     name  :'cancel',
27631                     weight : 'danger',
27632                     html : 'Cancel'
27633                 }
27634             ], 
27635             listeners : { 
27636                 btnclick : function() {
27637                     _this.uploadCancel();
27638                     this.hide();
27639                 }
27640             }
27641         });
27642          
27643         this.progressDialog.render(Roo.get(document.body));
27644          
27645         this.progress = new Roo.bootstrap.Progress({
27646             cls : 'roo-document-manager-progress',
27647             active : true,
27648             striped : true
27649         });
27650         
27651         this.progress.render(this.progressDialog.getChildContainer());
27652         
27653         this.progressBar = new Roo.bootstrap.ProgressBar({
27654             cls : 'roo-document-manager-progress-bar',
27655             aria_valuenow : 0,
27656             aria_valuemin : 0,
27657             aria_valuemax : 12,
27658             panel : 'success'
27659         });
27660         
27661         this.progressBar.render(this.progress.getChildContainer());
27662     },
27663     
27664     onUploaderClick : function(e)
27665     {
27666         e.preventDefault();
27667      
27668         if(this.fireEvent('beforeselectfile', this) != false){
27669             this.selectorEl.dom.click();
27670         }
27671         
27672     },
27673     
27674     onFileSelected : function(e)
27675     {
27676         e.preventDefault();
27677         
27678         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27679             return;
27680         }
27681         
27682         Roo.each(this.selectorEl.dom.files, function(file){
27683             if(this.fireEvent('inspect', this, file) != false){
27684                 this.files.push(file);
27685             }
27686         }, this);
27687         
27688         this.queue();
27689         
27690     },
27691     
27692     queue : function()
27693     {
27694         this.selectorEl.dom.value = '';
27695         
27696         if(!this.files.length){
27697             return;
27698         }
27699         
27700         if(this.boxes > 0 && this.files.length > this.boxes){
27701             this.files = this.files.slice(0, this.boxes);
27702         }
27703         
27704         this.uploader.show();
27705         
27706         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27707             this.uploader.hide();
27708         }
27709         
27710         var _this = this;
27711         
27712         var files = [];
27713         
27714         var docs = [];
27715         
27716         Roo.each(this.files, function(file){
27717             
27718             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27719                 var f = this.renderPreview(file);
27720                 files.push(f);
27721                 return;
27722             }
27723             
27724             if(file.type.indexOf('image') != -1){
27725                 this.delegates.push(
27726                     (function(){
27727                         _this.process(file);
27728                     }).createDelegate(this)
27729                 );
27730         
27731                 return;
27732             }
27733             
27734             docs.push(
27735                 (function(){
27736                     _this.process(file);
27737                 }).createDelegate(this)
27738             );
27739             
27740         }, this);
27741         
27742         this.files = files;
27743         
27744         this.delegates = this.delegates.concat(docs);
27745         
27746         if(!this.delegates.length){
27747             this.refresh();
27748             return;
27749         }
27750         
27751         this.progressBar.aria_valuemax = this.delegates.length;
27752         
27753         this.arrange();
27754         
27755         return;
27756     },
27757     
27758     arrange : function()
27759     {
27760         if(!this.delegates.length){
27761             this.progressDialog.hide();
27762             this.refresh();
27763             return;
27764         }
27765         
27766         var delegate = this.delegates.shift();
27767         
27768         this.progressDialog.show();
27769         
27770         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27771         
27772         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27773         
27774         delegate();
27775     },
27776     
27777     refresh : function()
27778     {
27779         this.uploader.show();
27780         
27781         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27782             this.uploader.hide();
27783         }
27784         
27785         Roo.isTouch ? this.closable(false) : this.closable(true);
27786         
27787         this.fireEvent('refresh', this);
27788     },
27789     
27790     onRemove : function(e, el, o)
27791     {
27792         e.preventDefault();
27793         
27794         this.fireEvent('remove', this, o);
27795         
27796     },
27797     
27798     remove : function(o)
27799     {
27800         var files = [];
27801         
27802         Roo.each(this.files, function(file){
27803             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27804                 files.push(file);
27805                 return;
27806             }
27807
27808             o.target.remove();
27809
27810         }, this);
27811         
27812         this.files = files;
27813         
27814         this.refresh();
27815     },
27816     
27817     clear : function()
27818     {
27819         Roo.each(this.files, function(file){
27820             if(!file.target){
27821                 return;
27822             }
27823             
27824             file.target.remove();
27825
27826         }, this);
27827         
27828         this.files = [];
27829         
27830         this.refresh();
27831     },
27832     
27833     onClick : function(e, el, o)
27834     {
27835         e.preventDefault();
27836         
27837         this.fireEvent('click', this, o);
27838         
27839     },
27840     
27841     closable : function(closable)
27842     {
27843         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27844             
27845             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27846             
27847             if(closable){
27848                 el.show();
27849                 return;
27850             }
27851             
27852             el.hide();
27853             
27854         }, this);
27855     },
27856     
27857     xhrOnLoad : function(xhr)
27858     {
27859         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27860             el.remove();
27861         }, this);
27862         
27863         if (xhr.readyState !== 4) {
27864             this.arrange();
27865             this.fireEvent('exception', this, xhr);
27866             return;
27867         }
27868
27869         var response = Roo.decode(xhr.responseText);
27870         
27871         if(!response.success){
27872             this.arrange();
27873             this.fireEvent('exception', this, xhr);
27874             return;
27875         }
27876         
27877         var file = this.renderPreview(response.data);
27878         
27879         this.files.push(file);
27880         
27881         this.arrange();
27882         
27883         this.fireEvent('onUpload', this, xhr);
27884         
27885     },
27886     
27887     xhrOnError : function(xhr)
27888     {
27889         Roo.log('xhr on error');
27890         
27891         var response = Roo.decode(xhr.responseText);
27892           
27893         Roo.log(response);
27894         
27895         this.arrange();
27896     },
27897     
27898     process : function(file)
27899     {
27900         if(this.fireEvent('process', this, file) !== false){
27901             if(this.editable && file.type.indexOf('image') != -1){
27902                 this.fireEvent('edit', this, file);
27903                 return;
27904             }
27905
27906             this.uploadStart(file, false);
27907
27908             return;
27909         }
27910         
27911     },
27912     
27913     uploadStart : function(file, crop)
27914     {
27915         this.xhr = new XMLHttpRequest();
27916         
27917         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27918             this.arrange();
27919             return;
27920         }
27921         
27922         file.xhr = this.xhr;
27923             
27924         this.managerEl.createChild({
27925             tag : 'div',
27926             cls : 'roo-document-manager-loading',
27927             cn : [
27928                 {
27929                     tag : 'div',
27930                     tooltip : file.name,
27931                     cls : 'roo-document-manager-thumb',
27932                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27933                 }
27934             ]
27935
27936         });
27937
27938         this.xhr.open(this.method, this.url, true);
27939         
27940         var headers = {
27941             "Accept": "application/json",
27942             "Cache-Control": "no-cache",
27943             "X-Requested-With": "XMLHttpRequest"
27944         };
27945         
27946         for (var headerName in headers) {
27947             var headerValue = headers[headerName];
27948             if (headerValue) {
27949                 this.xhr.setRequestHeader(headerName, headerValue);
27950             }
27951         }
27952         
27953         var _this = this;
27954         
27955         this.xhr.onload = function()
27956         {
27957             _this.xhrOnLoad(_this.xhr);
27958         }
27959         
27960         this.xhr.onerror = function()
27961         {
27962             _this.xhrOnError(_this.xhr);
27963         }
27964         
27965         var formData = new FormData();
27966
27967         formData.append('returnHTML', 'NO');
27968         
27969         if(crop){
27970             formData.append('crop', crop);
27971         }
27972         
27973         formData.append(this.paramName, file, file.name);
27974         
27975         if(this.fireEvent('prepare', this, formData) != false){
27976             this.xhr.send(formData);
27977         };
27978     },
27979     
27980     uploadCancel : function()
27981     {
27982         if (this.xhr) {
27983             this.xhr.abort();
27984         }
27985         
27986         
27987         this.delegates = [];
27988         
27989         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27990             el.remove();
27991         }, this);
27992         
27993         this.arrange();
27994     },
27995     
27996     renderPreview : function(file)
27997     {
27998         if(typeof(file.target) != 'undefined' && file.target){
27999             return file;
28000         }
28001         
28002         var previewEl = this.managerEl.createChild({
28003             tag : 'div',
28004             cls : 'roo-document-manager-preview',
28005             cn : [
28006                 {
28007                     tag : 'div',
28008                     tooltip : file.filename,
28009                     cls : 'roo-document-manager-thumb',
28010                     html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
28011                 },
28012                 {
28013                     tag : 'button',
28014                     cls : 'close',
28015                     html : '<i class="fa fa-times-circle"></i>'
28016                 }
28017             ]
28018         });
28019
28020         var close = previewEl.select('button.close', true).first();
28021
28022         close.on('click', this.onRemove, this, file);
28023
28024         file.target = previewEl;
28025
28026         var image = previewEl.select('img', true).first();
28027         
28028         var _this = this;
28029         
28030         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28031         
28032         image.on('click', this.onClick, this, file);
28033         
28034         return file;
28035         
28036     },
28037     
28038     onPreviewLoad : function(file, image)
28039     {
28040         if(typeof(file.target) == 'undefined' || !file.target){
28041             return;
28042         }
28043         
28044         var width = image.dom.naturalWidth || image.dom.width;
28045         var height = image.dom.naturalHeight || image.dom.height;
28046         
28047         if(width > height){
28048             file.target.addClass('wide');
28049             return;
28050         }
28051         
28052         file.target.addClass('tall');
28053         return;
28054         
28055     },
28056     
28057     uploadFromSource : function(file, crop)
28058     {
28059         this.xhr = new XMLHttpRequest();
28060         
28061         this.managerEl.createChild({
28062             tag : 'div',
28063             cls : 'roo-document-manager-loading',
28064             cn : [
28065                 {
28066                     tag : 'div',
28067                     tooltip : file.name,
28068                     cls : 'roo-document-manager-thumb',
28069                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28070                 }
28071             ]
28072
28073         });
28074
28075         this.xhr.open(this.method, this.url, true);
28076         
28077         var headers = {
28078             "Accept": "application/json",
28079             "Cache-Control": "no-cache",
28080             "X-Requested-With": "XMLHttpRequest"
28081         };
28082         
28083         for (var headerName in headers) {
28084             var headerValue = headers[headerName];
28085             if (headerValue) {
28086                 this.xhr.setRequestHeader(headerName, headerValue);
28087             }
28088         }
28089         
28090         var _this = this;
28091         
28092         this.xhr.onload = function()
28093         {
28094             _this.xhrOnLoad(_this.xhr);
28095         }
28096         
28097         this.xhr.onerror = function()
28098         {
28099             _this.xhrOnError(_this.xhr);
28100         }
28101         
28102         var formData = new FormData();
28103
28104         formData.append('returnHTML', 'NO');
28105         
28106         formData.append('crop', crop);
28107         
28108         if(typeof(file.filename) != 'undefined'){
28109             formData.append('filename', file.filename);
28110         }
28111         
28112         if(typeof(file.mimetype) != 'undefined'){
28113             formData.append('mimetype', file.mimetype);
28114         }
28115         
28116         if(this.fireEvent('prepare', this, formData) != false){
28117             this.xhr.send(formData);
28118         };
28119     }
28120 });
28121
28122 /*
28123 * Licence: LGPL
28124 */
28125
28126 /**
28127  * @class Roo.bootstrap.DocumentViewer
28128  * @extends Roo.bootstrap.Component
28129  * Bootstrap DocumentViewer class
28130  * @cfg {Boolean} showDownload (true|false) show download button (default true)
28131  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
28132  * 
28133  * @constructor
28134  * Create a new DocumentViewer
28135  * @param {Object} config The config object
28136  */
28137
28138 Roo.bootstrap.DocumentViewer = function(config){
28139     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
28140     
28141     this.addEvents({
28142         /**
28143          * @event initial
28144          * Fire after initEvent
28145          * @param {Roo.bootstrap.DocumentViewer} this
28146          */
28147         "initial" : true,
28148         /**
28149          * @event click
28150          * Fire after click
28151          * @param {Roo.bootstrap.DocumentViewer} this
28152          */
28153         "click" : true,
28154         /**
28155          * @event download
28156          * Fire after download button
28157          * @param {Roo.bootstrap.DocumentViewer} this
28158          */
28159         "download" : true,
28160         /**
28161          * @event trash
28162          * Fire after trash button
28163          * @param {Roo.bootstrap.DocumentViewer} this
28164          */
28165         "trash" : true
28166         
28167     });
28168 };
28169
28170 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
28171     
28172     showDownload : true,
28173     
28174     showTrash : true,
28175     
28176     getAutoCreate : function()
28177     {
28178         var cfg = {
28179             tag : 'div',
28180             cls : 'roo-document-viewer',
28181             cn : [
28182                 {
28183                     tag : 'div',
28184                     cls : 'roo-document-viewer-body',
28185                     cn : [
28186                         {
28187                             tag : 'div',
28188                             cls : 'roo-document-viewer-thumb',
28189                             cn : [
28190                                 {
28191                                     tag : 'img',
28192                                     cls : 'roo-document-viewer-image'
28193                                 }
28194                             ]
28195                         }
28196                     ]
28197                 },
28198                 {
28199                     tag : 'div',
28200                     cls : 'roo-document-viewer-footer',
28201                     cn : {
28202                         tag : 'div',
28203                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
28204                         cn : [
28205                             {
28206                                 tag : 'div',
28207                                 cls : 'btn-group roo-document-viewer-download',
28208                                 cn : [
28209                                     {
28210                                         tag : 'button',
28211                                         cls : 'btn btn-default',
28212                                         html : '<i class="fa fa-download"></i>'
28213                                     }
28214                                 ]
28215                             },
28216                             {
28217                                 tag : 'div',
28218                                 cls : 'btn-group roo-document-viewer-trash',
28219                                 cn : [
28220                                     {
28221                                         tag : 'button',
28222                                         cls : 'btn btn-default',
28223                                         html : '<i class="fa fa-trash"></i>'
28224                                     }
28225                                 ]
28226                             }
28227                         ]
28228                     }
28229                 }
28230             ]
28231         };
28232         
28233         return cfg;
28234     },
28235     
28236     initEvents : function()
28237     {
28238         
28239         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
28240         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
28241         
28242         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
28243         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
28244         
28245         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
28246         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
28247         
28248         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
28249         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
28250         
28251         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
28252         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
28253         
28254         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
28255         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
28256         
28257         this.bodyEl.on('click', this.onClick, this);
28258         this.downloadBtn.on('click', this.onDownload, this);
28259         this.trashBtn.on('click', this.onTrash, this);
28260         
28261         this.downloadBtn.hide();
28262         this.trashBtn.hide();
28263         
28264         if(this.showDownload){
28265             this.downloadBtn.show();
28266         }
28267         
28268         if(this.showTrash){
28269             this.trashBtn.show();
28270         }
28271         
28272         if(!this.showDownload && !this.showTrash) {
28273             this.footerEl.hide();
28274         }
28275         
28276     },
28277     
28278     initial : function()
28279     {
28280 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
28281         
28282         
28283         this.fireEvent('initial', this);
28284         
28285     },
28286     
28287     onClick : function(e)
28288     {
28289         e.preventDefault();
28290         
28291         this.fireEvent('click', this);
28292     },
28293     
28294     onDownload : function(e)
28295     {
28296         e.preventDefault();
28297         
28298         this.fireEvent('download', this);
28299     },
28300     
28301     onTrash : function(e)
28302     {
28303         e.preventDefault();
28304         
28305         this.fireEvent('trash', this);
28306     }
28307     
28308 });
28309 /*
28310  * - LGPL
28311  *
28312  * nav progress bar
28313  * 
28314  */
28315
28316 /**
28317  * @class Roo.bootstrap.NavProgressBar
28318  * @extends Roo.bootstrap.Component
28319  * Bootstrap NavProgressBar class
28320  * 
28321  * @constructor
28322  * Create a new nav progress bar
28323  * @param {Object} config The config object
28324  */
28325
28326 Roo.bootstrap.NavProgressBar = function(config){
28327     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
28328
28329     this.bullets = this.bullets || [];
28330    
28331 //    Roo.bootstrap.NavProgressBar.register(this);
28332      this.addEvents({
28333         /**
28334              * @event changed
28335              * Fires when the active item changes
28336              * @param {Roo.bootstrap.NavProgressBar} this
28337              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
28338              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
28339          */
28340         'changed': true
28341      });
28342     
28343 };
28344
28345 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
28346     
28347     bullets : [],
28348     barItems : [],
28349     
28350     getAutoCreate : function()
28351     {
28352         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
28353         
28354         cfg = {
28355             tag : 'div',
28356             cls : 'roo-navigation-bar-group',
28357             cn : [
28358                 {
28359                     tag : 'div',
28360                     cls : 'roo-navigation-top-bar'
28361                 },
28362                 {
28363                     tag : 'div',
28364                     cls : 'roo-navigation-bullets-bar',
28365                     cn : [
28366                         {
28367                             tag : 'ul',
28368                             cls : 'roo-navigation-bar'
28369                         }
28370                     ]
28371                 },
28372                 
28373                 {
28374                     tag : 'div',
28375                     cls : 'roo-navigation-bottom-bar'
28376                 }
28377             ]
28378             
28379         };
28380         
28381         return cfg;
28382         
28383     },
28384     
28385     initEvents: function() 
28386     {
28387         
28388     },
28389     
28390     onRender : function(ct, position) 
28391     {
28392         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28393         
28394         if(this.bullets.length){
28395             Roo.each(this.bullets, function(b){
28396                this.addItem(b);
28397             }, this);
28398         }
28399         
28400         this.format();
28401         
28402     },
28403     
28404     addItem : function(cfg)
28405     {
28406         var item = new Roo.bootstrap.NavProgressItem(cfg);
28407         
28408         item.parentId = this.id;
28409         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
28410         
28411         if(cfg.html){
28412             var top = new Roo.bootstrap.Element({
28413                 tag : 'div',
28414                 cls : 'roo-navigation-bar-text'
28415             });
28416             
28417             var bottom = new Roo.bootstrap.Element({
28418                 tag : 'div',
28419                 cls : 'roo-navigation-bar-text'
28420             });
28421             
28422             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
28423             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
28424             
28425             var topText = new Roo.bootstrap.Element({
28426                 tag : 'span',
28427                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
28428             });
28429             
28430             var bottomText = new Roo.bootstrap.Element({
28431                 tag : 'span',
28432                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
28433             });
28434             
28435             topText.onRender(top.el, null);
28436             bottomText.onRender(bottom.el, null);
28437             
28438             item.topEl = top;
28439             item.bottomEl = bottom;
28440         }
28441         
28442         this.barItems.push(item);
28443         
28444         return item;
28445     },
28446     
28447     getActive : function()
28448     {
28449         var active = false;
28450         
28451         Roo.each(this.barItems, function(v){
28452             
28453             if (!v.isActive()) {
28454                 return;
28455             }
28456             
28457             active = v;
28458             return false;
28459             
28460         });
28461         
28462         return active;
28463     },
28464     
28465     setActiveItem : function(item)
28466     {
28467         var prev = false;
28468         
28469         Roo.each(this.barItems, function(v){
28470             if (v.rid == item.rid) {
28471                 return ;
28472             }
28473             
28474             if (v.isActive()) {
28475                 v.setActive(false);
28476                 prev = v;
28477             }
28478         });
28479
28480         item.setActive(true);
28481         
28482         this.fireEvent('changed', this, item, prev);
28483     },
28484     
28485     getBarItem: function(rid)
28486     {
28487         var ret = false;
28488         
28489         Roo.each(this.barItems, function(e) {
28490             if (e.rid != rid) {
28491                 return;
28492             }
28493             
28494             ret =  e;
28495             return false;
28496         });
28497         
28498         return ret;
28499     },
28500     
28501     indexOfItem : function(item)
28502     {
28503         var index = false;
28504         
28505         Roo.each(this.barItems, function(v, i){
28506             
28507             if (v.rid != item.rid) {
28508                 return;
28509             }
28510             
28511             index = i;
28512             return false
28513         });
28514         
28515         return index;
28516     },
28517     
28518     setActiveNext : function()
28519     {
28520         var i = this.indexOfItem(this.getActive());
28521         
28522         if (i > this.barItems.length) {
28523             return;
28524         }
28525         
28526         this.setActiveItem(this.barItems[i+1]);
28527     },
28528     
28529     setActivePrev : function()
28530     {
28531         var i = this.indexOfItem(this.getActive());
28532         
28533         if (i  < 1) {
28534             return;
28535         }
28536         
28537         this.setActiveItem(this.barItems[i-1]);
28538     },
28539     
28540     format : function()
28541     {
28542         if(!this.barItems.length){
28543             return;
28544         }
28545      
28546         var width = 100 / this.barItems.length;
28547         
28548         Roo.each(this.barItems, function(i){
28549             i.el.setStyle('width', width + '%');
28550             i.topEl.el.setStyle('width', width + '%');
28551             i.bottomEl.el.setStyle('width', width + '%');
28552         }, this);
28553         
28554     }
28555     
28556 });
28557 /*
28558  * - LGPL
28559  *
28560  * Nav Progress Item
28561  * 
28562  */
28563
28564 /**
28565  * @class Roo.bootstrap.NavProgressItem
28566  * @extends Roo.bootstrap.Component
28567  * Bootstrap NavProgressItem class
28568  * @cfg {String} rid the reference id
28569  * @cfg {Boolean} active (true|false) Is item active default false
28570  * @cfg {Boolean} disabled (true|false) Is item active default false
28571  * @cfg {String} html
28572  * @cfg {String} position (top|bottom) text position default bottom
28573  * @cfg {String} icon show icon instead of number
28574  * 
28575  * @constructor
28576  * Create a new NavProgressItem
28577  * @param {Object} config The config object
28578  */
28579 Roo.bootstrap.NavProgressItem = function(config){
28580     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
28581     this.addEvents({
28582         // raw events
28583         /**
28584          * @event click
28585          * The raw click event for the entire grid.
28586          * @param {Roo.bootstrap.NavProgressItem} this
28587          * @param {Roo.EventObject} e
28588          */
28589         "click" : true
28590     });
28591    
28592 };
28593
28594 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
28595     
28596     rid : '',
28597     active : false,
28598     disabled : false,
28599     html : '',
28600     position : 'bottom',
28601     icon : false,
28602     
28603     getAutoCreate : function()
28604     {
28605         var iconCls = 'roo-navigation-bar-item-icon';
28606         
28607         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
28608         
28609         var cfg = {
28610             tag: 'li',
28611             cls: 'roo-navigation-bar-item',
28612             cn : [
28613                 {
28614                     tag : 'i',
28615                     cls : iconCls
28616                 }
28617             ]
28618         };
28619         
28620         if(this.active){
28621             cfg.cls += ' active';
28622         }
28623         if(this.disabled){
28624             cfg.cls += ' disabled';
28625         }
28626         
28627         return cfg;
28628     },
28629     
28630     disable : function()
28631     {
28632         this.setDisabled(true);
28633     },
28634     
28635     enable : function()
28636     {
28637         this.setDisabled(false);
28638     },
28639     
28640     initEvents: function() 
28641     {
28642         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
28643         
28644         this.iconEl.on('click', this.onClick, this);
28645     },
28646     
28647     onClick : function(e)
28648     {
28649         e.preventDefault();
28650         
28651         if(this.disabled){
28652             return;
28653         }
28654         
28655         if(this.fireEvent('click', this, e) === false){
28656             return;
28657         };
28658         
28659         this.parent().setActiveItem(this);
28660     },
28661     
28662     isActive: function () 
28663     {
28664         return this.active;
28665     },
28666     
28667     setActive : function(state)
28668     {
28669         if(this.active == state){
28670             return;
28671         }
28672         
28673         this.active = state;
28674         
28675         if (state) {
28676             this.el.addClass('active');
28677             return;
28678         }
28679         
28680         this.el.removeClass('active');
28681         
28682         return;
28683     },
28684     
28685     setDisabled : function(state)
28686     {
28687         if(this.disabled == state){
28688             return;
28689         }
28690         
28691         this.disabled = state;
28692         
28693         if (state) {
28694             this.el.addClass('disabled');
28695             return;
28696         }
28697         
28698         this.el.removeClass('disabled');
28699     },
28700     
28701     tooltipEl : function()
28702     {
28703         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
28704     }
28705 });
28706  
28707
28708  /*
28709  * - LGPL
28710  *
28711  * FieldLabel
28712  * 
28713  */
28714
28715 /**
28716  * @class Roo.bootstrap.FieldLabel
28717  * @extends Roo.bootstrap.Component
28718  * Bootstrap FieldLabel class
28719  * @cfg {String} html contents of the element
28720  * @cfg {String} tag tag of the element default label
28721  * @cfg {String} cls class of the element
28722  * @cfg {String} target label target 
28723  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
28724  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
28725  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
28726  * @cfg {String} iconTooltip default "This field is required"
28727  * 
28728  * @constructor
28729  * Create a new FieldLabel
28730  * @param {Object} config The config object
28731  */
28732
28733 Roo.bootstrap.FieldLabel = function(config){
28734     Roo.bootstrap.Element.superclass.constructor.call(this, config);
28735     
28736     this.addEvents({
28737             /**
28738              * @event invalid
28739              * Fires after the field has been marked as invalid.
28740              * @param {Roo.form.FieldLabel} this
28741              * @param {String} msg The validation message
28742              */
28743             invalid : true,
28744             /**
28745              * @event valid
28746              * Fires after the field has been validated with no errors.
28747              * @param {Roo.form.FieldLabel} this
28748              */
28749             valid : true
28750         });
28751 };
28752
28753 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
28754     
28755     tag: 'label',
28756     cls: '',
28757     html: '',
28758     target: '',
28759     allowBlank : true,
28760     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
28761     validClass : 'text-success fa fa-lg fa-check',
28762     iconTooltip : 'This field is required',
28763     
28764     getAutoCreate : function(){
28765         
28766         var cfg = {
28767             tag : this.tag,
28768             cls : 'roo-bootstrap-field-label ' + this.cls,
28769             for : this.target,
28770             cn : [
28771                 {
28772                     tag : 'i',
28773                     cls : '',
28774                     tooltip : this.iconTooltip
28775                 },
28776                 {
28777                     tag : 'span',
28778                     html : this.html
28779                 }
28780             ] 
28781         };
28782         
28783         return cfg;
28784     },
28785     
28786     initEvents: function() 
28787     {
28788         Roo.bootstrap.Element.superclass.initEvents.call(this);
28789         
28790         this.iconEl = this.el.select('i', true).first();
28791         
28792         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
28793         
28794         Roo.bootstrap.FieldLabel.register(this);
28795     },
28796     
28797     /**
28798      * Mark this field as valid
28799      */
28800     markValid : function()
28801     {
28802         this.iconEl.show();
28803         
28804         this.iconEl.removeClass(this.invalidClass);
28805         
28806         this.iconEl.addClass(this.validClass);
28807         
28808         this.fireEvent('valid', this);
28809     },
28810     
28811     /**
28812      * Mark this field as invalid
28813      * @param {String} msg The validation message
28814      */
28815     markInvalid : function(msg)
28816     {
28817         this.iconEl.show();
28818         
28819         this.iconEl.removeClass(this.validClass);
28820         
28821         this.iconEl.addClass(this.invalidClass);
28822         
28823         this.fireEvent('invalid', this, msg);
28824     }
28825     
28826    
28827 });
28828
28829 Roo.apply(Roo.bootstrap.FieldLabel, {
28830     
28831     groups: {},
28832     
28833      /**
28834     * register a FieldLabel Group
28835     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28836     */
28837     register : function(label)
28838     {
28839         if(this.groups.hasOwnProperty(label.target)){
28840             return;
28841         }
28842      
28843         this.groups[label.target] = label;
28844         
28845     },
28846     /**
28847     * fetch a FieldLabel Group based on the target
28848     * @param {string} target
28849     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28850     */
28851     get: function(target) {
28852         if (typeof(this.groups[target]) == 'undefined') {
28853             return false;
28854         }
28855         
28856         return this.groups[target] ;
28857     }
28858 });
28859
28860  
28861
28862  /*
28863  * - LGPL
28864  *
28865  * page DateSplitField.
28866  * 
28867  */
28868
28869
28870 /**
28871  * @class Roo.bootstrap.DateSplitField
28872  * @extends Roo.bootstrap.Component
28873  * Bootstrap DateSplitField class
28874  * @cfg {string} fieldLabel - the label associated
28875  * @cfg {Number} labelWidth set the width of label (0-12)
28876  * @cfg {String} labelAlign (top|left)
28877  * @cfg {Boolean} dayAllowBlank (true|false) default false
28878  * @cfg {Boolean} monthAllowBlank (true|false) default false
28879  * @cfg {Boolean} yearAllowBlank (true|false) default false
28880  * @cfg {string} dayPlaceholder 
28881  * @cfg {string} monthPlaceholder
28882  * @cfg {string} yearPlaceholder
28883  * @cfg {string} dayFormat default 'd'
28884  * @cfg {string} monthFormat default 'm'
28885  * @cfg {string} yearFormat default 'Y'
28886
28887  *     
28888  * @constructor
28889  * Create a new DateSplitField
28890  * @param {Object} config The config object
28891  */
28892
28893 Roo.bootstrap.DateSplitField = function(config){
28894     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28895     
28896     this.addEvents({
28897         // raw events
28898          /**
28899          * @event years
28900          * getting the data of years
28901          * @param {Roo.bootstrap.DateSplitField} this
28902          * @param {Object} years
28903          */
28904         "years" : true,
28905         /**
28906          * @event days
28907          * getting the data of days
28908          * @param {Roo.bootstrap.DateSplitField} this
28909          * @param {Object} days
28910          */
28911         "days" : true,
28912         /**
28913          * @event invalid
28914          * Fires after the field has been marked as invalid.
28915          * @param {Roo.form.Field} this
28916          * @param {String} msg The validation message
28917          */
28918         invalid : true,
28919        /**
28920          * @event valid
28921          * Fires after the field has been validated with no errors.
28922          * @param {Roo.form.Field} this
28923          */
28924         valid : true
28925     });
28926 };
28927
28928 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
28929     
28930     fieldLabel : '',
28931     labelAlign : 'top',
28932     labelWidth : 3,
28933     dayAllowBlank : false,
28934     monthAllowBlank : false,
28935     yearAllowBlank : false,
28936     dayPlaceholder : '',
28937     monthPlaceholder : '',
28938     yearPlaceholder : '',
28939     dayFormat : 'd',
28940     monthFormat : 'm',
28941     yearFormat : 'Y',
28942     isFormField : true,
28943     
28944     getAutoCreate : function()
28945     {
28946         var cfg = {
28947             tag : 'div',
28948             cls : 'row roo-date-split-field-group',
28949             cn : [
28950                 {
28951                     tag : 'input',
28952                     type : 'hidden',
28953                     cls : 'form-hidden-field roo-date-split-field-group-value',
28954                     name : this.name
28955                 }
28956             ]
28957         };
28958         
28959         if(this.fieldLabel){
28960             cfg.cn.push({
28961                 tag : 'div',
28962                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
28963                 cn : [
28964                     {
28965                         tag : 'label',
28966                         html : this.fieldLabel
28967                     }
28968                 ]
28969             });
28970         }
28971         
28972         Roo.each(['day', 'month', 'year'], function(t){
28973             cfg.cn.push({
28974                 tag : 'div',
28975                 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
28976             });
28977         }, this);
28978         
28979         return cfg;
28980     },
28981     
28982     inputEl: function ()
28983     {
28984         return this.el.select('.roo-date-split-field-group-value', true).first();
28985     },
28986     
28987     onRender : function(ct, position) 
28988     {
28989         var _this = this;
28990         
28991         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28992         
28993         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
28994         
28995         this.dayField = new Roo.bootstrap.ComboBox({
28996             allowBlank : this.dayAllowBlank,
28997             alwaysQuery : true,
28998             displayField : 'value',
28999             editable : false,
29000             fieldLabel : '',
29001             forceSelection : true,
29002             mode : 'local',
29003             placeholder : this.dayPlaceholder,
29004             selectOnFocus : true,
29005             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29006             triggerAction : 'all',
29007             typeAhead : true,
29008             valueField : 'value',
29009             store : new Roo.data.SimpleStore({
29010                 data : (function() {    
29011                     var days = [];
29012                     _this.fireEvent('days', _this, days);
29013                     return days;
29014                 })(),
29015                 fields : [ 'value' ]
29016             }),
29017             listeners : {
29018                 select : function (_self, record, index)
29019                 {
29020                     _this.setValue(_this.getValue());
29021                 }
29022             }
29023         });
29024
29025         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
29026         
29027         this.monthField = new Roo.bootstrap.MonthField({
29028             after : '<i class=\"fa fa-calendar\"></i>',
29029             allowBlank : this.monthAllowBlank,
29030             placeholder : this.monthPlaceholder,
29031             readOnly : true,
29032             listeners : {
29033                 render : function (_self)
29034                 {
29035                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
29036                         e.preventDefault();
29037                         _self.focus();
29038                     });
29039                 },
29040                 select : function (_self, oldvalue, newvalue)
29041                 {
29042                     _this.setValue(_this.getValue());
29043                 }
29044             }
29045         });
29046         
29047         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
29048         
29049         this.yearField = new Roo.bootstrap.ComboBox({
29050             allowBlank : this.yearAllowBlank,
29051             alwaysQuery : true,
29052             displayField : 'value',
29053             editable : false,
29054             fieldLabel : '',
29055             forceSelection : true,
29056             mode : 'local',
29057             placeholder : this.yearPlaceholder,
29058             selectOnFocus : true,
29059             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29060             triggerAction : 'all',
29061             typeAhead : true,
29062             valueField : 'value',
29063             store : new Roo.data.SimpleStore({
29064                 data : (function() {
29065                     var years = [];
29066                     _this.fireEvent('years', _this, years);
29067                     return years;
29068                 })(),
29069                 fields : [ 'value' ]
29070             }),
29071             listeners : {
29072                 select : function (_self, record, index)
29073                 {
29074                     _this.setValue(_this.getValue());
29075                 }
29076             }
29077         });
29078
29079         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
29080     },
29081     
29082     setValue : function(v, format)
29083     {
29084         this.inputEl.dom.value = v;
29085         
29086         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
29087         
29088         var d = Date.parseDate(v, f);
29089         
29090         if(!d){
29091             this.validate();
29092             return;
29093         }
29094         
29095         this.setDay(d.format(this.dayFormat));
29096         this.setMonth(d.format(this.monthFormat));
29097         this.setYear(d.format(this.yearFormat));
29098         
29099         this.validate();
29100         
29101         return;
29102     },
29103     
29104     setDay : function(v)
29105     {
29106         this.dayField.setValue(v);
29107         this.inputEl.dom.value = this.getValue();
29108         this.validate();
29109         return;
29110     },
29111     
29112     setMonth : function(v)
29113     {
29114         this.monthField.setValue(v, true);
29115         this.inputEl.dom.value = this.getValue();
29116         this.validate();
29117         return;
29118     },
29119     
29120     setYear : function(v)
29121     {
29122         this.yearField.setValue(v);
29123         this.inputEl.dom.value = this.getValue();
29124         this.validate();
29125         return;
29126     },
29127     
29128     getDay : function()
29129     {
29130         return this.dayField.getValue();
29131     },
29132     
29133     getMonth : function()
29134     {
29135         return this.monthField.getValue();
29136     },
29137     
29138     getYear : function()
29139     {
29140         return this.yearField.getValue();
29141     },
29142     
29143     getValue : function()
29144     {
29145         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
29146         
29147         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
29148         
29149         return date;
29150     },
29151     
29152     reset : function()
29153     {
29154         this.setDay('');
29155         this.setMonth('');
29156         this.setYear('');
29157         this.inputEl.dom.value = '';
29158         this.validate();
29159         return;
29160     },
29161     
29162     validate : function()
29163     {
29164         var d = this.dayField.validate();
29165         var m = this.monthField.validate();
29166         var y = this.yearField.validate();
29167         
29168         var valid = true;
29169         
29170         if(
29171                 (!this.dayAllowBlank && !d) ||
29172                 (!this.monthAllowBlank && !m) ||
29173                 (!this.yearAllowBlank && !y)
29174         ){
29175             valid = false;
29176         }
29177         
29178         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
29179             return valid;
29180         }
29181         
29182         if(valid){
29183             this.markValid();
29184             return valid;
29185         }
29186         
29187         this.markInvalid();
29188         
29189         return valid;
29190     },
29191     
29192     markValid : function()
29193     {
29194         
29195         var label = this.el.select('label', true).first();
29196         var icon = this.el.select('i.fa-star', true).first();
29197
29198         if(label && icon){
29199             icon.remove();
29200         }
29201         
29202         this.fireEvent('valid', this);
29203     },
29204     
29205      /**
29206      * Mark this field as invalid
29207      * @param {String} msg The validation message
29208      */
29209     markInvalid : function(msg)
29210     {
29211         
29212         var label = this.el.select('label', true).first();
29213         var icon = this.el.select('i.fa-star', true).first();
29214
29215         if(label && !icon){
29216             this.el.select('.roo-date-split-field-label', true).createChild({
29217                 tag : 'i',
29218                 cls : 'text-danger fa fa-lg fa-star',
29219                 tooltip : 'This field is required',
29220                 style : 'margin-right:5px;'
29221             }, label, true);
29222         }
29223         
29224         this.fireEvent('invalid', this, msg);
29225     },
29226     
29227     clearInvalid : function()
29228     {
29229         var label = this.el.select('label', true).first();
29230         var icon = this.el.select('i.fa-star', true).first();
29231
29232         if(label && icon){
29233             icon.remove();
29234         }
29235         
29236         this.fireEvent('valid', this);
29237     },
29238     
29239     getName: function()
29240     {
29241         return this.name;
29242     }
29243     
29244 });
29245
29246  /**
29247  *
29248  * This is based on 
29249  * http://masonry.desandro.com
29250  *
29251  * The idea is to render all the bricks based on vertical width...
29252  *
29253  * The original code extends 'outlayer' - we might need to use that....
29254  * 
29255  */
29256
29257
29258 /**
29259  * @class Roo.bootstrap.LayoutMasonry
29260  * @extends Roo.bootstrap.Component
29261  * Bootstrap Layout Masonry class
29262  * 
29263  * @constructor
29264  * Create a new Element
29265  * @param {Object} config The config object
29266  */
29267
29268 Roo.bootstrap.LayoutMasonry = function(config){
29269     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
29270     
29271     this.bricks = [];
29272     
29273 };
29274
29275 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
29276     
29277     /**
29278      * @cfg {Boolean} isLayoutInstant = no animation?
29279      */   
29280     isLayoutInstant : false, // needed?
29281    
29282     /**
29283      * @cfg {Number} boxWidth  width of the columns
29284      */   
29285     boxWidth : 450,
29286     
29287       /**
29288      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
29289      */   
29290     boxHeight : 0,
29291     
29292     /**
29293      * @cfg {Number} padWidth padding below box..
29294      */   
29295     padWidth : 10, 
29296     
29297     /**
29298      * @cfg {Number} gutter gutter width..
29299      */   
29300     gutter : 10,
29301     
29302      /**
29303      * @cfg {Number} maxCols maximum number of columns
29304      */   
29305     
29306     maxCols: 0,
29307     
29308     /**
29309      * @cfg {Boolean} isAutoInitial defalut true
29310      */   
29311     isAutoInitial : true, 
29312     
29313     containerWidth: 0,
29314     
29315     /**
29316      * @cfg {Boolean} isHorizontal defalut false
29317      */   
29318     isHorizontal : false, 
29319
29320     currentSize : null,
29321     
29322     tag: 'div',
29323     
29324     cls: '',
29325     
29326     bricks: null, //CompositeElement
29327     
29328     cols : 1,
29329     
29330     _isLayoutInited : false,
29331     
29332 //    isAlternative : false, // only use for vertical layout...
29333     
29334     /**
29335      * @cfg {Number} alternativePadWidth padding below box..
29336      */   
29337     alternativePadWidth : 50, 
29338     
29339     getAutoCreate : function(){
29340         
29341         var cfg = {
29342             tag: this.tag,
29343             cls: 'blog-masonary-wrapper ' + this.cls,
29344             cn : {
29345                 cls : 'mas-boxes masonary'
29346             }
29347         };
29348         
29349         return cfg;
29350     },
29351     
29352     getChildContainer: function( )
29353     {
29354         if (this.boxesEl) {
29355             return this.boxesEl;
29356         }
29357         
29358         this.boxesEl = this.el.select('.mas-boxes').first();
29359         
29360         return this.boxesEl;
29361     },
29362     
29363     
29364     initEvents : function()
29365     {
29366         var _this = this;
29367         
29368         if(this.isAutoInitial){
29369             Roo.log('hook children rendered');
29370             this.on('childrenrendered', function() {
29371                 Roo.log('children rendered');
29372                 _this.initial();
29373             } ,this);
29374         }
29375     },
29376     
29377     initial : function()
29378     {
29379         this.currentSize = this.el.getBox(true);
29380         
29381         Roo.EventManager.onWindowResize(this.resize, this); 
29382
29383         if(!this.isAutoInitial){
29384             this.layout();
29385             return;
29386         }
29387         
29388         this.layout();
29389         
29390         return;
29391         //this.layout.defer(500,this);
29392         
29393     },
29394     
29395     resize : function()
29396     {
29397         Roo.log('resize');
29398         
29399         var cs = this.el.getBox(true);
29400         
29401         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29402             Roo.log("no change in with or X");
29403             return;
29404         }
29405         
29406         this.currentSize = cs;
29407         
29408         this.layout();
29409         
29410     },
29411     
29412     layout : function()
29413     {   
29414         this._resetLayout();
29415         
29416         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29417         
29418         this.layoutItems( isInstant );
29419       
29420         this._isLayoutInited = true;
29421         
29422     },
29423     
29424     _resetLayout : function()
29425     {
29426         if(this.isHorizontal){
29427             this.horizontalMeasureColumns();
29428             return;
29429         }
29430         
29431         this.verticalMeasureColumns();
29432         
29433     },
29434     
29435     verticalMeasureColumns : function()
29436     {
29437         this.getContainerWidth();
29438         
29439 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29440 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
29441 //            return;
29442 //        }
29443         
29444         var boxWidth = this.boxWidth + this.padWidth;
29445         
29446         if(this.containerWidth < this.boxWidth){
29447             boxWidth = this.containerWidth
29448         }
29449         
29450         var containerWidth = this.containerWidth;
29451         
29452         var cols = Math.floor(containerWidth / boxWidth);
29453         
29454         this.cols = Math.max( cols, 1 );
29455         
29456         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
29457         
29458         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
29459         
29460         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
29461         
29462         this.colWidth = boxWidth + avail - this.padWidth;
29463         
29464         this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
29465         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
29466     },
29467     
29468     horizontalMeasureColumns : function()
29469     {
29470         this.getContainerWidth();
29471         
29472         var boxWidth = this.boxWidth;
29473         
29474         if(this.containerWidth < boxWidth){
29475             boxWidth = this.containerWidth;
29476         }
29477         
29478         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
29479         
29480         this.el.setHeight(boxWidth);
29481         
29482     },
29483     
29484     getContainerWidth : function()
29485     {
29486         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
29487     },
29488     
29489     layoutItems : function( isInstant )
29490     {
29491         var items = Roo.apply([], this.bricks);
29492         
29493         if(this.isHorizontal){
29494             this._horizontalLayoutItems( items , isInstant );
29495             return;
29496         }
29497         
29498 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29499 //            this._verticalAlternativeLayoutItems( items , isInstant );
29500 //            return;
29501 //        }
29502         
29503         this._verticalLayoutItems( items , isInstant );
29504         
29505     },
29506     
29507     _verticalLayoutItems : function ( items , isInstant)
29508     {
29509         if ( !items || !items.length ) {
29510             return;
29511         }
29512         
29513         var standard = [
29514             ['xs', 'xs', 'xs', 'tall'],
29515             ['xs', 'xs', 'tall'],
29516             ['xs', 'xs', 'sm'],
29517             ['xs', 'xs', 'xs'],
29518             ['xs', 'tall'],
29519             ['xs', 'sm'],
29520             ['xs', 'xs'],
29521             ['xs'],
29522             
29523             ['sm', 'xs', 'xs'],
29524             ['sm', 'xs'],
29525             ['sm'],
29526             
29527             ['tall', 'xs', 'xs', 'xs'],
29528             ['tall', 'xs', 'xs'],
29529             ['tall', 'xs'],
29530             ['tall']
29531             
29532         ];
29533         
29534         var queue = [];
29535         
29536         var boxes = [];
29537         
29538         var box = [];
29539         
29540         Roo.each(items, function(item, k){
29541             
29542             switch (item.size) {
29543                 // these layouts take up a full box,
29544                 case 'md' :
29545                 case 'md-left' :
29546                 case 'md-right' :
29547                 case 'wide' :
29548                     
29549                     if(box.length){
29550                         boxes.push(box);
29551                         box = [];
29552                     }
29553                     
29554                     boxes.push([item]);
29555                     
29556                     break;
29557                     
29558                 case 'xs' :
29559                 case 'sm' :
29560                 case 'tall' :
29561                     
29562                     box.push(item);
29563                     
29564                     break;
29565                 default :
29566                     break;
29567                     
29568             }
29569             
29570         }, this);
29571         
29572         if(box.length){
29573             boxes.push(box);
29574             box = [];
29575         }
29576         
29577         var filterPattern = function(box, length)
29578         {
29579             if(!box.length){
29580                 return;
29581             }
29582             
29583             var match = false;
29584             
29585             var pattern = box.slice(0, length);
29586             
29587             var format = [];
29588             
29589             Roo.each(pattern, function(i){
29590                 format.push(i.size);
29591             }, this);
29592             
29593             Roo.each(standard, function(s){
29594                 
29595                 if(String(s) != String(format)){
29596                     return;
29597                 }
29598                 
29599                 match = true;
29600                 return false;
29601                 
29602             }, this);
29603             
29604             if(!match && length == 1){
29605                 return;
29606             }
29607             
29608             if(!match){
29609                 filterPattern(box, length - 1);
29610                 return;
29611             }
29612                 
29613             queue.push(pattern);
29614
29615             box = box.slice(length, box.length);
29616
29617             filterPattern(box, 4);
29618
29619             return;
29620             
29621         }
29622         
29623         Roo.each(boxes, function(box, k){
29624             
29625             if(!box.length){
29626                 return;
29627             }
29628             
29629             if(box.length == 1){
29630                 queue.push(box);
29631                 return;
29632             }
29633             
29634             filterPattern(box, 4);
29635             
29636         }, this);
29637         
29638         this._processVerticalLayoutQueue( queue, isInstant );
29639         
29640     },
29641     
29642 //    _verticalAlternativeLayoutItems : function( items , isInstant )
29643 //    {
29644 //        if ( !items || !items.length ) {
29645 //            return;
29646 //        }
29647 //
29648 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
29649 //        
29650 //    },
29651     
29652     _horizontalLayoutItems : function ( items , isInstant)
29653     {
29654         if ( !items || !items.length || items.length < 3) {
29655             return;
29656         }
29657         
29658         items.reverse();
29659         
29660         var eItems = items.slice(0, 3);
29661         
29662         items = items.slice(3, items.length);
29663         
29664         var standard = [
29665             ['xs', 'xs', 'xs', 'wide'],
29666             ['xs', 'xs', 'wide'],
29667             ['xs', 'xs', 'sm'],
29668             ['xs', 'xs', 'xs'],
29669             ['xs', 'wide'],
29670             ['xs', 'sm'],
29671             ['xs', 'xs'],
29672             ['xs'],
29673             
29674             ['sm', 'xs', 'xs'],
29675             ['sm', 'xs'],
29676             ['sm'],
29677             
29678             ['wide', 'xs', 'xs', 'xs'],
29679             ['wide', 'xs', 'xs'],
29680             ['wide', 'xs'],
29681             ['wide'],
29682             
29683             ['wide-thin']
29684         ];
29685         
29686         var queue = [];
29687         
29688         var boxes = [];
29689         
29690         var box = [];
29691         
29692         Roo.each(items, function(item, k){
29693             
29694             switch (item.size) {
29695                 case 'md' :
29696                 case 'md-left' :
29697                 case 'md-right' :
29698                 case 'tall' :
29699                     
29700                     if(box.length){
29701                         boxes.push(box);
29702                         box = [];
29703                     }
29704                     
29705                     boxes.push([item]);
29706                     
29707                     break;
29708                     
29709                 case 'xs' :
29710                 case 'sm' :
29711                 case 'wide' :
29712                 case 'wide-thin' :
29713                     
29714                     box.push(item);
29715                     
29716                     break;
29717                 default :
29718                     break;
29719                     
29720             }
29721             
29722         }, this);
29723         
29724         if(box.length){
29725             boxes.push(box);
29726             box = [];
29727         }
29728         
29729         var filterPattern = function(box, length)
29730         {
29731             if(!box.length){
29732                 return;
29733             }
29734             
29735             var match = false;
29736             
29737             var pattern = box.slice(0, length);
29738             
29739             var format = [];
29740             
29741             Roo.each(pattern, function(i){
29742                 format.push(i.size);
29743             }, this);
29744             
29745             Roo.each(standard, function(s){
29746                 
29747                 if(String(s) != String(format)){
29748                     return;
29749                 }
29750                 
29751                 match = true;
29752                 return false;
29753                 
29754             }, this);
29755             
29756             if(!match && length == 1){
29757                 return;
29758             }
29759             
29760             if(!match){
29761                 filterPattern(box, length - 1);
29762                 return;
29763             }
29764                 
29765             queue.push(pattern);
29766
29767             box = box.slice(length, box.length);
29768
29769             filterPattern(box, 4);
29770
29771             return;
29772             
29773         }
29774         
29775         Roo.each(boxes, function(box, k){
29776             
29777             if(!box.length){
29778                 return;
29779             }
29780             
29781             if(box.length == 1){
29782                 queue.push(box);
29783                 return;
29784             }
29785             
29786             filterPattern(box, 4);
29787             
29788         }, this);
29789         
29790         
29791         var prune = [];
29792         
29793         var pos = this.el.getBox(true);
29794         
29795         var minX = pos.x;
29796         
29797         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29798         
29799         var hit_end = false;
29800         
29801         Roo.each(queue, function(box){
29802             
29803             if(hit_end){
29804                 
29805                 Roo.each(box, function(b){
29806                 
29807                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29808                     b.el.hide();
29809
29810                 }, this);
29811
29812                 return;
29813             }
29814             
29815             var mx = 0;
29816             
29817             Roo.each(box, function(b){
29818                 
29819                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29820                 b.el.show();
29821
29822                 mx = Math.max(mx, b.x);
29823                 
29824             }, this);
29825             
29826             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29827             
29828             if(maxX < minX){
29829                 
29830                 Roo.each(box, function(b){
29831                 
29832                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29833                     b.el.hide();
29834                     
29835                 }, this);
29836                 
29837                 hit_end = true;
29838                 
29839                 return;
29840             }
29841             
29842             prune.push(box);
29843             
29844         }, this);
29845         
29846         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29847     },
29848     
29849     /** Sets position of item in DOM
29850     * @param {Element} item
29851     * @param {Number} x - horizontal position
29852     * @param {Number} y - vertical position
29853     * @param {Boolean} isInstant - disables transitions
29854     */
29855     _processVerticalLayoutQueue : function( queue, isInstant )
29856     {
29857         var pos = this.el.getBox(true);
29858         var x = pos.x;
29859         var y = pos.y;
29860         var maxY = [];
29861         
29862         for (var i = 0; i < this.cols; i++){
29863             maxY[i] = pos.y;
29864         }
29865         
29866         Roo.each(queue, function(box, k){
29867             
29868             var col = k % this.cols;
29869             
29870             Roo.each(box, function(b,kk){
29871                 
29872                 b.el.position('absolute');
29873                 
29874                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29875                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29876                 
29877                 if(b.size == 'md-left' || b.size == 'md-right'){
29878                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29879                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29880                 }
29881                 
29882                 b.el.setWidth(width);
29883                 b.el.setHeight(height);
29884                 // iframe?
29885                 b.el.select('iframe',true).setSize(width,height);
29886                 
29887             }, this);
29888             
29889             for (var i = 0; i < this.cols; i++){
29890                 
29891                 if(maxY[i] < maxY[col]){
29892                     col = i;
29893                     continue;
29894                 }
29895                 
29896                 col = Math.min(col, i);
29897                 
29898             }
29899             
29900             x = pos.x + col * (this.colWidth + this.padWidth);
29901             
29902             y = maxY[col];
29903             
29904             var positions = [];
29905             
29906             switch (box.length){
29907                 case 1 :
29908                     positions = this.getVerticalOneBoxColPositions(x, y, box);
29909                     break;
29910                 case 2 :
29911                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
29912                     break;
29913                 case 3 :
29914                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
29915                     break;
29916                 case 4 :
29917                     positions = this.getVerticalFourBoxColPositions(x, y, box);
29918                     break;
29919                 default :
29920                     break;
29921             }
29922             
29923             Roo.each(box, function(b,kk){
29924                 
29925                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29926                 
29927                 var sz = b.el.getSize();
29928                 
29929                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29930                 
29931             }, this);
29932             
29933         }, this);
29934         
29935         var mY = 0;
29936         
29937         for (var i = 0; i < this.cols; i++){
29938             mY = Math.max(mY, maxY[i]);
29939         }
29940         
29941         this.el.setHeight(mY - pos.y);
29942         
29943     },
29944     
29945 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29946 //    {
29947 //        var pos = this.el.getBox(true);
29948 //        var x = pos.x;
29949 //        var y = pos.y;
29950 //        var maxX = pos.right;
29951 //        
29952 //        var maxHeight = 0;
29953 //        
29954 //        Roo.each(items, function(item, k){
29955 //            
29956 //            var c = k % 2;
29957 //            
29958 //            item.el.position('absolute');
29959 //                
29960 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29961 //
29962 //            item.el.setWidth(width);
29963 //
29964 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
29965 //
29966 //            item.el.setHeight(height);
29967 //            
29968 //            if(c == 0){
29969 //                item.el.setXY([x, y], isInstant ? false : true);
29970 //            } else {
29971 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
29972 //            }
29973 //            
29974 //            y = y + height + this.alternativePadWidth;
29975 //            
29976 //            maxHeight = maxHeight + height + this.alternativePadWidth;
29977 //            
29978 //        }, this);
29979 //        
29980 //        this.el.setHeight(maxHeight);
29981 //        
29982 //    },
29983     
29984     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
29985     {
29986         var pos = this.el.getBox(true);
29987         
29988         var minX = pos.x;
29989         var minY = pos.y;
29990         
29991         var maxX = pos.right;
29992         
29993         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
29994         
29995         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29996         
29997         Roo.each(queue, function(box, k){
29998             
29999             Roo.each(box, function(b, kk){
30000                 
30001                 b.el.position('absolute');
30002                 
30003                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30004                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30005                 
30006                 if(b.size == 'md-left' || b.size == 'md-right'){
30007                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30008                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30009                 }
30010                 
30011                 b.el.setWidth(width);
30012                 b.el.setHeight(height);
30013                 
30014             }, this);
30015             
30016             if(!box.length){
30017                 return;
30018             }
30019             
30020             var positions = [];
30021             
30022             switch (box.length){
30023                 case 1 :
30024                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
30025                     break;
30026                 case 2 :
30027                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
30028                     break;
30029                 case 3 :
30030                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
30031                     break;
30032                 case 4 :
30033                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
30034                     break;
30035                 default :
30036                     break;
30037             }
30038             
30039             Roo.each(box, function(b,kk){
30040                 
30041                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30042                 
30043                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
30044                 
30045             }, this);
30046             
30047         }, this);
30048         
30049     },
30050     
30051     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
30052     {
30053         Roo.each(eItems, function(b,k){
30054             
30055             b.size = (k == 0) ? 'sm' : 'xs';
30056             b.x = (k == 0) ? 2 : 1;
30057             b.y = (k == 0) ? 2 : 1;
30058             
30059             b.el.position('absolute');
30060             
30061             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30062                 
30063             b.el.setWidth(width);
30064             
30065             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30066             
30067             b.el.setHeight(height);
30068             
30069         }, this);
30070
30071         var positions = [];
30072         
30073         positions.push({
30074             x : maxX - this.unitWidth * 2 - this.gutter,
30075             y : minY
30076         });
30077         
30078         positions.push({
30079             x : maxX - this.unitWidth,
30080             y : minY + (this.unitWidth + this.gutter) * 2
30081         });
30082         
30083         positions.push({
30084             x : maxX - this.unitWidth * 3 - this.gutter * 2,
30085             y : minY
30086         });
30087         
30088         Roo.each(eItems, function(b,k){
30089             
30090             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
30091
30092         }, this);
30093         
30094     },
30095     
30096     getVerticalOneBoxColPositions : function(x, y, box)
30097     {
30098         var pos = [];
30099         
30100         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
30101         
30102         if(box[0].size == 'md-left'){
30103             rand = 0;
30104         }
30105         
30106         if(box[0].size == 'md-right'){
30107             rand = 1;
30108         }
30109         
30110         pos.push({
30111             x : x + (this.unitWidth + this.gutter) * rand,
30112             y : y
30113         });
30114         
30115         return pos;
30116     },
30117     
30118     getVerticalTwoBoxColPositions : function(x, y, box)
30119     {
30120         var pos = [];
30121         
30122         if(box[0].size == 'xs'){
30123             
30124             pos.push({
30125                 x : x,
30126                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
30127             });
30128
30129             pos.push({
30130                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
30131                 y : y
30132             });
30133             
30134             return pos;
30135             
30136         }
30137         
30138         pos.push({
30139             x : x,
30140             y : y
30141         });
30142
30143         pos.push({
30144             x : x + (this.unitWidth + this.gutter) * 2,
30145             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
30146         });
30147         
30148         return pos;
30149         
30150     },
30151     
30152     getVerticalThreeBoxColPositions : function(x, y, box)
30153     {
30154         var pos = [];
30155         
30156         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30157             
30158             pos.push({
30159                 x : x,
30160                 y : y
30161             });
30162
30163             pos.push({
30164                 x : x + (this.unitWidth + this.gutter) * 1,
30165                 y : y
30166             });
30167             
30168             pos.push({
30169                 x : x + (this.unitWidth + this.gutter) * 2,
30170                 y : y
30171             });
30172             
30173             return pos;
30174             
30175         }
30176         
30177         if(box[0].size == 'xs' && box[1].size == 'xs'){
30178             
30179             pos.push({
30180                 x : x,
30181                 y : y
30182             });
30183
30184             pos.push({
30185                 x : x,
30186                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
30187             });
30188             
30189             pos.push({
30190                 x : x + (this.unitWidth + this.gutter) * 1,
30191                 y : y
30192             });
30193             
30194             return pos;
30195             
30196         }
30197         
30198         pos.push({
30199             x : x,
30200             y : y
30201         });
30202
30203         pos.push({
30204             x : x + (this.unitWidth + this.gutter) * 2,
30205             y : y
30206         });
30207
30208         pos.push({
30209             x : x + (this.unitWidth + this.gutter) * 2,
30210             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
30211         });
30212             
30213         return pos;
30214         
30215     },
30216     
30217     getVerticalFourBoxColPositions : function(x, y, box)
30218     {
30219         var pos = [];
30220         
30221         if(box[0].size == 'xs'){
30222             
30223             pos.push({
30224                 x : x,
30225                 y : y
30226             });
30227
30228             pos.push({
30229                 x : x,
30230                 y : y + (this.unitHeight + this.gutter) * 1
30231             });
30232             
30233             pos.push({
30234                 x : x,
30235                 y : y + (this.unitHeight + this.gutter) * 2
30236             });
30237             
30238             pos.push({
30239                 x : x + (this.unitWidth + this.gutter) * 1,
30240                 y : y
30241             });
30242             
30243             return pos;
30244             
30245         }
30246         
30247         pos.push({
30248             x : x,
30249             y : y
30250         });
30251
30252         pos.push({
30253             x : x + (this.unitWidth + this.gutter) * 2,
30254             y : y
30255         });
30256
30257         pos.push({
30258             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
30259             y : y + (this.unitHeight + this.gutter) * 1
30260         });
30261
30262         pos.push({
30263             x : x + (this.unitWidth + this.gutter) * 2,
30264             y : y + (this.unitWidth + this.gutter) * 2
30265         });
30266
30267         return pos;
30268         
30269     },
30270     
30271     getHorizontalOneBoxColPositions : function(maxX, minY, box)
30272     {
30273         var pos = [];
30274         
30275         if(box[0].size == 'md-left'){
30276             pos.push({
30277                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30278                 y : minY
30279             });
30280             
30281             return pos;
30282         }
30283         
30284         if(box[0].size == 'md-right'){
30285             pos.push({
30286                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30287                 y : minY + (this.unitWidth + this.gutter) * 1
30288             });
30289             
30290             return pos;
30291         }
30292         
30293         var rand = Math.floor(Math.random() * (4 - box[0].y));
30294         
30295         pos.push({
30296             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30297             y : minY + (this.unitWidth + this.gutter) * rand
30298         });
30299         
30300         return pos;
30301         
30302     },
30303     
30304     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
30305     {
30306         var pos = [];
30307         
30308         if(box[0].size == 'xs'){
30309             
30310             pos.push({
30311                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30312                 y : minY
30313             });
30314
30315             pos.push({
30316                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30317                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
30318             });
30319             
30320             return pos;
30321             
30322         }
30323         
30324         pos.push({
30325             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30326             y : minY
30327         });
30328
30329         pos.push({
30330             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30331             y : minY + (this.unitWidth + this.gutter) * 2
30332         });
30333         
30334         return pos;
30335         
30336     },
30337     
30338     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
30339     {
30340         var pos = [];
30341         
30342         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30343             
30344             pos.push({
30345                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30346                 y : minY
30347             });
30348
30349             pos.push({
30350                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30351                 y : minY + (this.unitWidth + this.gutter) * 1
30352             });
30353             
30354             pos.push({
30355                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30356                 y : minY + (this.unitWidth + this.gutter) * 2
30357             });
30358             
30359             return pos;
30360             
30361         }
30362         
30363         if(box[0].size == 'xs' && box[1].size == 'xs'){
30364             
30365             pos.push({
30366                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30367                 y : minY
30368             });
30369
30370             pos.push({
30371                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30372                 y : minY
30373             });
30374             
30375             pos.push({
30376                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30377                 y : minY + (this.unitWidth + this.gutter) * 1
30378             });
30379             
30380             return pos;
30381             
30382         }
30383         
30384         pos.push({
30385             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30386             y : minY
30387         });
30388
30389         pos.push({
30390             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30391             y : minY + (this.unitWidth + this.gutter) * 2
30392         });
30393
30394         pos.push({
30395             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30396             y : minY + (this.unitWidth + this.gutter) * 2
30397         });
30398             
30399         return pos;
30400         
30401     },
30402     
30403     getHorizontalFourBoxColPositions : function(maxX, minY, box)
30404     {
30405         var pos = [];
30406         
30407         if(box[0].size == 'xs'){
30408             
30409             pos.push({
30410                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30411                 y : minY
30412             });
30413
30414             pos.push({
30415                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30416                 y : minY
30417             });
30418             
30419             pos.push({
30420                 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),
30421                 y : minY
30422             });
30423             
30424             pos.push({
30425                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
30426                 y : minY + (this.unitWidth + this.gutter) * 1
30427             });
30428             
30429             return pos;
30430             
30431         }
30432         
30433         pos.push({
30434             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30435             y : minY
30436         });
30437         
30438         pos.push({
30439             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30440             y : minY + (this.unitWidth + this.gutter) * 2
30441         });
30442         
30443         pos.push({
30444             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30445             y : minY + (this.unitWidth + this.gutter) * 2
30446         });
30447         
30448         pos.push({
30449             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),
30450             y : minY + (this.unitWidth + this.gutter) * 2
30451         });
30452
30453         return pos;
30454         
30455     }
30456     
30457 });
30458
30459  
30460
30461  /**
30462  *
30463  * This is based on 
30464  * http://masonry.desandro.com
30465  *
30466  * The idea is to render all the bricks based on vertical width...
30467  *
30468  * The original code extends 'outlayer' - we might need to use that....
30469  * 
30470  */
30471
30472
30473 /**
30474  * @class Roo.bootstrap.LayoutMasonryAuto
30475  * @extends Roo.bootstrap.Component
30476  * Bootstrap Layout Masonry class
30477  * 
30478  * @constructor
30479  * Create a new Element
30480  * @param {Object} config The config object
30481  */
30482
30483 Roo.bootstrap.LayoutMasonryAuto = function(config){
30484     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
30485 };
30486
30487 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
30488     
30489       /**
30490      * @cfg {Boolean} isFitWidth  - resize the width..
30491      */   
30492     isFitWidth : false,  // options..
30493     /**
30494      * @cfg {Boolean} isOriginLeft = left align?
30495      */   
30496     isOriginLeft : true,
30497     /**
30498      * @cfg {Boolean} isOriginTop = top align?
30499      */   
30500     isOriginTop : false,
30501     /**
30502      * @cfg {Boolean} isLayoutInstant = no animation?
30503      */   
30504     isLayoutInstant : false, // needed?
30505     /**
30506      * @cfg {Boolean} isResizingContainer = not sure if this is used..
30507      */   
30508     isResizingContainer : true,
30509     /**
30510      * @cfg {Number} columnWidth  width of the columns 
30511      */   
30512     
30513     columnWidth : 0,
30514     
30515     /**
30516      * @cfg {Number} maxCols maximum number of columns
30517      */   
30518     
30519     maxCols: 0,
30520     /**
30521      * @cfg {Number} padHeight padding below box..
30522      */   
30523     
30524     padHeight : 10, 
30525     
30526     /**
30527      * @cfg {Boolean} isAutoInitial defalut true
30528      */   
30529     
30530     isAutoInitial : true, 
30531     
30532     // private?
30533     gutter : 0,
30534     
30535     containerWidth: 0,
30536     initialColumnWidth : 0,
30537     currentSize : null,
30538     
30539     colYs : null, // array.
30540     maxY : 0,
30541     padWidth: 10,
30542     
30543     
30544     tag: 'div',
30545     cls: '',
30546     bricks: null, //CompositeElement
30547     cols : 0, // array?
30548     // element : null, // wrapped now this.el
30549     _isLayoutInited : null, 
30550     
30551     
30552     getAutoCreate : function(){
30553         
30554         var cfg = {
30555             tag: this.tag,
30556             cls: 'blog-masonary-wrapper ' + this.cls,
30557             cn : {
30558                 cls : 'mas-boxes masonary'
30559             }
30560         };
30561         
30562         return cfg;
30563     },
30564     
30565     getChildContainer: function( )
30566     {
30567         if (this.boxesEl) {
30568             return this.boxesEl;
30569         }
30570         
30571         this.boxesEl = this.el.select('.mas-boxes').first();
30572         
30573         return this.boxesEl;
30574     },
30575     
30576     
30577     initEvents : function()
30578     {
30579         var _this = this;
30580         
30581         if(this.isAutoInitial){
30582             Roo.log('hook children rendered');
30583             this.on('childrenrendered', function() {
30584                 Roo.log('children rendered');
30585                 _this.initial();
30586             } ,this);
30587         }
30588         
30589     },
30590     
30591     initial : function()
30592     {
30593         this.reloadItems();
30594
30595         this.currentSize = this.el.getBox(true);
30596
30597         /// was window resize... - let's see if this works..
30598         Roo.EventManager.onWindowResize(this.resize, this); 
30599
30600         if(!this.isAutoInitial){
30601             this.layout();
30602             return;
30603         }
30604         
30605         this.layout.defer(500,this);
30606     },
30607     
30608     reloadItems: function()
30609     {
30610         this.bricks = this.el.select('.masonry-brick', true);
30611         
30612         this.bricks.each(function(b) {
30613             //Roo.log(b.getSize());
30614             if (!b.attr('originalwidth')) {
30615                 b.attr('originalwidth',  b.getSize().width);
30616             }
30617             
30618         });
30619         
30620         Roo.log(this.bricks.elements.length);
30621     },
30622     
30623     resize : function()
30624     {
30625         Roo.log('resize');
30626         var cs = this.el.getBox(true);
30627         
30628         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
30629             Roo.log("no change in with or X");
30630             return;
30631         }
30632         this.currentSize = cs;
30633         this.layout();
30634     },
30635     
30636     layout : function()
30637     {
30638          Roo.log('layout');
30639         this._resetLayout();
30640         //this._manageStamps();
30641       
30642         // don't animate first layout
30643         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30644         this.layoutItems( isInstant );
30645       
30646         // flag for initalized
30647         this._isLayoutInited = true;
30648     },
30649     
30650     layoutItems : function( isInstant )
30651     {
30652         //var items = this._getItemsForLayout( this.items );
30653         // original code supports filtering layout items.. we just ignore it..
30654         
30655         this._layoutItems( this.bricks , isInstant );
30656       
30657         this._postLayout();
30658     },
30659     _layoutItems : function ( items , isInstant)
30660     {
30661        //this.fireEvent( 'layout', this, items );
30662     
30663
30664         if ( !items || !items.elements.length ) {
30665           // no items, emit event with empty array
30666             return;
30667         }
30668
30669         var queue = [];
30670         items.each(function(item) {
30671             Roo.log("layout item");
30672             Roo.log(item);
30673             // get x/y object from method
30674             var position = this._getItemLayoutPosition( item );
30675             // enqueue
30676             position.item = item;
30677             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
30678             queue.push( position );
30679         }, this);
30680       
30681         this._processLayoutQueue( queue );
30682     },
30683     /** Sets position of item in DOM
30684     * @param {Element} item
30685     * @param {Number} x - horizontal position
30686     * @param {Number} y - vertical position
30687     * @param {Boolean} isInstant - disables transitions
30688     */
30689     _processLayoutQueue : function( queue )
30690     {
30691         for ( var i=0, len = queue.length; i < len; i++ ) {
30692             var obj = queue[i];
30693             obj.item.position('absolute');
30694             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
30695         }
30696     },
30697       
30698     
30699     /**
30700     * Any logic you want to do after each layout,
30701     * i.e. size the container
30702     */
30703     _postLayout : function()
30704     {
30705         this.resizeContainer();
30706     },
30707     
30708     resizeContainer : function()
30709     {
30710         if ( !this.isResizingContainer ) {
30711             return;
30712         }
30713         var size = this._getContainerSize();
30714         if ( size ) {
30715             this.el.setSize(size.width,size.height);
30716             this.boxesEl.setSize(size.width,size.height);
30717         }
30718     },
30719     
30720     
30721     
30722     _resetLayout : function()
30723     {
30724         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
30725         this.colWidth = this.el.getWidth();
30726         //this.gutter = this.el.getWidth(); 
30727         
30728         this.measureColumns();
30729
30730         // reset column Y
30731         var i = this.cols;
30732         this.colYs = [];
30733         while (i--) {
30734             this.colYs.push( 0 );
30735         }
30736     
30737         this.maxY = 0;
30738     },
30739
30740     measureColumns : function()
30741     {
30742         this.getContainerWidth();
30743       // if columnWidth is 0, default to outerWidth of first item
30744         if ( !this.columnWidth ) {
30745             var firstItem = this.bricks.first();
30746             Roo.log(firstItem);
30747             this.columnWidth  = this.containerWidth;
30748             if (firstItem && firstItem.attr('originalwidth') ) {
30749                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
30750             }
30751             // columnWidth fall back to item of first element
30752             Roo.log("set column width?");
30753                         this.initialColumnWidth = this.columnWidth  ;
30754
30755             // if first elem has no width, default to size of container
30756             
30757         }
30758         
30759         
30760         if (this.initialColumnWidth) {
30761             this.columnWidth = this.initialColumnWidth;
30762         }
30763         
30764         
30765             
30766         // column width is fixed at the top - however if container width get's smaller we should
30767         // reduce it...
30768         
30769         // this bit calcs how man columns..
30770             
30771         var columnWidth = this.columnWidth += this.gutter;
30772       
30773         // calculate columns
30774         var containerWidth = this.containerWidth + this.gutter;
30775         
30776         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
30777         // fix rounding errors, typically with gutters
30778         var excess = columnWidth - containerWidth % columnWidth;
30779         
30780         
30781         // if overshoot is less than a pixel, round up, otherwise floor it
30782         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
30783         cols = Math[ mathMethod ]( cols );
30784         this.cols = Math.max( cols, 1 );
30785         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30786         
30787          // padding positioning..
30788         var totalColWidth = this.cols * this.columnWidth;
30789         var padavail = this.containerWidth - totalColWidth;
30790         // so for 2 columns - we need 3 'pads'
30791         
30792         var padNeeded = (1+this.cols) * this.padWidth;
30793         
30794         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
30795         
30796         this.columnWidth += padExtra
30797         //this.padWidth = Math.floor(padavail /  ( this.cols));
30798         
30799         // adjust colum width so that padding is fixed??
30800         
30801         // we have 3 columns ... total = width * 3
30802         // we have X left over... that should be used by 
30803         
30804         //if (this.expandC) {
30805             
30806         //}
30807         
30808         
30809         
30810     },
30811     
30812     getContainerWidth : function()
30813     {
30814        /* // container is parent if fit width
30815         var container = this.isFitWidth ? this.element.parentNode : this.element;
30816         // check that this.size and size are there
30817         // IE8 triggers resize on body size change, so they might not be
30818         
30819         var size = getSize( container );  //FIXME
30820         this.containerWidth = size && size.innerWidth; //FIXME
30821         */
30822          
30823         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30824         
30825     },
30826     
30827     _getItemLayoutPosition : function( item )  // what is item?
30828     {
30829         // we resize the item to our columnWidth..
30830       
30831         item.setWidth(this.columnWidth);
30832         item.autoBoxAdjust  = false;
30833         
30834         var sz = item.getSize();
30835  
30836         // how many columns does this brick span
30837         var remainder = this.containerWidth % this.columnWidth;
30838         
30839         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30840         // round if off by 1 pixel, otherwise use ceil
30841         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
30842         colSpan = Math.min( colSpan, this.cols );
30843         
30844         // normally this should be '1' as we dont' currently allow multi width columns..
30845         
30846         var colGroup = this._getColGroup( colSpan );
30847         // get the minimum Y value from the columns
30848         var minimumY = Math.min.apply( Math, colGroup );
30849         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30850         
30851         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
30852          
30853         // position the brick
30854         var position = {
30855             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30856             y: this.currentSize.y + minimumY + this.padHeight
30857         };
30858         
30859         Roo.log(position);
30860         // apply setHeight to necessary columns
30861         var setHeight = minimumY + sz.height + this.padHeight;
30862         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30863         
30864         var setSpan = this.cols + 1 - colGroup.length;
30865         for ( var i = 0; i < setSpan; i++ ) {
30866           this.colYs[ shortColIndex + i ] = setHeight ;
30867         }
30868       
30869         return position;
30870     },
30871     
30872     /**
30873      * @param {Number} colSpan - number of columns the element spans
30874      * @returns {Array} colGroup
30875      */
30876     _getColGroup : function( colSpan )
30877     {
30878         if ( colSpan < 2 ) {
30879           // if brick spans only one column, use all the column Ys
30880           return this.colYs;
30881         }
30882       
30883         var colGroup = [];
30884         // how many different places could this brick fit horizontally
30885         var groupCount = this.cols + 1 - colSpan;
30886         // for each group potential horizontal position
30887         for ( var i = 0; i < groupCount; i++ ) {
30888           // make an array of colY values for that one group
30889           var groupColYs = this.colYs.slice( i, i + colSpan );
30890           // and get the max value of the array
30891           colGroup[i] = Math.max.apply( Math, groupColYs );
30892         }
30893         return colGroup;
30894     },
30895     /*
30896     _manageStamp : function( stamp )
30897     {
30898         var stampSize =  stamp.getSize();
30899         var offset = stamp.getBox();
30900         // get the columns that this stamp affects
30901         var firstX = this.isOriginLeft ? offset.x : offset.right;
30902         var lastX = firstX + stampSize.width;
30903         var firstCol = Math.floor( firstX / this.columnWidth );
30904         firstCol = Math.max( 0, firstCol );
30905         
30906         var lastCol = Math.floor( lastX / this.columnWidth );
30907         // lastCol should not go over if multiple of columnWidth #425
30908         lastCol -= lastX % this.columnWidth ? 0 : 1;
30909         lastCol = Math.min( this.cols - 1, lastCol );
30910         
30911         // set colYs to bottom of the stamp
30912         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30913             stampSize.height;
30914             
30915         for ( var i = firstCol; i <= lastCol; i++ ) {
30916           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30917         }
30918     },
30919     */
30920     
30921     _getContainerSize : function()
30922     {
30923         this.maxY = Math.max.apply( Math, this.colYs );
30924         var size = {
30925             height: this.maxY
30926         };
30927       
30928         if ( this.isFitWidth ) {
30929             size.width = this._getContainerFitWidth();
30930         }
30931       
30932         return size;
30933     },
30934     
30935     _getContainerFitWidth : function()
30936     {
30937         var unusedCols = 0;
30938         // count unused columns
30939         var i = this.cols;
30940         while ( --i ) {
30941           if ( this.colYs[i] !== 0 ) {
30942             break;
30943           }
30944           unusedCols++;
30945         }
30946         // fit container to columns that have been used
30947         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30948     },
30949     
30950     needsResizeLayout : function()
30951     {
30952         var previousWidth = this.containerWidth;
30953         this.getContainerWidth();
30954         return previousWidth !== this.containerWidth;
30955     }
30956  
30957 });
30958
30959  
30960
30961  /*
30962  * - LGPL
30963  *
30964  * element
30965  * 
30966  */
30967
30968 /**
30969  * @class Roo.bootstrap.MasonryBrick
30970  * @extends Roo.bootstrap.Component
30971  * Bootstrap MasonryBrick class
30972  * 
30973  * @constructor
30974  * Create a new MasonryBrick
30975  * @param {Object} config The config object
30976  */
30977
30978 Roo.bootstrap.MasonryBrick = function(config){
30979     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
30980     
30981     this.addEvents({
30982         // raw events
30983         /**
30984          * @event click
30985          * When a MasonryBrick is clcik
30986          * @param {Roo.bootstrap.MasonryBrick} this
30987          * @param {Roo.EventObject} e
30988          */
30989         "click" : true
30990     });
30991 };
30992
30993 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
30994     
30995     /**
30996      * @cfg {String} title
30997      */   
30998     title : '',
30999     /**
31000      * @cfg {String} html
31001      */   
31002     html : '',
31003     /**
31004      * @cfg {String} bgimage
31005      */   
31006     bgimage : '',
31007     /**
31008      * @cfg {String} videourl
31009      */   
31010     videourl : '',
31011     /**
31012      * @cfg {String} cls
31013      */   
31014     cls : '',
31015     /**
31016      * @cfg {String} href
31017      */   
31018     href : '',
31019     /**
31020      * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
31021      */   
31022     size : 'xs',
31023     
31024     /**
31025      * @cfg {String} (center|bottom) placetitle
31026      */   
31027     placetitle : '',
31028     
31029     /**
31030      * @cfg {Boolean} isFitContainer defalut true
31031      */   
31032     isFitContainer : true, 
31033     
31034     /**
31035      * @cfg {Boolean} preventDefault defalut false
31036      */   
31037     preventDefault : false, 
31038     
31039     getAutoCreate : function()
31040     {
31041         if(!this.isFitContainer){
31042             return this.getSplitAutoCreate();
31043         }
31044         
31045         var cls = 'masonry-brick masonry-brick-full';
31046         
31047         if(this.href.length){
31048             cls += ' masonry-brick-link';
31049         }
31050         
31051         if(this.bgimage.length){
31052             cls += ' masonry-brick-image';
31053         }
31054         
31055         if(!this.html.length){
31056             cls += ' enable-mask';
31057         }
31058         
31059         if(this.size){
31060             cls += ' masonry-' + this.size + '-brick';
31061         }
31062         
31063         if(this.placetitle.length){
31064             
31065             switch (this.placetitle) {
31066                 case 'center' :
31067                     cls += ' masonry-center-title';
31068                     break;
31069                 case 'bottom' :
31070                     cls += ' masonry-bottom-title';
31071                     break;
31072                 default:
31073                     break;
31074             }
31075             
31076         } else {
31077             if(!this.html.length && !this.bgimage.length){
31078                 cls += ' masonry-center-title';
31079             }
31080
31081             if(!this.html.length && this.bgimage.length){
31082                 cls += ' masonry-bottom-title';
31083             }
31084         }
31085         
31086         if(this.cls){
31087             cls += ' ' + this.cls;
31088         }
31089         
31090         var cfg = {
31091             tag: (this.href.length) ? 'a' : 'div',
31092             cls: cls,
31093             cn: [
31094                 {
31095                     tag: 'div',
31096                     cls: 'masonry-brick-paragraph',
31097                     cn: []
31098                 }
31099             ]
31100         };
31101         
31102         if(this.href.length){
31103             cfg.href = this.href;
31104         }
31105         
31106         var cn = cfg.cn[0].cn;
31107         
31108         if(this.title.length){
31109             cn.push({
31110                 tag: 'h4',
31111                 cls: 'masonry-brick-title',
31112                 html: this.title
31113             });
31114         }
31115         
31116         if(this.html.length){
31117             cn.push({
31118                 tag: 'p',
31119                 cls: 'masonry-brick-text',
31120                 html: this.html
31121             });
31122         }  
31123         if (!this.title.length && !this.html.length) {
31124             cfg.cn[0].cls += ' hide';
31125         }
31126         
31127         if(this.bgimage.length){
31128             cfg.cn.push({
31129                 tag: 'img',
31130                 cls: 'masonry-brick-image-view',
31131                 src: this.bgimage
31132             });
31133         }
31134         
31135         if(this.videourl.length){
31136             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31137             // youtube support only?
31138             cfg.cn.push({
31139                 tag: 'iframe',
31140                 cls: 'masonry-brick-image-view',
31141                 src: vurl,
31142                 frameborder : 0,
31143                 allowfullscreen : true
31144             });
31145             
31146             
31147         }
31148         
31149         cfg.cn.push({
31150             tag: 'div',
31151             cls: 'masonry-brick-mask'
31152         });
31153         
31154         return cfg;
31155         
31156     },
31157     
31158     getSplitAutoCreate : function()
31159     {
31160         var cls = 'masonry-brick masonry-brick-split';
31161         
31162         if(this.href.length){
31163             cls += ' masonry-brick-link';
31164         }
31165         
31166         if(this.bgimage.length){
31167             cls += ' masonry-brick-image';
31168         }
31169         
31170         if(this.size){
31171             cls += ' masonry-' + this.size + '-brick';
31172         }
31173         
31174         switch (this.placetitle) {
31175             case 'center' :
31176                 cls += ' masonry-center-title';
31177                 break;
31178             case 'bottom' :
31179                 cls += ' masonry-bottom-title';
31180                 break;
31181             default:
31182                 if(!this.bgimage.length){
31183                     cls += ' masonry-center-title';
31184                 }
31185
31186                 if(this.bgimage.length){
31187                     cls += ' masonry-bottom-title';
31188                 }
31189                 break;
31190         }
31191         
31192         if(this.cls){
31193             cls += ' ' + this.cls;
31194         }
31195         
31196         var cfg = {
31197             tag: (this.href.length) ? 'a' : 'div',
31198             cls: cls,
31199             cn: [
31200                 {
31201                     tag: 'div',
31202                     cls: 'masonry-brick-split-head',
31203                     cn: [
31204                         {
31205                             tag: 'div',
31206                             cls: 'masonry-brick-paragraph',
31207                             cn: []
31208                         }
31209                     ]
31210                 },
31211                 {
31212                     tag: 'div',
31213                     cls: 'masonry-brick-split-body',
31214                     cn: []
31215                 }
31216             ]
31217         };
31218         
31219         if(this.href.length){
31220             cfg.href = this.href;
31221         }
31222         
31223         if(this.title.length){
31224             cfg.cn[0].cn[0].cn.push({
31225                 tag: 'h4',
31226                 cls: 'masonry-brick-title',
31227                 html: this.title
31228             });
31229         }
31230         
31231         if(this.html.length){
31232             cfg.cn[1].cn.push({
31233                 tag: 'p',
31234                 cls: 'masonry-brick-text',
31235                 html: this.html
31236             });
31237         }
31238
31239         if(this.bgimage.length){
31240             cfg.cn[0].cn.push({
31241                 tag: 'img',
31242                 cls: 'masonry-brick-image-view',
31243                 src: this.bgimage
31244             });
31245         }
31246         
31247         if(this.videourl.length){
31248             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31249             // youtube support only?
31250             cfg.cn[0].cn.cn.push({
31251                 tag: 'iframe',
31252                 cls: 'masonry-brick-image-view',
31253                 src: vurl,
31254                 frameborder : 0,
31255                 allowfullscreen : true
31256             });
31257         }
31258         
31259         return cfg;
31260     },
31261     
31262     initEvents: function() 
31263     {
31264         switch (this.size) {
31265             case 'xs' :
31266                 this.x = 1;
31267                 this.y = 1;
31268                 break;
31269             case 'sm' :
31270                 this.x = 2;
31271                 this.y = 2;
31272                 break;
31273             case 'md' :
31274             case 'md-left' :
31275             case 'md-right' :
31276                 this.x = 3;
31277                 this.y = 3;
31278                 break;
31279             case 'tall' :
31280                 this.x = 2;
31281                 this.y = 3;
31282                 break;
31283             case 'wide' :
31284                 this.x = 3;
31285                 this.y = 2;
31286                 break;
31287             case 'wide-thin' :
31288                 this.x = 3;
31289                 this.y = 1;
31290                 break;
31291                         
31292             default :
31293                 break;
31294         }
31295         
31296         if(Roo.isTouch){
31297             this.el.on('touchstart', this.onTouchStart, this);
31298             this.el.on('touchmove', this.onTouchMove, this);
31299             this.el.on('touchend', this.onTouchEnd, this);
31300             this.el.on('contextmenu', this.onContextMenu, this);
31301         } else {
31302             this.el.on('mouseenter'  ,this.enter, this);
31303             this.el.on('mouseleave', this.leave, this);
31304             this.el.on('click', this.onClick, this);
31305         }
31306         
31307         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
31308             this.parent().bricks.push(this);   
31309         }
31310         
31311     },
31312     
31313     onClick: function(e, el)
31314     {
31315         var time = this.endTimer - this.startTimer;
31316         
31317         if(Roo.isTouch){
31318             if(time > 1000){
31319                 e.preventDefault();
31320                 return;
31321             }
31322         }
31323         
31324         if(!this.preventDefault){
31325             return;
31326         }
31327         
31328         e.preventDefault();
31329         this.fireEvent('click', this);
31330     },
31331     
31332     enter: function(e, el)
31333     {
31334         e.preventDefault();
31335         
31336         if(!this.isFitContainer){
31337             return;
31338         }
31339         
31340         if(this.bgimage.length && this.html.length){
31341             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31342         }
31343     },
31344     
31345     leave: function(e, el)
31346     {
31347         e.preventDefault();
31348         
31349         if(!this.isFitContainer){
31350             return;
31351         }
31352         
31353         if(this.bgimage.length && this.html.length){
31354             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31355         }
31356     },
31357     
31358     onTouchStart: function(e, el)
31359     {
31360 //        e.preventDefault();
31361         
31362         this.touchmoved = false;
31363         
31364         if(!this.isFitContainer){
31365             return;
31366         }
31367         
31368         if(!this.bgimage.length || !this.html.length){
31369             return;
31370         }
31371         
31372         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31373         
31374         this.timer = new Date().getTime();
31375         
31376     },
31377     
31378     onTouchMove: function(e, el)
31379     {
31380         this.touchmoved = true;
31381     },
31382     
31383     onContextMenu : function(e,el)
31384     {
31385         e.preventDefault();
31386         e.stopPropagation();
31387         return false;
31388     },
31389     
31390     onTouchEnd: function(e, el)
31391     {
31392 //        e.preventDefault();
31393         
31394         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
31395         
31396             this.leave(e,el);
31397             
31398             return;
31399         }
31400         
31401         if(!this.bgimage.length || !this.html.length){
31402             
31403             if(this.href.length){
31404                 window.location.href = this.href;
31405             }
31406             
31407             return;
31408         }
31409         
31410         if(!this.isFitContainer){
31411             return;
31412         }
31413         
31414         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31415         
31416         window.location.href = this.href;
31417     }
31418     
31419 });
31420
31421  
31422
31423  /*
31424  * - LGPL
31425  *
31426  * element
31427  * 
31428  */
31429
31430 /**
31431  * @class Roo.bootstrap.Brick
31432  * @extends Roo.bootstrap.Component
31433  * Bootstrap Brick class
31434  * 
31435  * @constructor
31436  * Create a new Brick
31437  * @param {Object} config The config object
31438  */
31439
31440 Roo.bootstrap.Brick = function(config){
31441     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
31442     
31443     this.addEvents({
31444         // raw events
31445         /**
31446          * @event click
31447          * When a Brick is click
31448          * @param {Roo.bootstrap.Brick} this
31449          * @param {Roo.EventObject} e
31450          */
31451         "click" : true
31452     });
31453 };
31454
31455 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
31456     
31457     /**
31458      * @cfg {String} title
31459      */   
31460     title : '',
31461     /**
31462      * @cfg {String} html
31463      */   
31464     html : '',
31465     /**
31466      * @cfg {String} bgimage
31467      */   
31468     bgimage : '',
31469     /**
31470      * @cfg {String} cls
31471      */   
31472     cls : '',
31473     /**
31474      * @cfg {String} href
31475      */   
31476     href : '',
31477     /**
31478      * @cfg {String} video
31479      */   
31480     video : '',
31481     /**
31482      * @cfg {Boolean} square
31483      */   
31484     square : true,
31485     
31486     getAutoCreate : function()
31487     {
31488         var cls = 'roo-brick';
31489         
31490         if(this.href.length){
31491             cls += ' roo-brick-link';
31492         }
31493         
31494         if(this.bgimage.length){
31495             cls += ' roo-brick-image';
31496         }
31497         
31498         if(!this.html.length && !this.bgimage.length){
31499             cls += ' roo-brick-center-title';
31500         }
31501         
31502         if(!this.html.length && this.bgimage.length){
31503             cls += ' roo-brick-bottom-title';
31504         }
31505         
31506         if(this.cls){
31507             cls += ' ' + this.cls;
31508         }
31509         
31510         var cfg = {
31511             tag: (this.href.length) ? 'a' : 'div',
31512             cls: cls,
31513             cn: [
31514                 {
31515                     tag: 'div',
31516                     cls: 'roo-brick-paragraph',
31517                     cn: []
31518                 }
31519             ]
31520         };
31521         
31522         if(this.href.length){
31523             cfg.href = this.href;
31524         }
31525         
31526         var cn = cfg.cn[0].cn;
31527         
31528         if(this.title.length){
31529             cn.push({
31530                 tag: 'h4',
31531                 cls: 'roo-brick-title',
31532                 html: this.title
31533             });
31534         }
31535         
31536         if(this.html.length){
31537             cn.push({
31538                 tag: 'p',
31539                 cls: 'roo-brick-text',
31540                 html: this.html
31541             });
31542         } else {
31543             cn.cls += ' hide';
31544         }
31545         
31546         if(this.bgimage.length){
31547             cfg.cn.push({
31548                 tag: 'img',
31549                 cls: 'roo-brick-image-view',
31550                 src: this.bgimage
31551             });
31552         }
31553         
31554         return cfg;
31555     },
31556     
31557     initEvents: function() 
31558     {
31559         if(this.title.length || this.html.length){
31560             this.el.on('mouseenter'  ,this.enter, this);
31561             this.el.on('mouseleave', this.leave, this);
31562         }
31563         
31564         
31565         Roo.EventManager.onWindowResize(this.resize, this); 
31566         
31567         this.resize();
31568     },
31569     
31570     resize : function()
31571     {
31572         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
31573         
31574         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
31575         
31576         if(this.bgimage.length){
31577             var image = this.el.select('.roo-brick-image-view', true).first();
31578             image.setWidth(paragraph.getWidth());
31579             image.setHeight(paragraph.getWidth());
31580             
31581             this.el.setHeight(paragraph.getWidth());
31582             
31583         }
31584         
31585     },
31586     
31587     enter: function(e, el)
31588     {
31589         e.preventDefault();
31590         
31591         if(this.bgimage.length){
31592             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
31593             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
31594         }
31595     },
31596     
31597     leave: function(e, el)
31598     {
31599         e.preventDefault();
31600         
31601         if(this.bgimage.length){
31602             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
31603             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
31604         }
31605     }
31606     
31607 });
31608
31609  
31610
31611  /*
31612  * Based on:
31613  * Ext JS Library 1.1.1
31614  * Copyright(c) 2006-2007, Ext JS, LLC.
31615  *
31616  * Originally Released Under LGPL - original licence link has changed is not relivant.
31617  *
31618  * Fork - LGPL
31619  * <script type="text/javascript">
31620  */
31621
31622
31623 /**
31624  * @class Roo.bootstrap.SplitBar
31625  * @extends Roo.util.Observable
31626  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
31627  * <br><br>
31628  * Usage:
31629  * <pre><code>
31630 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
31631                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
31632 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
31633 split.minSize = 100;
31634 split.maxSize = 600;
31635 split.animate = true;
31636 split.on('moved', splitterMoved);
31637 </code></pre>
31638  * @constructor
31639  * Create a new SplitBar
31640  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
31641  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
31642  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31643  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
31644                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
31645                         position of the SplitBar).
31646  */
31647 Roo.bootstrap.SplitBar = function(cfg){
31648     
31649     /** @private */
31650     
31651     //{
31652     //  dragElement : elm
31653     //  resizingElement: el,
31654         // optional..
31655     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
31656     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
31657         // existingProxy ???
31658     //}
31659     
31660     this.el = Roo.get(cfg.dragElement, true);
31661     this.el.dom.unselectable = "on";
31662     /** @private */
31663     this.resizingEl = Roo.get(cfg.resizingElement, true);
31664
31665     /**
31666      * @private
31667      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31668      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
31669      * @type Number
31670      */
31671     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
31672     
31673     /**
31674      * The minimum size of the resizing element. (Defaults to 0)
31675      * @type Number
31676      */
31677     this.minSize = 0;
31678     
31679     /**
31680      * The maximum size of the resizing element. (Defaults to 2000)
31681      * @type Number
31682      */
31683     this.maxSize = 2000;
31684     
31685     /**
31686      * Whether to animate the transition to the new size
31687      * @type Boolean
31688      */
31689     this.animate = false;
31690     
31691     /**
31692      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
31693      * @type Boolean
31694      */
31695     this.useShim = false;
31696     
31697     /** @private */
31698     this.shim = null;
31699     
31700     if(!cfg.existingProxy){
31701         /** @private */
31702         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
31703     }else{
31704         this.proxy = Roo.get(cfg.existingProxy).dom;
31705     }
31706     /** @private */
31707     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
31708     
31709     /** @private */
31710     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
31711     
31712     /** @private */
31713     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
31714     
31715     /** @private */
31716     this.dragSpecs = {};
31717     
31718     /**
31719      * @private The adapter to use to positon and resize elements
31720      */
31721     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31722     this.adapter.init(this);
31723     
31724     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31725         /** @private */
31726         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
31727         this.el.addClass("roo-splitbar-h");
31728     }else{
31729         /** @private */
31730         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
31731         this.el.addClass("roo-splitbar-v");
31732     }
31733     
31734     this.addEvents({
31735         /**
31736          * @event resize
31737          * Fires when the splitter is moved (alias for {@link #event-moved})
31738          * @param {Roo.bootstrap.SplitBar} this
31739          * @param {Number} newSize the new width or height
31740          */
31741         "resize" : true,
31742         /**
31743          * @event moved
31744          * Fires when the splitter is moved
31745          * @param {Roo.bootstrap.SplitBar} this
31746          * @param {Number} newSize the new width or height
31747          */
31748         "moved" : true,
31749         /**
31750          * @event beforeresize
31751          * Fires before the splitter is dragged
31752          * @param {Roo.bootstrap.SplitBar} this
31753          */
31754         "beforeresize" : true,
31755
31756         "beforeapply" : true
31757     });
31758
31759     Roo.util.Observable.call(this);
31760 };
31761
31762 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
31763     onStartProxyDrag : function(x, y){
31764         this.fireEvent("beforeresize", this);
31765         if(!this.overlay){
31766             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
31767             o.unselectable();
31768             o.enableDisplayMode("block");
31769             // all splitbars share the same overlay
31770             Roo.bootstrap.SplitBar.prototype.overlay = o;
31771         }
31772         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
31773         this.overlay.show();
31774         Roo.get(this.proxy).setDisplayed("block");
31775         var size = this.adapter.getElementSize(this);
31776         this.activeMinSize = this.getMinimumSize();;
31777         this.activeMaxSize = this.getMaximumSize();;
31778         var c1 = size - this.activeMinSize;
31779         var c2 = Math.max(this.activeMaxSize - size, 0);
31780         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31781             this.dd.resetConstraints();
31782             this.dd.setXConstraint(
31783                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
31784                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
31785             );
31786             this.dd.setYConstraint(0, 0);
31787         }else{
31788             this.dd.resetConstraints();
31789             this.dd.setXConstraint(0, 0);
31790             this.dd.setYConstraint(
31791                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
31792                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
31793             );
31794          }
31795         this.dragSpecs.startSize = size;
31796         this.dragSpecs.startPoint = [x, y];
31797         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
31798     },
31799     
31800     /** 
31801      * @private Called after the drag operation by the DDProxy
31802      */
31803     onEndProxyDrag : function(e){
31804         Roo.get(this.proxy).setDisplayed(false);
31805         var endPoint = Roo.lib.Event.getXY(e);
31806         if(this.overlay){
31807             this.overlay.hide();
31808         }
31809         var newSize;
31810         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31811             newSize = this.dragSpecs.startSize + 
31812                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
31813                     endPoint[0] - this.dragSpecs.startPoint[0] :
31814                     this.dragSpecs.startPoint[0] - endPoint[0]
31815                 );
31816         }else{
31817             newSize = this.dragSpecs.startSize + 
31818                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
31819                     endPoint[1] - this.dragSpecs.startPoint[1] :
31820                     this.dragSpecs.startPoint[1] - endPoint[1]
31821                 );
31822         }
31823         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
31824         if(newSize != this.dragSpecs.startSize){
31825             if(this.fireEvent('beforeapply', this, newSize) !== false){
31826                 this.adapter.setElementSize(this, newSize);
31827                 this.fireEvent("moved", this, newSize);
31828                 this.fireEvent("resize", this, newSize);
31829             }
31830         }
31831     },
31832     
31833     /**
31834      * Get the adapter this SplitBar uses
31835      * @return The adapter object
31836      */
31837     getAdapter : function(){
31838         return this.adapter;
31839     },
31840     
31841     /**
31842      * Set the adapter this SplitBar uses
31843      * @param {Object} adapter A SplitBar adapter object
31844      */
31845     setAdapter : function(adapter){
31846         this.adapter = adapter;
31847         this.adapter.init(this);
31848     },
31849     
31850     /**
31851      * Gets the minimum size for the resizing element
31852      * @return {Number} The minimum size
31853      */
31854     getMinimumSize : function(){
31855         return this.minSize;
31856     },
31857     
31858     /**
31859      * Sets the minimum size for the resizing element
31860      * @param {Number} minSize The minimum size
31861      */
31862     setMinimumSize : function(minSize){
31863         this.minSize = minSize;
31864     },
31865     
31866     /**
31867      * Gets the maximum size for the resizing element
31868      * @return {Number} The maximum size
31869      */
31870     getMaximumSize : function(){
31871         return this.maxSize;
31872     },
31873     
31874     /**
31875      * Sets the maximum size for the resizing element
31876      * @param {Number} maxSize The maximum size
31877      */
31878     setMaximumSize : function(maxSize){
31879         this.maxSize = maxSize;
31880     },
31881     
31882     /**
31883      * Sets the initialize size for the resizing element
31884      * @param {Number} size The initial size
31885      */
31886     setCurrentSize : function(size){
31887         var oldAnimate = this.animate;
31888         this.animate = false;
31889         this.adapter.setElementSize(this, size);
31890         this.animate = oldAnimate;
31891     },
31892     
31893     /**
31894      * Destroy this splitbar. 
31895      * @param {Boolean} removeEl True to remove the element
31896      */
31897     destroy : function(removeEl){
31898         if(this.shim){
31899             this.shim.remove();
31900         }
31901         this.dd.unreg();
31902         this.proxy.parentNode.removeChild(this.proxy);
31903         if(removeEl){
31904             this.el.remove();
31905         }
31906     }
31907 });
31908
31909 /**
31910  * @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.
31911  */
31912 Roo.bootstrap.SplitBar.createProxy = function(dir){
31913     var proxy = new Roo.Element(document.createElement("div"));
31914     proxy.unselectable();
31915     var cls = 'roo-splitbar-proxy';
31916     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
31917     document.body.appendChild(proxy.dom);
31918     return proxy.dom;
31919 };
31920
31921 /** 
31922  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
31923  * Default Adapter. It assumes the splitter and resizing element are not positioned
31924  * elements and only gets/sets the width of the element. Generally used for table based layouts.
31925  */
31926 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
31927 };
31928
31929 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
31930     // do nothing for now
31931     init : function(s){
31932     
31933     },
31934     /**
31935      * Called before drag operations to get the current size of the resizing element. 
31936      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31937      */
31938      getElementSize : function(s){
31939         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31940             return s.resizingEl.getWidth();
31941         }else{
31942             return s.resizingEl.getHeight();
31943         }
31944     },
31945     
31946     /**
31947      * Called after drag operations to set the size of the resizing element.
31948      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31949      * @param {Number} newSize The new size to set
31950      * @param {Function} onComplete A function to be invoked when resizing is complete
31951      */
31952     setElementSize : function(s, newSize, onComplete){
31953         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31954             if(!s.animate){
31955                 s.resizingEl.setWidth(newSize);
31956                 if(onComplete){
31957                     onComplete(s, newSize);
31958                 }
31959             }else{
31960                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
31961             }
31962         }else{
31963             
31964             if(!s.animate){
31965                 s.resizingEl.setHeight(newSize);
31966                 if(onComplete){
31967                     onComplete(s, newSize);
31968                 }
31969             }else{
31970                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
31971             }
31972         }
31973     }
31974 };
31975
31976 /** 
31977  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
31978  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
31979  * Adapter that  moves the splitter element to align with the resized sizing element. 
31980  * Used with an absolute positioned SplitBar.
31981  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
31982  * document.body, make sure you assign an id to the body element.
31983  */
31984 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
31985     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31986     this.container = Roo.get(container);
31987 };
31988
31989 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
31990     init : function(s){
31991         this.basic.init(s);
31992     },
31993     
31994     getElementSize : function(s){
31995         return this.basic.getElementSize(s);
31996     },
31997     
31998     setElementSize : function(s, newSize, onComplete){
31999         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
32000     },
32001     
32002     moveSplitter : function(s){
32003         var yes = Roo.bootstrap.SplitBar;
32004         switch(s.placement){
32005             case yes.LEFT:
32006                 s.el.setX(s.resizingEl.getRight());
32007                 break;
32008             case yes.RIGHT:
32009                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
32010                 break;
32011             case yes.TOP:
32012                 s.el.setY(s.resizingEl.getBottom());
32013                 break;
32014             case yes.BOTTOM:
32015                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
32016                 break;
32017         }
32018     }
32019 };
32020
32021 /**
32022  * Orientation constant - Create a vertical SplitBar
32023  * @static
32024  * @type Number
32025  */
32026 Roo.bootstrap.SplitBar.VERTICAL = 1;
32027
32028 /**
32029  * Orientation constant - Create a horizontal SplitBar
32030  * @static
32031  * @type Number
32032  */
32033 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
32034
32035 /**
32036  * Placement constant - The resizing element is to the left of the splitter element
32037  * @static
32038  * @type Number
32039  */
32040 Roo.bootstrap.SplitBar.LEFT = 1;
32041
32042 /**
32043  * Placement constant - The resizing element is to the right of the splitter element
32044  * @static
32045  * @type Number
32046  */
32047 Roo.bootstrap.SplitBar.RIGHT = 2;
32048
32049 /**
32050  * Placement constant - The resizing element is positioned above the splitter element
32051  * @static
32052  * @type Number
32053  */
32054 Roo.bootstrap.SplitBar.TOP = 3;
32055
32056 /**
32057  * Placement constant - The resizing element is positioned under splitter element
32058  * @static
32059  * @type Number
32060  */
32061 Roo.bootstrap.SplitBar.BOTTOM = 4;
32062 Roo.namespace("Roo.bootstrap.layout");/*
32063  * Based on:
32064  * Ext JS Library 1.1.1
32065  * Copyright(c) 2006-2007, Ext JS, LLC.
32066  *
32067  * Originally Released Under LGPL - original licence link has changed is not relivant.
32068  *
32069  * Fork - LGPL
32070  * <script type="text/javascript">
32071  */
32072
32073 /**
32074  * @class Roo.bootstrap.layout.Manager
32075  * @extends Roo.bootstrap.Component
32076  * Base class for layout managers.
32077  */
32078 Roo.bootstrap.layout.Manager = function(config)
32079 {
32080     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
32081
32082
32083
32084
32085
32086     /** false to disable window resize monitoring @type Boolean */
32087     this.monitorWindowResize = true;
32088     this.regions = {};
32089     this.addEvents({
32090         /**
32091          * @event layout
32092          * Fires when a layout is performed.
32093          * @param {Roo.LayoutManager} this
32094          */
32095         "layout" : true,
32096         /**
32097          * @event regionresized
32098          * Fires when the user resizes a region.
32099          * @param {Roo.LayoutRegion} region The resized region
32100          * @param {Number} newSize The new size (width for east/west, height for north/south)
32101          */
32102         "regionresized" : true,
32103         /**
32104          * @event regioncollapsed
32105          * Fires when a region is collapsed.
32106          * @param {Roo.LayoutRegion} region The collapsed region
32107          */
32108         "regioncollapsed" : true,
32109         /**
32110          * @event regionexpanded
32111          * Fires when a region is expanded.
32112          * @param {Roo.LayoutRegion} region The expanded region
32113          */
32114         "regionexpanded" : true
32115     });
32116     this.updating = false;
32117
32118     if (config.el) {
32119         this.el = Roo.get(config.el);
32120         this.initEvents();
32121     }
32122
32123 };
32124
32125 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
32126
32127
32128     regions : null,
32129
32130     monitorWindowResize : true,
32131
32132
32133     updating : false,
32134
32135
32136     onRender : function(ct, position)
32137     {
32138         if(!this.el){
32139             this.el = Roo.get(ct);
32140             this.initEvents();
32141         }
32142         //this.fireEvent('render',this);
32143     },
32144
32145
32146     initEvents: function()
32147     {
32148
32149
32150         // ie scrollbar fix
32151         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32152             document.body.scroll = "no";
32153         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32154             this.el.position('relative');
32155         }
32156         this.id = this.el.id;
32157         this.el.addClass("roo-layout-container");
32158         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32159         if(this.el.dom != document.body ) {
32160             this.el.on('resize', this.layout,this);
32161             this.el.on('show', this.layout,this);
32162         }
32163
32164     },
32165
32166     /**
32167      * Returns true if this layout is currently being updated
32168      * @return {Boolean}
32169      */
32170     isUpdating : function(){
32171         return this.updating;
32172     },
32173
32174     /**
32175      * Suspend the LayoutManager from doing auto-layouts while
32176      * making multiple add or remove calls
32177      */
32178     beginUpdate : function(){
32179         this.updating = true;
32180     },
32181
32182     /**
32183      * Restore auto-layouts and optionally disable the manager from performing a layout
32184      * @param {Boolean} noLayout true to disable a layout update
32185      */
32186     endUpdate : function(noLayout){
32187         this.updating = false;
32188         if(!noLayout){
32189             this.layout();
32190         }
32191     },
32192
32193     layout: function(){
32194         // abstract...
32195     },
32196
32197     onRegionResized : function(region, newSize){
32198         this.fireEvent("regionresized", region, newSize);
32199         this.layout();
32200     },
32201
32202     onRegionCollapsed : function(region){
32203         this.fireEvent("regioncollapsed", region);
32204     },
32205
32206     onRegionExpanded : function(region){
32207         this.fireEvent("regionexpanded", region);
32208     },
32209
32210     /**
32211      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32212      * performs box-model adjustments.
32213      * @return {Object} The size as an object {width: (the width), height: (the height)}
32214      */
32215     getViewSize : function()
32216     {
32217         var size;
32218         if(this.el.dom != document.body){
32219             size = this.el.getSize();
32220         }else{
32221             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32222         }
32223         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32224         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32225         return size;
32226     },
32227
32228     /**
32229      * Returns the Element this layout is bound to.
32230      * @return {Roo.Element}
32231      */
32232     getEl : function(){
32233         return this.el;
32234     },
32235
32236     /**
32237      * Returns the specified region.
32238      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32239      * @return {Roo.LayoutRegion}
32240      */
32241     getRegion : function(target){
32242         return this.regions[target.toLowerCase()];
32243     },
32244
32245     onWindowResize : function(){
32246         if(this.monitorWindowResize){
32247             this.layout();
32248         }
32249     }
32250 });
32251 /*
32252  * Based on:
32253  * Ext JS Library 1.1.1
32254  * Copyright(c) 2006-2007, Ext JS, LLC.
32255  *
32256  * Originally Released Under LGPL - original licence link has changed is not relivant.
32257  *
32258  * Fork - LGPL
32259  * <script type="text/javascript">
32260  */
32261 /**
32262  * @class Roo.bootstrap.layout.Border
32263  * @extends Roo.bootstrap.layout.Manager
32264  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32265  * please see: examples/bootstrap/nested.html<br><br>
32266  
32267 <b>The container the layout is rendered into can be either the body element or any other element.
32268 If it is not the body element, the container needs to either be an absolute positioned element,
32269 or you will need to add "position:relative" to the css of the container.  You will also need to specify
32270 the container size if it is not the body element.</b>
32271
32272 * @constructor
32273 * Create a new Border
32274 * @param {Object} config Configuration options
32275  */
32276 Roo.bootstrap.layout.Border = function(config){
32277     config = config || {};
32278     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
32279     
32280     
32281     
32282     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
32283         if(config[region]){
32284             config[region].region = region;
32285             this.addRegion(config[region]);
32286         }
32287     },this);
32288     
32289 };
32290
32291 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
32292
32293 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
32294     /**
32295      * Creates and adds a new region if it doesn't already exist.
32296      * @param {String} target The target region key (north, south, east, west or center).
32297      * @param {Object} config The regions config object
32298      * @return {BorderLayoutRegion} The new region
32299      */
32300     addRegion : function(config)
32301     {
32302         if(!this.regions[config.region]){
32303             var r = this.factory(config);
32304             this.bindRegion(r);
32305         }
32306         return this.regions[config.region];
32307     },
32308
32309     // private (kinda)
32310     bindRegion : function(r){
32311         this.regions[r.config.region] = r;
32312         
32313         r.on("visibilitychange",    this.layout, this);
32314         r.on("paneladded",          this.layout, this);
32315         r.on("panelremoved",        this.layout, this);
32316         r.on("invalidated",         this.layout, this);
32317         r.on("resized",             this.onRegionResized, this);
32318         r.on("collapsed",           this.onRegionCollapsed, this);
32319         r.on("expanded",            this.onRegionExpanded, this);
32320     },
32321
32322     /**
32323      * Performs a layout update.
32324      */
32325     layout : function()
32326     {
32327         if(this.updating) {
32328             return;
32329         }
32330         
32331         // render all the rebions if they have not been done alreayd?
32332         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
32333             if(this.regions[region] && !this.regions[region].bodyEl){
32334                 this.regions[region].onRender(this.el)
32335             }
32336         },this);
32337         
32338         var size = this.getViewSize();
32339         var w = size.width;
32340         var h = size.height;
32341         var centerW = w;
32342         var centerH = h;
32343         var centerY = 0;
32344         var centerX = 0;
32345         //var x = 0, y = 0;
32346
32347         var rs = this.regions;
32348         var north = rs["north"];
32349         var south = rs["south"]; 
32350         var west = rs["west"];
32351         var east = rs["east"];
32352         var center = rs["center"];
32353         //if(this.hideOnLayout){ // not supported anymore
32354             //c.el.setStyle("display", "none");
32355         //}
32356         if(north && north.isVisible()){
32357             var b = north.getBox();
32358             var m = north.getMargins();
32359             b.width = w - (m.left+m.right);
32360             b.x = m.left;
32361             b.y = m.top;
32362             centerY = b.height + b.y + m.bottom;
32363             centerH -= centerY;
32364             north.updateBox(this.safeBox(b));
32365         }
32366         if(south && south.isVisible()){
32367             var b = south.getBox();
32368             var m = south.getMargins();
32369             b.width = w - (m.left+m.right);
32370             b.x = m.left;
32371             var totalHeight = (b.height + m.top + m.bottom);
32372             b.y = h - totalHeight + m.top;
32373             centerH -= totalHeight;
32374             south.updateBox(this.safeBox(b));
32375         }
32376         if(west && west.isVisible()){
32377             var b = west.getBox();
32378             var m = west.getMargins();
32379             b.height = centerH - (m.top+m.bottom);
32380             b.x = m.left;
32381             b.y = centerY + m.top;
32382             var totalWidth = (b.width + m.left + m.right);
32383             centerX += totalWidth;
32384             centerW -= totalWidth;
32385             west.updateBox(this.safeBox(b));
32386         }
32387         if(east && east.isVisible()){
32388             var b = east.getBox();
32389             var m = east.getMargins();
32390             b.height = centerH - (m.top+m.bottom);
32391             var totalWidth = (b.width + m.left + m.right);
32392             b.x = w - totalWidth + m.left;
32393             b.y = centerY + m.top;
32394             centerW -= totalWidth;
32395             east.updateBox(this.safeBox(b));
32396         }
32397         if(center){
32398             var m = center.getMargins();
32399             var centerBox = {
32400                 x: centerX + m.left,
32401                 y: centerY + m.top,
32402                 width: centerW - (m.left+m.right),
32403                 height: centerH - (m.top+m.bottom)
32404             };
32405             //if(this.hideOnLayout){
32406                 //center.el.setStyle("display", "block");
32407             //}
32408             center.updateBox(this.safeBox(centerBox));
32409         }
32410         this.el.repaint();
32411         this.fireEvent("layout", this);
32412     },
32413
32414     // private
32415     safeBox : function(box){
32416         box.width = Math.max(0, box.width);
32417         box.height = Math.max(0, box.height);
32418         return box;
32419     },
32420
32421     /**
32422      * Adds a ContentPanel (or subclass) to this layout.
32423      * @param {String} target The target region key (north, south, east, west or center).
32424      * @param {Roo.ContentPanel} panel The panel to add
32425      * @return {Roo.ContentPanel} The added panel
32426      */
32427     add : function(target, panel){
32428          
32429         target = target.toLowerCase();
32430         return this.regions[target].add(panel);
32431     },
32432
32433     /**
32434      * Remove a ContentPanel (or subclass) to this layout.
32435      * @param {String} target The target region key (north, south, east, west or center).
32436      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
32437      * @return {Roo.ContentPanel} The removed panel
32438      */
32439     remove : function(target, panel){
32440         target = target.toLowerCase();
32441         return this.regions[target].remove(panel);
32442     },
32443
32444     /**
32445      * Searches all regions for a panel with the specified id
32446      * @param {String} panelId
32447      * @return {Roo.ContentPanel} The panel or null if it wasn't found
32448      */
32449     findPanel : function(panelId){
32450         var rs = this.regions;
32451         for(var target in rs){
32452             if(typeof rs[target] != "function"){
32453                 var p = rs[target].getPanel(panelId);
32454                 if(p){
32455                     return p;
32456                 }
32457             }
32458         }
32459         return null;
32460     },
32461
32462     /**
32463      * Searches all regions for a panel with the specified id and activates (shows) it.
32464      * @param {String/ContentPanel} panelId The panels id or the panel itself
32465      * @return {Roo.ContentPanel} The shown panel or null
32466      */
32467     showPanel : function(panelId) {
32468       var rs = this.regions;
32469       for(var target in rs){
32470          var r = rs[target];
32471          if(typeof r != "function"){
32472             if(r.hasPanel(panelId)){
32473                return r.showPanel(panelId);
32474             }
32475          }
32476       }
32477       return null;
32478    },
32479
32480    /**
32481      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
32482      * @param {Roo.state.Provider} provider (optional) An alternate state provider
32483      */
32484    /*
32485     restoreState : function(provider){
32486         if(!provider){
32487             provider = Roo.state.Manager;
32488         }
32489         var sm = new Roo.LayoutStateManager();
32490         sm.init(this, provider);
32491     },
32492 */
32493  
32494  
32495     /**
32496      * Adds a xtype elements to the layout.
32497      * <pre><code>
32498
32499 layout.addxtype({
32500        xtype : 'ContentPanel',
32501        region: 'west',
32502        items: [ .... ]
32503    }
32504 );
32505
32506 layout.addxtype({
32507         xtype : 'NestedLayoutPanel',
32508         region: 'west',
32509         layout: {
32510            center: { },
32511            west: { }   
32512         },
32513         items : [ ... list of content panels or nested layout panels.. ]
32514    }
32515 );
32516 </code></pre>
32517      * @param {Object} cfg Xtype definition of item to add.
32518      */
32519     addxtype : function(cfg)
32520     {
32521         // basically accepts a pannel...
32522         // can accept a layout region..!?!?
32523         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
32524         
32525         
32526         // theory?  children can only be panels??
32527         
32528         //if (!cfg.xtype.match(/Panel$/)) {
32529         //    return false;
32530         //}
32531         var ret = false;
32532         
32533         if (typeof(cfg.region) == 'undefined') {
32534             Roo.log("Failed to add Panel, region was not set");
32535             Roo.log(cfg);
32536             return false;
32537         }
32538         var region = cfg.region;
32539         delete cfg.region;
32540         
32541           
32542         var xitems = [];
32543         if (cfg.items) {
32544             xitems = cfg.items;
32545             delete cfg.items;
32546         }
32547         var nb = false;
32548         
32549         switch(cfg.xtype) 
32550         {
32551             case 'Content':  // ContentPanel (el, cfg)
32552             case 'Scroll':  // ContentPanel (el, cfg)
32553             case 'View': 
32554                 cfg.autoCreate = true;
32555                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32556                 //} else {
32557                 //    var el = this.el.createChild();
32558                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
32559                 //}
32560                 
32561                 this.add(region, ret);
32562                 break;
32563             
32564             /*
32565             case 'TreePanel': // our new panel!
32566                 cfg.el = this.el.createChild();
32567                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32568                 this.add(region, ret);
32569                 break;
32570             */
32571             
32572             case 'Nest': 
32573                 // create a new Layout (which is  a Border Layout...
32574                 
32575                 var clayout = cfg.layout;
32576                 clayout.el  = this.el.createChild();
32577                 clayout.items   = clayout.items  || [];
32578                 
32579                 delete cfg.layout;
32580                 
32581                 // replace this exitems with the clayout ones..
32582                 xitems = clayout.items;
32583                  
32584                 // force background off if it's in center...
32585                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
32586                     cfg.background = false;
32587                 }
32588                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
32589                 
32590                 
32591                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32592                 //console.log('adding nested layout panel '  + cfg.toSource());
32593                 this.add(region, ret);
32594                 nb = {}; /// find first...
32595                 break;
32596             
32597             case 'Grid':
32598                 
32599                 // needs grid and region
32600                 
32601                 //var el = this.getRegion(region).el.createChild();
32602                 /*
32603                  *var el = this.el.createChild();
32604                 // create the grid first...
32605                 cfg.grid.container = el;
32606                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
32607                 */
32608                 
32609                 if (region == 'center' && this.active ) {
32610                     cfg.background = false;
32611                 }
32612                 
32613                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32614                 
32615                 this.add(region, ret);
32616                 /*
32617                 if (cfg.background) {
32618                     // render grid on panel activation (if panel background)
32619                     ret.on('activate', function(gp) {
32620                         if (!gp.grid.rendered) {
32621                     //        gp.grid.render(el);
32622                         }
32623                     });
32624                 } else {
32625                   //  cfg.grid.render(el);
32626                 }
32627                 */
32628                 break;
32629            
32630            
32631             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
32632                 // it was the old xcomponent building that caused this before.
32633                 // espeically if border is the top element in the tree.
32634                 ret = this;
32635                 break; 
32636                 
32637                     
32638                 
32639                 
32640                 
32641             default:
32642                 /*
32643                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
32644                     
32645                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32646                     this.add(region, ret);
32647                 } else {
32648                 */
32649                     Roo.log(cfg);
32650                     throw "Can not add '" + cfg.xtype + "' to Border";
32651                     return null;
32652              
32653                                 
32654              
32655         }
32656         this.beginUpdate();
32657         // add children..
32658         var region = '';
32659         var abn = {};
32660         Roo.each(xitems, function(i)  {
32661             region = nb && i.region ? i.region : false;
32662             
32663             var add = ret.addxtype(i);
32664            
32665             if (region) {
32666                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
32667                 if (!i.background) {
32668                     abn[region] = nb[region] ;
32669                 }
32670             }
32671             
32672         });
32673         this.endUpdate();
32674
32675         // make the last non-background panel active..
32676         //if (nb) { Roo.log(abn); }
32677         if (nb) {
32678             
32679             for(var r in abn) {
32680                 region = this.getRegion(r);
32681                 if (region) {
32682                     // tried using nb[r], but it does not work..
32683                      
32684                     region.showPanel(abn[r]);
32685                    
32686                 }
32687             }
32688         }
32689         return ret;
32690         
32691     },
32692     
32693     
32694 // private
32695     factory : function(cfg)
32696     {
32697         
32698         var validRegions = Roo.bootstrap.layout.Border.regions;
32699
32700         var target = cfg.region;
32701         cfg.mgr = this;
32702         
32703         var r = Roo.bootstrap.layout;
32704         Roo.log(target);
32705         switch(target){
32706             case "north":
32707                 return new r.North(cfg);
32708             case "south":
32709                 return new r.South(cfg);
32710             case "east":
32711                 return new r.East(cfg);
32712             case "west":
32713                 return new r.West(cfg);
32714             case "center":
32715                 return new r.Center(cfg);
32716         }
32717         throw 'Layout region "'+target+'" not supported.';
32718     }
32719     
32720     
32721 });
32722  /*
32723  * Based on:
32724  * Ext JS Library 1.1.1
32725  * Copyright(c) 2006-2007, Ext JS, LLC.
32726  *
32727  * Originally Released Under LGPL - original licence link has changed is not relivant.
32728  *
32729  * Fork - LGPL
32730  * <script type="text/javascript">
32731  */
32732  
32733 /**
32734  * @class Roo.bootstrap.layout.Basic
32735  * @extends Roo.util.Observable
32736  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
32737  * and does not have a titlebar, tabs or any other features. All it does is size and position 
32738  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
32739  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
32740  * @cfg {string}   region  the region that it inhabits..
32741  * @cfg {bool}   skipConfig skip config?
32742  * 
32743
32744  */
32745 Roo.bootstrap.layout.Basic = function(config){
32746     
32747     this.mgr = config.mgr;
32748     
32749     this.position = config.region;
32750     
32751     var skipConfig = config.skipConfig;
32752     
32753     this.events = {
32754         /**
32755          * @scope Roo.BasicLayoutRegion
32756          */
32757         
32758         /**
32759          * @event beforeremove
32760          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
32761          * @param {Roo.LayoutRegion} this
32762          * @param {Roo.ContentPanel} panel The panel
32763          * @param {Object} e The cancel event object
32764          */
32765         "beforeremove" : true,
32766         /**
32767          * @event invalidated
32768          * Fires when the layout for this region is changed.
32769          * @param {Roo.LayoutRegion} this
32770          */
32771         "invalidated" : true,
32772         /**
32773          * @event visibilitychange
32774          * Fires when this region is shown or hidden 
32775          * @param {Roo.LayoutRegion} this
32776          * @param {Boolean} visibility true or false
32777          */
32778         "visibilitychange" : true,
32779         /**
32780          * @event paneladded
32781          * Fires when a panel is added. 
32782          * @param {Roo.LayoutRegion} this
32783          * @param {Roo.ContentPanel} panel The panel
32784          */
32785         "paneladded" : true,
32786         /**
32787          * @event panelremoved
32788          * Fires when a panel is removed. 
32789          * @param {Roo.LayoutRegion} this
32790          * @param {Roo.ContentPanel} panel The panel
32791          */
32792         "panelremoved" : true,
32793         /**
32794          * @event beforecollapse
32795          * Fires when this region before collapse.
32796          * @param {Roo.LayoutRegion} this
32797          */
32798         "beforecollapse" : true,
32799         /**
32800          * @event collapsed
32801          * Fires when this region is collapsed.
32802          * @param {Roo.LayoutRegion} this
32803          */
32804         "collapsed" : true,
32805         /**
32806          * @event expanded
32807          * Fires when this region is expanded.
32808          * @param {Roo.LayoutRegion} this
32809          */
32810         "expanded" : true,
32811         /**
32812          * @event slideshow
32813          * Fires when this region is slid into view.
32814          * @param {Roo.LayoutRegion} this
32815          */
32816         "slideshow" : true,
32817         /**
32818          * @event slidehide
32819          * Fires when this region slides out of view. 
32820          * @param {Roo.LayoutRegion} this
32821          */
32822         "slidehide" : true,
32823         /**
32824          * @event panelactivated
32825          * Fires when a panel is activated. 
32826          * @param {Roo.LayoutRegion} this
32827          * @param {Roo.ContentPanel} panel The activated panel
32828          */
32829         "panelactivated" : true,
32830         /**
32831          * @event resized
32832          * Fires when the user resizes this region. 
32833          * @param {Roo.LayoutRegion} this
32834          * @param {Number} newSize The new size (width for east/west, height for north/south)
32835          */
32836         "resized" : true
32837     };
32838     /** A collection of panels in this region. @type Roo.util.MixedCollection */
32839     this.panels = new Roo.util.MixedCollection();
32840     this.panels.getKey = this.getPanelId.createDelegate(this);
32841     this.box = null;
32842     this.activePanel = null;
32843     // ensure listeners are added...
32844     
32845     if (config.listeners || config.events) {
32846         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
32847             listeners : config.listeners || {},
32848             events : config.events || {}
32849         });
32850     }
32851     
32852     if(skipConfig !== true){
32853         this.applyConfig(config);
32854     }
32855 };
32856
32857 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
32858 {
32859     getPanelId : function(p){
32860         return p.getId();
32861     },
32862     
32863     applyConfig : function(config){
32864         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32865         this.config = config;
32866         
32867     },
32868     
32869     /**
32870      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
32871      * the width, for horizontal (north, south) the height.
32872      * @param {Number} newSize The new width or height
32873      */
32874     resizeTo : function(newSize){
32875         var el = this.el ? this.el :
32876                  (this.activePanel ? this.activePanel.getEl() : null);
32877         if(el){
32878             switch(this.position){
32879                 case "east":
32880                 case "west":
32881                     el.setWidth(newSize);
32882                     this.fireEvent("resized", this, newSize);
32883                 break;
32884                 case "north":
32885                 case "south":
32886                     el.setHeight(newSize);
32887                     this.fireEvent("resized", this, newSize);
32888                 break;                
32889             }
32890         }
32891     },
32892     
32893     getBox : function(){
32894         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
32895     },
32896     
32897     getMargins : function(){
32898         return this.margins;
32899     },
32900     
32901     updateBox : function(box){
32902         this.box = box;
32903         var el = this.activePanel.getEl();
32904         el.dom.style.left = box.x + "px";
32905         el.dom.style.top = box.y + "px";
32906         this.activePanel.setSize(box.width, box.height);
32907     },
32908     
32909     /**
32910      * Returns the container element for this region.
32911      * @return {Roo.Element}
32912      */
32913     getEl : function(){
32914         return this.activePanel;
32915     },
32916     
32917     /**
32918      * Returns true if this region is currently visible.
32919      * @return {Boolean}
32920      */
32921     isVisible : function(){
32922         return this.activePanel ? true : false;
32923     },
32924     
32925     setActivePanel : function(panel){
32926         panel = this.getPanel(panel);
32927         if(this.activePanel && this.activePanel != panel){
32928             this.activePanel.setActiveState(false);
32929             this.activePanel.getEl().setLeftTop(-10000,-10000);
32930         }
32931         this.activePanel = panel;
32932         panel.setActiveState(true);
32933         if(this.box){
32934             panel.setSize(this.box.width, this.box.height);
32935         }
32936         this.fireEvent("panelactivated", this, panel);
32937         this.fireEvent("invalidated");
32938     },
32939     
32940     /**
32941      * Show the specified panel.
32942      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
32943      * @return {Roo.ContentPanel} The shown panel or null
32944      */
32945     showPanel : function(panel){
32946         panel = this.getPanel(panel);
32947         if(panel){
32948             this.setActivePanel(panel);
32949         }
32950         return panel;
32951     },
32952     
32953     /**
32954      * Get the active panel for this region.
32955      * @return {Roo.ContentPanel} The active panel or null
32956      */
32957     getActivePanel : function(){
32958         return this.activePanel;
32959     },
32960     
32961     /**
32962      * Add the passed ContentPanel(s)
32963      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32964      * @return {Roo.ContentPanel} The panel added (if only one was added)
32965      */
32966     add : function(panel){
32967         if(arguments.length > 1){
32968             for(var i = 0, len = arguments.length; i < len; i++) {
32969                 this.add(arguments[i]);
32970             }
32971             return null;
32972         }
32973         if(this.hasPanel(panel)){
32974             this.showPanel(panel);
32975             return panel;
32976         }
32977         var el = panel.getEl();
32978         if(el.dom.parentNode != this.mgr.el.dom){
32979             this.mgr.el.dom.appendChild(el.dom);
32980         }
32981         if(panel.setRegion){
32982             panel.setRegion(this);
32983         }
32984         this.panels.add(panel);
32985         el.setStyle("position", "absolute");
32986         if(!panel.background){
32987             this.setActivePanel(panel);
32988             if(this.config.initialSize && this.panels.getCount()==1){
32989                 this.resizeTo(this.config.initialSize);
32990             }
32991         }
32992         this.fireEvent("paneladded", this, panel);
32993         return panel;
32994     },
32995     
32996     /**
32997      * Returns true if the panel is in this region.
32998      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32999      * @return {Boolean}
33000      */
33001     hasPanel : function(panel){
33002         if(typeof panel == "object"){ // must be panel obj
33003             panel = panel.getId();
33004         }
33005         return this.getPanel(panel) ? true : false;
33006     },
33007     
33008     /**
33009      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33010      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33011      * @param {Boolean} preservePanel Overrides the config preservePanel option
33012      * @return {Roo.ContentPanel} The panel that was removed
33013      */
33014     remove : function(panel, preservePanel){
33015         panel = this.getPanel(panel);
33016         if(!panel){
33017             return null;
33018         }
33019         var e = {};
33020         this.fireEvent("beforeremove", this, panel, e);
33021         if(e.cancel === true){
33022             return null;
33023         }
33024         var panelId = panel.getId();
33025         this.panels.removeKey(panelId);
33026         return panel;
33027     },
33028     
33029     /**
33030      * Returns the panel specified or null if it's not in this region.
33031      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33032      * @return {Roo.ContentPanel}
33033      */
33034     getPanel : function(id){
33035         if(typeof id == "object"){ // must be panel obj
33036             return id;
33037         }
33038         return this.panels.get(id);
33039     },
33040     
33041     /**
33042      * Returns this regions position (north/south/east/west/center).
33043      * @return {String} 
33044      */
33045     getPosition: function(){
33046         return this.position;    
33047     }
33048 });/*
33049  * Based on:
33050  * Ext JS Library 1.1.1
33051  * Copyright(c) 2006-2007, Ext JS, LLC.
33052  *
33053  * Originally Released Under LGPL - original licence link has changed is not relivant.
33054  *
33055  * Fork - LGPL
33056  * <script type="text/javascript">
33057  */
33058  
33059 /**
33060  * @class Roo.bootstrap.layout.Region
33061  * @extends Roo.bootstrap.layout.Basic
33062  * This class represents a region in a layout manager.
33063  
33064  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33065  * @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})
33066  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
33067  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
33068  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
33069  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
33070  * @cfg {String}    title           The title for the region (overrides panel titles)
33071  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
33072  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33073  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
33074  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33075  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
33076  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33077  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
33078  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
33079  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
33080  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
33081
33082  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
33083  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
33084  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
33085  * @cfg {Number}    width           For East/West panels
33086  * @cfg {Number}    height          For North/South panels
33087  * @cfg {Boolean}   split           To show the splitter
33088  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
33089  * 
33090  * @cfg {string}   cls             Extra CSS classes to add to region
33091  * 
33092  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
33093  * @cfg {string}   region  the region that it inhabits..
33094  *
33095
33096  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
33097  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
33098
33099  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
33100  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
33101  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
33102  */
33103 Roo.bootstrap.layout.Region = function(config)
33104 {
33105     this.applyConfig(config);
33106
33107     var mgr = config.mgr;
33108     var pos = config.region;
33109     config.skipConfig = true;
33110     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
33111     
33112     if (mgr.el) {
33113         this.onRender(mgr.el);   
33114     }
33115      
33116     this.visible = true;
33117     this.collapsed = false;
33118     this.unrendered_panels = [];
33119 };
33120
33121 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
33122
33123     position: '', // set by wrapper (eg. north/south etc..)
33124     unrendered_panels : null,  // unrendered panels.
33125     createBody : function(){
33126         /** This region's body element 
33127         * @type Roo.Element */
33128         this.bodyEl = this.el.createChild({
33129                 tag: "div",
33130                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
33131         });
33132     },
33133
33134     onRender: function(ctr, pos)
33135     {
33136         var dh = Roo.DomHelper;
33137         /** This region's container element 
33138         * @type Roo.Element */
33139         this.el = dh.append(ctr.dom, {
33140                 tag: "div",
33141                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
33142             }, true);
33143         /** This region's title element 
33144         * @type Roo.Element */
33145     
33146         this.titleEl = dh.append(this.el.dom,
33147             {
33148                     tag: "div",
33149                     unselectable: "on",
33150                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
33151                     children:[
33152                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
33153                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
33154                     ]}, true);
33155         
33156         this.titleEl.enableDisplayMode();
33157         /** This region's title text element 
33158         * @type HTMLElement */
33159         this.titleTextEl = this.titleEl.dom.firstChild;
33160         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33161         /*
33162         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
33163         this.closeBtn.enableDisplayMode();
33164         this.closeBtn.on("click", this.closeClicked, this);
33165         this.closeBtn.hide();
33166     */
33167         this.createBody(this.config);
33168         if(this.config.hideWhenEmpty){
33169             this.hide();
33170             this.on("paneladded", this.validateVisibility, this);
33171             this.on("panelremoved", this.validateVisibility, this);
33172         }
33173         if(this.autoScroll){
33174             this.bodyEl.setStyle("overflow", "auto");
33175         }else{
33176             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
33177         }
33178         //if(c.titlebar !== false){
33179             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
33180                 this.titleEl.hide();
33181             }else{
33182                 this.titleEl.show();
33183                 if(this.config.title){
33184                     this.titleTextEl.innerHTML = this.config.title;
33185                 }
33186             }
33187         //}
33188         if(this.config.collapsed){
33189             this.collapse(true);
33190         }
33191         if(this.config.hidden){
33192             this.hide();
33193         }
33194         
33195         if (this.unrendered_panels && this.unrendered_panels.length) {
33196             for (var i =0;i< this.unrendered_panels.length; i++) {
33197                 this.add(this.unrendered_panels[i]);
33198             }
33199             this.unrendered_panels = null;
33200             
33201         }
33202         
33203     },
33204     
33205     applyConfig : function(c)
33206     {
33207         /*
33208          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
33209             var dh = Roo.DomHelper;
33210             if(c.titlebar !== false){
33211                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
33212                 this.collapseBtn.on("click", this.collapse, this);
33213                 this.collapseBtn.enableDisplayMode();
33214                 /*
33215                 if(c.showPin === true || this.showPin){
33216                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
33217                     this.stickBtn.enableDisplayMode();
33218                     this.stickBtn.on("click", this.expand, this);
33219                     this.stickBtn.hide();
33220                 }
33221                 
33222             }
33223             */
33224             /** This region's collapsed element
33225             * @type Roo.Element */
33226             /*
33227              *
33228             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33229                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33230             ]}, true);
33231             
33232             if(c.floatable !== false){
33233                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33234                this.collapsedEl.on("click", this.collapseClick, this);
33235             }
33236
33237             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33238                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33239                    id: "message", unselectable: "on", style:{"float":"left"}});
33240                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33241              }
33242             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33243             this.expandBtn.on("click", this.expand, this);
33244             
33245         }
33246         
33247         if(this.collapseBtn){
33248             this.collapseBtn.setVisible(c.collapsible == true);
33249         }
33250         
33251         this.cmargins = c.cmargins || this.cmargins ||
33252                          (this.position == "west" || this.position == "east" ?
33253                              {top: 0, left: 2, right:2, bottom: 0} :
33254                              {top: 2, left: 0, right:0, bottom: 2});
33255         */
33256         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33257         
33258         
33259         this.bottomTabs = c.tabPosition != "top";
33260         
33261         this.autoScroll = c.autoScroll || false;
33262         
33263         
33264        
33265         
33266         this.duration = c.duration || .30;
33267         this.slideDuration = c.slideDuration || .45;
33268         this.config = c;
33269        
33270     },
33271     /**
33272      * Returns true if this region is currently visible.
33273      * @return {Boolean}
33274      */
33275     isVisible : function(){
33276         return this.visible;
33277     },
33278
33279     /**
33280      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33281      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
33282      */
33283     //setCollapsedTitle : function(title){
33284     //    title = title || "&#160;";
33285      //   if(this.collapsedTitleTextEl){
33286       //      this.collapsedTitleTextEl.innerHTML = title;
33287        // }
33288     //},
33289
33290     getBox : function(){
33291         var b;
33292       //  if(!this.collapsed){
33293             b = this.el.getBox(false, true);
33294        // }else{
33295           //  b = this.collapsedEl.getBox(false, true);
33296         //}
33297         return b;
33298     },
33299
33300     getMargins : function(){
33301         return this.margins;
33302         //return this.collapsed ? this.cmargins : this.margins;
33303     },
33304 /*
33305     highlight : function(){
33306         this.el.addClass("x-layout-panel-dragover");
33307     },
33308
33309     unhighlight : function(){
33310         this.el.removeClass("x-layout-panel-dragover");
33311     },
33312 */
33313     updateBox : function(box)
33314     {
33315         if (!this.bodyEl) {
33316             return; // not rendered yet..
33317         }
33318         
33319         this.box = box;
33320         if(!this.collapsed){
33321             this.el.dom.style.left = box.x + "px";
33322             this.el.dom.style.top = box.y + "px";
33323             this.updateBody(box.width, box.height);
33324         }else{
33325             this.collapsedEl.dom.style.left = box.x + "px";
33326             this.collapsedEl.dom.style.top = box.y + "px";
33327             this.collapsedEl.setSize(box.width, box.height);
33328         }
33329         if(this.tabs){
33330             this.tabs.autoSizeTabs();
33331         }
33332     },
33333
33334     updateBody : function(w, h)
33335     {
33336         if(w !== null){
33337             this.el.setWidth(w);
33338             w -= this.el.getBorderWidth("rl");
33339             if(this.config.adjustments){
33340                 w += this.config.adjustments[0];
33341             }
33342         }
33343         if(h !== null && h > 0){
33344             this.el.setHeight(h);
33345             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
33346             h -= this.el.getBorderWidth("tb");
33347             if(this.config.adjustments){
33348                 h += this.config.adjustments[1];
33349             }
33350             this.bodyEl.setHeight(h);
33351             if(this.tabs){
33352                 h = this.tabs.syncHeight(h);
33353             }
33354         }
33355         if(this.panelSize){
33356             w = w !== null ? w : this.panelSize.width;
33357             h = h !== null ? h : this.panelSize.height;
33358         }
33359         if(this.activePanel){
33360             var el = this.activePanel.getEl();
33361             w = w !== null ? w : el.getWidth();
33362             h = h !== null ? h : el.getHeight();
33363             this.panelSize = {width: w, height: h};
33364             this.activePanel.setSize(w, h);
33365         }
33366         if(Roo.isIE && this.tabs){
33367             this.tabs.el.repaint();
33368         }
33369     },
33370
33371     /**
33372      * Returns the container element for this region.
33373      * @return {Roo.Element}
33374      */
33375     getEl : function(){
33376         return this.el;
33377     },
33378
33379     /**
33380      * Hides this region.
33381      */
33382     hide : function(){
33383         //if(!this.collapsed){
33384             this.el.dom.style.left = "-2000px";
33385             this.el.hide();
33386         //}else{
33387          //   this.collapsedEl.dom.style.left = "-2000px";
33388          //   this.collapsedEl.hide();
33389        // }
33390         this.visible = false;
33391         this.fireEvent("visibilitychange", this, false);
33392     },
33393
33394     /**
33395      * Shows this region if it was previously hidden.
33396      */
33397     show : function(){
33398         //if(!this.collapsed){
33399             this.el.show();
33400         //}else{
33401         //    this.collapsedEl.show();
33402        // }
33403         this.visible = true;
33404         this.fireEvent("visibilitychange", this, true);
33405     },
33406 /*
33407     closeClicked : function(){
33408         if(this.activePanel){
33409             this.remove(this.activePanel);
33410         }
33411     },
33412
33413     collapseClick : function(e){
33414         if(this.isSlid){
33415            e.stopPropagation();
33416            this.slideIn();
33417         }else{
33418            e.stopPropagation();
33419            this.slideOut();
33420         }
33421     },
33422 */
33423     /**
33424      * Collapses this region.
33425      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
33426      */
33427     /*
33428     collapse : function(skipAnim, skipCheck = false){
33429         if(this.collapsed) {
33430             return;
33431         }
33432         
33433         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
33434             
33435             this.collapsed = true;
33436             if(this.split){
33437                 this.split.el.hide();
33438             }
33439             if(this.config.animate && skipAnim !== true){
33440                 this.fireEvent("invalidated", this);
33441                 this.animateCollapse();
33442             }else{
33443                 this.el.setLocation(-20000,-20000);
33444                 this.el.hide();
33445                 this.collapsedEl.show();
33446                 this.fireEvent("collapsed", this);
33447                 this.fireEvent("invalidated", this);
33448             }
33449         }
33450         
33451     },
33452 */
33453     animateCollapse : function(){
33454         // overridden
33455     },
33456
33457     /**
33458      * Expands this region if it was previously collapsed.
33459      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
33460      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
33461      */
33462     /*
33463     expand : function(e, skipAnim){
33464         if(e) {
33465             e.stopPropagation();
33466         }
33467         if(!this.collapsed || this.el.hasActiveFx()) {
33468             return;
33469         }
33470         if(this.isSlid){
33471             this.afterSlideIn();
33472             skipAnim = true;
33473         }
33474         this.collapsed = false;
33475         if(this.config.animate && skipAnim !== true){
33476             this.animateExpand();
33477         }else{
33478             this.el.show();
33479             if(this.split){
33480                 this.split.el.show();
33481             }
33482             this.collapsedEl.setLocation(-2000,-2000);
33483             this.collapsedEl.hide();
33484             this.fireEvent("invalidated", this);
33485             this.fireEvent("expanded", this);
33486         }
33487     },
33488 */
33489     animateExpand : function(){
33490         // overridden
33491     },
33492
33493     initTabs : function()
33494     {
33495         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
33496         
33497         var ts = new Roo.bootstrap.panel.Tabs({
33498                 el: this.bodyEl.dom,
33499                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
33500                 disableTooltips: this.config.disableTabTips,
33501                 toolbar : this.config.toolbar
33502             });
33503         
33504         if(this.config.hideTabs){
33505             ts.stripWrap.setDisplayed(false);
33506         }
33507         this.tabs = ts;
33508         ts.resizeTabs = this.config.resizeTabs === true;
33509         ts.minTabWidth = this.config.minTabWidth || 40;
33510         ts.maxTabWidth = this.config.maxTabWidth || 250;
33511         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
33512         ts.monitorResize = false;
33513         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
33514         ts.bodyEl.addClass('roo-layout-tabs-body');
33515         this.panels.each(this.initPanelAsTab, this);
33516     },
33517
33518     initPanelAsTab : function(panel){
33519         var ti = this.tabs.addTab(
33520             panel.getEl().id,
33521             panel.getTitle(),
33522             null,
33523             this.config.closeOnTab && panel.isClosable(),
33524             panel.tpl
33525         );
33526         if(panel.tabTip !== undefined){
33527             ti.setTooltip(panel.tabTip);
33528         }
33529         ti.on("activate", function(){
33530               this.setActivePanel(panel);
33531         }, this);
33532         
33533         if(this.config.closeOnTab){
33534             ti.on("beforeclose", function(t, e){
33535                 e.cancel = true;
33536                 this.remove(panel);
33537             }, this);
33538         }
33539         
33540         panel.tabItem = ti;
33541         
33542         return ti;
33543     },
33544
33545     updatePanelTitle : function(panel, title)
33546     {
33547         if(this.activePanel == panel){
33548             this.updateTitle(title);
33549         }
33550         if(this.tabs){
33551             var ti = this.tabs.getTab(panel.getEl().id);
33552             ti.setText(title);
33553             if(panel.tabTip !== undefined){
33554                 ti.setTooltip(panel.tabTip);
33555             }
33556         }
33557     },
33558
33559     updateTitle : function(title){
33560         if(this.titleTextEl && !this.config.title){
33561             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
33562         }
33563     },
33564
33565     setActivePanel : function(panel)
33566     {
33567         panel = this.getPanel(panel);
33568         if(this.activePanel && this.activePanel != panel){
33569             this.activePanel.setActiveState(false);
33570         }
33571         this.activePanel = panel;
33572         panel.setActiveState(true);
33573         if(this.panelSize){
33574             panel.setSize(this.panelSize.width, this.panelSize.height);
33575         }
33576         if(this.closeBtn){
33577             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
33578         }
33579         this.updateTitle(panel.getTitle());
33580         if(this.tabs){
33581             this.fireEvent("invalidated", this);
33582         }
33583         this.fireEvent("panelactivated", this, panel);
33584     },
33585
33586     /**
33587      * Shows the specified panel.
33588      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
33589      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
33590      */
33591     showPanel : function(panel)
33592     {
33593         panel = this.getPanel(panel);
33594         if(panel){
33595             if(this.tabs){
33596                 var tab = this.tabs.getTab(panel.getEl().id);
33597                 if(tab.isHidden()){
33598                     this.tabs.unhideTab(tab.id);
33599                 }
33600                 tab.activate();
33601             }else{
33602                 this.setActivePanel(panel);
33603             }
33604         }
33605         return panel;
33606     },
33607
33608     /**
33609      * Get the active panel for this region.
33610      * @return {Roo.ContentPanel} The active panel or null
33611      */
33612     getActivePanel : function(){
33613         return this.activePanel;
33614     },
33615
33616     validateVisibility : function(){
33617         if(this.panels.getCount() < 1){
33618             this.updateTitle("&#160;");
33619             this.closeBtn.hide();
33620             this.hide();
33621         }else{
33622             if(!this.isVisible()){
33623                 this.show();
33624             }
33625         }
33626     },
33627
33628     /**
33629      * Adds the passed ContentPanel(s) to this region.
33630      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33631      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
33632      */
33633     add : function(panel)
33634     {
33635         if(arguments.length > 1){
33636             for(var i = 0, len = arguments.length; i < len; i++) {
33637                 this.add(arguments[i]);
33638             }
33639             return null;
33640         }
33641         
33642         // if we have not been rendered yet, then we can not really do much of this..
33643         if (!this.bodyEl) {
33644             this.unrendered_panels.push(panel);
33645             return panel;
33646         }
33647         
33648         
33649         
33650         
33651         if(this.hasPanel(panel)){
33652             this.showPanel(panel);
33653             return panel;
33654         }
33655         panel.setRegion(this);
33656         this.panels.add(panel);
33657        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
33658             // sinle panel - no tab...?? would it not be better to render it with the tabs,
33659             // and hide them... ???
33660             this.bodyEl.dom.appendChild(panel.getEl().dom);
33661             if(panel.background !== true){
33662                 this.setActivePanel(panel);
33663             }
33664             this.fireEvent("paneladded", this, panel);
33665             return panel;
33666         }
33667         */
33668         if(!this.tabs){
33669             this.initTabs();
33670         }else{
33671             this.initPanelAsTab(panel);
33672         }
33673         
33674         
33675         if(panel.background !== true){
33676             this.tabs.activate(panel.getEl().id);
33677         }
33678         this.fireEvent("paneladded", this, panel);
33679         return panel;
33680     },
33681
33682     /**
33683      * Hides the tab for the specified panel.
33684      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33685      */
33686     hidePanel : function(panel){
33687         if(this.tabs && (panel = this.getPanel(panel))){
33688             this.tabs.hideTab(panel.getEl().id);
33689         }
33690     },
33691
33692     /**
33693      * Unhides the tab for a previously hidden panel.
33694      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33695      */
33696     unhidePanel : function(panel){
33697         if(this.tabs && (panel = this.getPanel(panel))){
33698             this.tabs.unhideTab(panel.getEl().id);
33699         }
33700     },
33701
33702     clearPanels : function(){
33703         while(this.panels.getCount() > 0){
33704              this.remove(this.panels.first());
33705         }
33706     },
33707
33708     /**
33709      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33710      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33711      * @param {Boolean} preservePanel Overrides the config preservePanel option
33712      * @return {Roo.ContentPanel} The panel that was removed
33713      */
33714     remove : function(panel, preservePanel)
33715     {
33716         panel = this.getPanel(panel);
33717         if(!panel){
33718             return null;
33719         }
33720         var e = {};
33721         this.fireEvent("beforeremove", this, panel, e);
33722         if(e.cancel === true){
33723             return null;
33724         }
33725         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
33726         var panelId = panel.getId();
33727         this.panels.removeKey(panelId);
33728         if(preservePanel){
33729             document.body.appendChild(panel.getEl().dom);
33730         }
33731         if(this.tabs){
33732             this.tabs.removeTab(panel.getEl().id);
33733         }else if (!preservePanel){
33734             this.bodyEl.dom.removeChild(panel.getEl().dom);
33735         }
33736         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
33737             var p = this.panels.first();
33738             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
33739             tempEl.appendChild(p.getEl().dom);
33740             this.bodyEl.update("");
33741             this.bodyEl.dom.appendChild(p.getEl().dom);
33742             tempEl = null;
33743             this.updateTitle(p.getTitle());
33744             this.tabs = null;
33745             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33746             this.setActivePanel(p);
33747         }
33748         panel.setRegion(null);
33749         if(this.activePanel == panel){
33750             this.activePanel = null;
33751         }
33752         if(this.config.autoDestroy !== false && preservePanel !== true){
33753             try{panel.destroy();}catch(e){}
33754         }
33755         this.fireEvent("panelremoved", this, panel);
33756         return panel;
33757     },
33758
33759     /**
33760      * Returns the TabPanel component used by this region
33761      * @return {Roo.TabPanel}
33762      */
33763     getTabs : function(){
33764         return this.tabs;
33765     },
33766
33767     createTool : function(parentEl, className){
33768         var btn = Roo.DomHelper.append(parentEl, {
33769             tag: "div",
33770             cls: "x-layout-tools-button",
33771             children: [ {
33772                 tag: "div",
33773                 cls: "roo-layout-tools-button-inner " + className,
33774                 html: "&#160;"
33775             }]
33776         }, true);
33777         btn.addClassOnOver("roo-layout-tools-button-over");
33778         return btn;
33779     }
33780 });/*
33781  * Based on:
33782  * Ext JS Library 1.1.1
33783  * Copyright(c) 2006-2007, Ext JS, LLC.
33784  *
33785  * Originally Released Under LGPL - original licence link has changed is not relivant.
33786  *
33787  * Fork - LGPL
33788  * <script type="text/javascript">
33789  */
33790  
33791
33792
33793 /**
33794  * @class Roo.SplitLayoutRegion
33795  * @extends Roo.LayoutRegion
33796  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
33797  */
33798 Roo.bootstrap.layout.Split = function(config){
33799     this.cursor = config.cursor;
33800     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
33801 };
33802
33803 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
33804 {
33805     splitTip : "Drag to resize.",
33806     collapsibleSplitTip : "Drag to resize. Double click to hide.",
33807     useSplitTips : false,
33808
33809     applyConfig : function(config){
33810         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
33811     },
33812     
33813     onRender : function(ctr,pos) {
33814         
33815         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
33816         if(!this.config.split){
33817             return;
33818         }
33819         if(!this.split){
33820             
33821             var splitEl = Roo.DomHelper.append(ctr.dom,  {
33822                             tag: "div",
33823                             id: this.el.id + "-split",
33824                             cls: "roo-layout-split roo-layout-split-"+this.position,
33825                             html: "&#160;"
33826             });
33827             /** The SplitBar for this region 
33828             * @type Roo.SplitBar */
33829             // does not exist yet...
33830             Roo.log([this.position, this.orientation]);
33831             
33832             this.split = new Roo.bootstrap.SplitBar({
33833                 dragElement : splitEl,
33834                 resizingElement: this.el,
33835                 orientation : this.orientation
33836             });
33837             
33838             this.split.on("moved", this.onSplitMove, this);
33839             this.split.useShim = this.config.useShim === true;
33840             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
33841             if(this.useSplitTips){
33842                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
33843             }
33844             //if(config.collapsible){
33845             //    this.split.el.on("dblclick", this.collapse,  this);
33846             //}
33847         }
33848         if(typeof this.config.minSize != "undefined"){
33849             this.split.minSize = this.config.minSize;
33850         }
33851         if(typeof this.config.maxSize != "undefined"){
33852             this.split.maxSize = this.config.maxSize;
33853         }
33854         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
33855             this.hideSplitter();
33856         }
33857         
33858     },
33859
33860     getHMaxSize : function(){
33861          var cmax = this.config.maxSize || 10000;
33862          var center = this.mgr.getRegion("center");
33863          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
33864     },
33865
33866     getVMaxSize : function(){
33867          var cmax = this.config.maxSize || 10000;
33868          var center = this.mgr.getRegion("center");
33869          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
33870     },
33871
33872     onSplitMove : function(split, newSize){
33873         this.fireEvent("resized", this, newSize);
33874     },
33875     
33876     /** 
33877      * Returns the {@link Roo.SplitBar} for this region.
33878      * @return {Roo.SplitBar}
33879      */
33880     getSplitBar : function(){
33881         return this.split;
33882     },
33883     
33884     hide : function(){
33885         this.hideSplitter();
33886         Roo.bootstrap.layout.Split.superclass.hide.call(this);
33887     },
33888
33889     hideSplitter : function(){
33890         if(this.split){
33891             this.split.el.setLocation(-2000,-2000);
33892             this.split.el.hide();
33893         }
33894     },
33895
33896     show : function(){
33897         if(this.split){
33898             this.split.el.show();
33899         }
33900         Roo.bootstrap.layout.Split.superclass.show.call(this);
33901     },
33902     
33903     beforeSlide: function(){
33904         if(Roo.isGecko){// firefox overflow auto bug workaround
33905             this.bodyEl.clip();
33906             if(this.tabs) {
33907                 this.tabs.bodyEl.clip();
33908             }
33909             if(this.activePanel){
33910                 this.activePanel.getEl().clip();
33911                 
33912                 if(this.activePanel.beforeSlide){
33913                     this.activePanel.beforeSlide();
33914                 }
33915             }
33916         }
33917     },
33918     
33919     afterSlide : function(){
33920         if(Roo.isGecko){// firefox overflow auto bug workaround
33921             this.bodyEl.unclip();
33922             if(this.tabs) {
33923                 this.tabs.bodyEl.unclip();
33924             }
33925             if(this.activePanel){
33926                 this.activePanel.getEl().unclip();
33927                 if(this.activePanel.afterSlide){
33928                     this.activePanel.afterSlide();
33929                 }
33930             }
33931         }
33932     },
33933
33934     initAutoHide : function(){
33935         if(this.autoHide !== false){
33936             if(!this.autoHideHd){
33937                 var st = new Roo.util.DelayedTask(this.slideIn, this);
33938                 this.autoHideHd = {
33939                     "mouseout": function(e){
33940                         if(!e.within(this.el, true)){
33941                             st.delay(500);
33942                         }
33943                     },
33944                     "mouseover" : function(e){
33945                         st.cancel();
33946                     },
33947                     scope : this
33948                 };
33949             }
33950             this.el.on(this.autoHideHd);
33951         }
33952     },
33953
33954     clearAutoHide : function(){
33955         if(this.autoHide !== false){
33956             this.el.un("mouseout", this.autoHideHd.mouseout);
33957             this.el.un("mouseover", this.autoHideHd.mouseover);
33958         }
33959     },
33960
33961     clearMonitor : function(){
33962         Roo.get(document).un("click", this.slideInIf, this);
33963     },
33964
33965     // these names are backwards but not changed for compat
33966     slideOut : function(){
33967         if(this.isSlid || this.el.hasActiveFx()){
33968             return;
33969         }
33970         this.isSlid = true;
33971         if(this.collapseBtn){
33972             this.collapseBtn.hide();
33973         }
33974         this.closeBtnState = this.closeBtn.getStyle('display');
33975         this.closeBtn.hide();
33976         if(this.stickBtn){
33977             this.stickBtn.show();
33978         }
33979         this.el.show();
33980         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
33981         this.beforeSlide();
33982         this.el.setStyle("z-index", 10001);
33983         this.el.slideIn(this.getSlideAnchor(), {
33984             callback: function(){
33985                 this.afterSlide();
33986                 this.initAutoHide();
33987                 Roo.get(document).on("click", this.slideInIf, this);
33988                 this.fireEvent("slideshow", this);
33989             },
33990             scope: this,
33991             block: true
33992         });
33993     },
33994
33995     afterSlideIn : function(){
33996         this.clearAutoHide();
33997         this.isSlid = false;
33998         this.clearMonitor();
33999         this.el.setStyle("z-index", "");
34000         if(this.collapseBtn){
34001             this.collapseBtn.show();
34002         }
34003         this.closeBtn.setStyle('display', this.closeBtnState);
34004         if(this.stickBtn){
34005             this.stickBtn.hide();
34006         }
34007         this.fireEvent("slidehide", this);
34008     },
34009
34010     slideIn : function(cb){
34011         if(!this.isSlid || this.el.hasActiveFx()){
34012             Roo.callback(cb);
34013             return;
34014         }
34015         this.isSlid = false;
34016         this.beforeSlide();
34017         this.el.slideOut(this.getSlideAnchor(), {
34018             callback: function(){
34019                 this.el.setLeftTop(-10000, -10000);
34020                 this.afterSlide();
34021                 this.afterSlideIn();
34022                 Roo.callback(cb);
34023             },
34024             scope: this,
34025             block: true
34026         });
34027     },
34028     
34029     slideInIf : function(e){
34030         if(!e.within(this.el)){
34031             this.slideIn();
34032         }
34033     },
34034
34035     animateCollapse : function(){
34036         this.beforeSlide();
34037         this.el.setStyle("z-index", 20000);
34038         var anchor = this.getSlideAnchor();
34039         this.el.slideOut(anchor, {
34040             callback : function(){
34041                 this.el.setStyle("z-index", "");
34042                 this.collapsedEl.slideIn(anchor, {duration:.3});
34043                 this.afterSlide();
34044                 this.el.setLocation(-10000,-10000);
34045                 this.el.hide();
34046                 this.fireEvent("collapsed", this);
34047             },
34048             scope: this,
34049             block: true
34050         });
34051     },
34052
34053     animateExpand : function(){
34054         this.beforeSlide();
34055         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34056         this.el.setStyle("z-index", 20000);
34057         this.collapsedEl.hide({
34058             duration:.1
34059         });
34060         this.el.slideIn(this.getSlideAnchor(), {
34061             callback : function(){
34062                 this.el.setStyle("z-index", "");
34063                 this.afterSlide();
34064                 if(this.split){
34065                     this.split.el.show();
34066                 }
34067                 this.fireEvent("invalidated", this);
34068                 this.fireEvent("expanded", this);
34069             },
34070             scope: this,
34071             block: true
34072         });
34073     },
34074
34075     anchors : {
34076         "west" : "left",
34077         "east" : "right",
34078         "north" : "top",
34079         "south" : "bottom"
34080     },
34081
34082     sanchors : {
34083         "west" : "l",
34084         "east" : "r",
34085         "north" : "t",
34086         "south" : "b"
34087     },
34088
34089     canchors : {
34090         "west" : "tl-tr",
34091         "east" : "tr-tl",
34092         "north" : "tl-bl",
34093         "south" : "bl-tl"
34094     },
34095
34096     getAnchor : function(){
34097         return this.anchors[this.position];
34098     },
34099
34100     getCollapseAnchor : function(){
34101         return this.canchors[this.position];
34102     },
34103
34104     getSlideAnchor : function(){
34105         return this.sanchors[this.position];
34106     },
34107
34108     getAlignAdj : function(){
34109         var cm = this.cmargins;
34110         switch(this.position){
34111             case "west":
34112                 return [0, 0];
34113             break;
34114             case "east":
34115                 return [0, 0];
34116             break;
34117             case "north":
34118                 return [0, 0];
34119             break;
34120             case "south":
34121                 return [0, 0];
34122             break;
34123         }
34124     },
34125
34126     getExpandAdj : function(){
34127         var c = this.collapsedEl, cm = this.cmargins;
34128         switch(this.position){
34129             case "west":
34130                 return [-(cm.right+c.getWidth()+cm.left), 0];
34131             break;
34132             case "east":
34133                 return [cm.right+c.getWidth()+cm.left, 0];
34134             break;
34135             case "north":
34136                 return [0, -(cm.top+cm.bottom+c.getHeight())];
34137             break;
34138             case "south":
34139                 return [0, cm.top+cm.bottom+c.getHeight()];
34140             break;
34141         }
34142     }
34143 });/*
34144  * Based on:
34145  * Ext JS Library 1.1.1
34146  * Copyright(c) 2006-2007, Ext JS, LLC.
34147  *
34148  * Originally Released Under LGPL - original licence link has changed is not relivant.
34149  *
34150  * Fork - LGPL
34151  * <script type="text/javascript">
34152  */
34153 /*
34154  * These classes are private internal classes
34155  */
34156 Roo.bootstrap.layout.Center = function(config){
34157     config.region = "center";
34158     Roo.bootstrap.layout.Region.call(this, config);
34159     this.visible = true;
34160     this.minWidth = config.minWidth || 20;
34161     this.minHeight = config.minHeight || 20;
34162 };
34163
34164 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
34165     hide : function(){
34166         // center panel can't be hidden
34167     },
34168     
34169     show : function(){
34170         // center panel can't be hidden
34171     },
34172     
34173     getMinWidth: function(){
34174         return this.minWidth;
34175     },
34176     
34177     getMinHeight: function(){
34178         return this.minHeight;
34179     }
34180 });
34181
34182
34183
34184
34185  
34186
34187
34188
34189
34190
34191 Roo.bootstrap.layout.North = function(config)
34192 {
34193     config.region = 'north';
34194     config.cursor = 'n-resize';
34195     
34196     Roo.bootstrap.layout.Split.call(this, config);
34197     
34198     
34199     if(this.split){
34200         this.split.placement = Roo.bootstrap.SplitBar.TOP;
34201         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
34202         this.split.el.addClass("roo-layout-split-v");
34203     }
34204     var size = config.initialSize || config.height;
34205     if(typeof size != "undefined"){
34206         this.el.setHeight(size);
34207     }
34208 };
34209 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
34210 {
34211     orientation: Roo.bootstrap.SplitBar.VERTICAL,
34212     
34213     
34214     
34215     getBox : function(){
34216         if(this.collapsed){
34217             return this.collapsedEl.getBox();
34218         }
34219         var box = this.el.getBox();
34220         if(this.split){
34221             box.height += this.split.el.getHeight();
34222         }
34223         return box;
34224     },
34225     
34226     updateBox : function(box){
34227         if(this.split && !this.collapsed){
34228             box.height -= this.split.el.getHeight();
34229             this.split.el.setLeft(box.x);
34230             this.split.el.setTop(box.y+box.height);
34231             this.split.el.setWidth(box.width);
34232         }
34233         if(this.collapsed){
34234             this.updateBody(box.width, null);
34235         }
34236         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34237     }
34238 });
34239
34240
34241
34242
34243
34244 Roo.bootstrap.layout.South = function(config){
34245     config.region = 'south';
34246     config.cursor = 's-resize';
34247     Roo.bootstrap.layout.Split.call(this, config);
34248     if(this.split){
34249         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
34250         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
34251         this.split.el.addClass("roo-layout-split-v");
34252     }
34253     var size = config.initialSize || config.height;
34254     if(typeof size != "undefined"){
34255         this.el.setHeight(size);
34256     }
34257 };
34258
34259 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
34260     orientation: Roo.bootstrap.SplitBar.VERTICAL,
34261     getBox : function(){
34262         if(this.collapsed){
34263             return this.collapsedEl.getBox();
34264         }
34265         var box = this.el.getBox();
34266         if(this.split){
34267             var sh = this.split.el.getHeight();
34268             box.height += sh;
34269             box.y -= sh;
34270         }
34271         return box;
34272     },
34273     
34274     updateBox : function(box){
34275         if(this.split && !this.collapsed){
34276             var sh = this.split.el.getHeight();
34277             box.height -= sh;
34278             box.y += sh;
34279             this.split.el.setLeft(box.x);
34280             this.split.el.setTop(box.y-sh);
34281             this.split.el.setWidth(box.width);
34282         }
34283         if(this.collapsed){
34284             this.updateBody(box.width, null);
34285         }
34286         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34287     }
34288 });
34289
34290 Roo.bootstrap.layout.East = function(config){
34291     config.region = "east";
34292     config.cursor = "e-resize";
34293     Roo.bootstrap.layout.Split.call(this, config);
34294     if(this.split){
34295         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
34296         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
34297         this.split.el.addClass("roo-layout-split-h");
34298     }
34299     var size = config.initialSize || config.width;
34300     if(typeof size != "undefined"){
34301         this.el.setWidth(size);
34302     }
34303 };
34304 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
34305     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
34306     getBox : function(){
34307         if(this.collapsed){
34308             return this.collapsedEl.getBox();
34309         }
34310         var box = this.el.getBox();
34311         if(this.split){
34312             var sw = this.split.el.getWidth();
34313             box.width += sw;
34314             box.x -= sw;
34315         }
34316         return box;
34317     },
34318
34319     updateBox : function(box){
34320         if(this.split && !this.collapsed){
34321             var sw = this.split.el.getWidth();
34322             box.width -= sw;
34323             this.split.el.setLeft(box.x);
34324             this.split.el.setTop(box.y);
34325             this.split.el.setHeight(box.height);
34326             box.x += sw;
34327         }
34328         if(this.collapsed){
34329             this.updateBody(null, box.height);
34330         }
34331         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34332     }
34333 });
34334
34335 Roo.bootstrap.layout.West = function(config){
34336     config.region = "west";
34337     config.cursor = "w-resize";
34338     
34339     Roo.bootstrap.layout.Split.call(this, config);
34340     if(this.split){
34341         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
34342         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
34343         this.split.el.addClass("roo-layout-split-h");
34344     }
34345     
34346 };
34347 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
34348     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
34349     
34350     onRender: function(ctr, pos)
34351     {
34352         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
34353         var size = this.config.initialSize || this.config.width;
34354         if(typeof size != "undefined"){
34355             this.el.setWidth(size);
34356         }
34357     },
34358     
34359     getBox : function(){
34360         if(this.collapsed){
34361             return this.collapsedEl.getBox();
34362         }
34363         var box = this.el.getBox();
34364         if(this.split){
34365             box.width += this.split.el.getWidth();
34366         }
34367         return box;
34368     },
34369     
34370     updateBox : function(box){
34371         if(this.split && !this.collapsed){
34372             var sw = this.split.el.getWidth();
34373             box.width -= sw;
34374             this.split.el.setLeft(box.x+box.width);
34375             this.split.el.setTop(box.y);
34376             this.split.el.setHeight(box.height);
34377         }
34378         if(this.collapsed){
34379             this.updateBody(null, box.height);
34380         }
34381         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34382     }
34383 });
34384 Roo.namespace("Roo.bootstrap.panel");/*
34385  * Based on:
34386  * Ext JS Library 1.1.1
34387  * Copyright(c) 2006-2007, Ext JS, LLC.
34388  *
34389  * Originally Released Under LGPL - original licence link has changed is not relivant.
34390  *
34391  * Fork - LGPL
34392  * <script type="text/javascript">
34393  */
34394 /**
34395  * @class Roo.ContentPanel
34396  * @extends Roo.util.Observable
34397  * A basic ContentPanel element.
34398  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
34399  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
34400  * @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
34401  * @cfg {Boolean}   closable      True if the panel can be closed/removed
34402  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
34403  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
34404  * @cfg {Toolbar}   toolbar       A toolbar for this panel
34405  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
34406  * @cfg {String} title          The title for this panel
34407  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
34408  * @cfg {String} url            Calls {@link #setUrl} with this value
34409  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
34410  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
34411  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
34412  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
34413  * @cfg {Boolean} badges render the badges
34414
34415  * @constructor
34416  * Create a new ContentPanel.
34417  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
34418  * @param {String/Object} config A string to set only the title or a config object
34419  * @param {String} content (optional) Set the HTML content for this panel
34420  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
34421  */
34422 Roo.bootstrap.panel.Content = function( config){
34423     
34424     this.tpl = config.tpl || false;
34425     
34426     var el = config.el;
34427     var content = config.content;
34428
34429     if(config.autoCreate){ // xtype is available if this is called from factory
34430         el = Roo.id();
34431     }
34432     this.el = Roo.get(el);
34433     if(!this.el && config && config.autoCreate){
34434         if(typeof config.autoCreate == "object"){
34435             if(!config.autoCreate.id){
34436                 config.autoCreate.id = config.id||el;
34437             }
34438             this.el = Roo.DomHelper.append(document.body,
34439                         config.autoCreate, true);
34440         }else{
34441             var elcfg =  {   tag: "div",
34442                             cls: "roo-layout-inactive-content",
34443                             id: config.id||el
34444                             };
34445             if (config.html) {
34446                 elcfg.html = config.html;
34447                 
34448             }
34449                         
34450             this.el = Roo.DomHelper.append(document.body, elcfg , true);
34451         }
34452     } 
34453     this.closable = false;
34454     this.loaded = false;
34455     this.active = false;
34456    
34457       
34458     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
34459         
34460         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
34461         
34462         this.wrapEl = this.el; //this.el.wrap();
34463         var ti = [];
34464         if (config.toolbar.items) {
34465             ti = config.toolbar.items ;
34466             delete config.toolbar.items ;
34467         }
34468         
34469         var nitems = [];
34470         this.toolbar.render(this.wrapEl, 'before');
34471         for(var i =0;i < ti.length;i++) {
34472           //  Roo.log(['add child', items[i]]);
34473             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34474         }
34475         this.toolbar.items = nitems;
34476         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
34477         delete config.toolbar;
34478         
34479     }
34480     /*
34481     // xtype created footer. - not sure if will work as we normally have to render first..
34482     if (this.footer && !this.footer.el && this.footer.xtype) {
34483         if (!this.wrapEl) {
34484             this.wrapEl = this.el.wrap();
34485         }
34486     
34487         this.footer.container = this.wrapEl.createChild();
34488          
34489         this.footer = Roo.factory(this.footer, Roo);
34490         
34491     }
34492     */
34493     
34494      if(typeof config == "string"){
34495         this.title = config;
34496     }else{
34497         Roo.apply(this, config);
34498     }
34499     
34500     if(this.resizeEl){
34501         this.resizeEl = Roo.get(this.resizeEl, true);
34502     }else{
34503         this.resizeEl = this.el;
34504     }
34505     // handle view.xtype
34506     
34507  
34508     
34509     
34510     this.addEvents({
34511         /**
34512          * @event activate
34513          * Fires when this panel is activated. 
34514          * @param {Roo.ContentPanel} this
34515          */
34516         "activate" : true,
34517         /**
34518          * @event deactivate
34519          * Fires when this panel is activated. 
34520          * @param {Roo.ContentPanel} this
34521          */
34522         "deactivate" : true,
34523
34524         /**
34525          * @event resize
34526          * Fires when this panel is resized if fitToFrame is true.
34527          * @param {Roo.ContentPanel} this
34528          * @param {Number} width The width after any component adjustments
34529          * @param {Number} height The height after any component adjustments
34530          */
34531         "resize" : true,
34532         
34533          /**
34534          * @event render
34535          * Fires when this tab is created
34536          * @param {Roo.ContentPanel} this
34537          */
34538         "render" : true
34539         
34540         
34541         
34542     });
34543     
34544
34545     
34546     
34547     if(this.autoScroll){
34548         this.resizeEl.setStyle("overflow", "auto");
34549     } else {
34550         // fix randome scrolling
34551         //this.el.on('scroll', function() {
34552         //    Roo.log('fix random scolling');
34553         //    this.scrollTo('top',0); 
34554         //});
34555     }
34556     content = content || this.content;
34557     if(content){
34558         this.setContent(content);
34559     }
34560     if(config && config.url){
34561         this.setUrl(this.url, this.params, this.loadOnce);
34562     }
34563     
34564     
34565     
34566     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
34567     
34568     if (this.view && typeof(this.view.xtype) != 'undefined') {
34569         this.view.el = this.el.appendChild(document.createElement("div"));
34570         this.view = Roo.factory(this.view); 
34571         this.view.render  &&  this.view.render(false, '');  
34572     }
34573     
34574     
34575     this.fireEvent('render', this);
34576 };
34577
34578 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
34579     
34580     tabTip : '',
34581     
34582     setRegion : function(region){
34583         this.region = region;
34584         this.setActiveClass(region && !this.background);
34585     },
34586     
34587     
34588     setActiveClass: function(state)
34589     {
34590         if(state){
34591            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
34592            this.el.setStyle('position','relative');
34593         }else{
34594            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
34595            this.el.setStyle('position', 'absolute');
34596         } 
34597     },
34598     
34599     /**
34600      * Returns the toolbar for this Panel if one was configured. 
34601      * @return {Roo.Toolbar} 
34602      */
34603     getToolbar : function(){
34604         return this.toolbar;
34605     },
34606     
34607     setActiveState : function(active)
34608     {
34609         this.active = active;
34610         this.setActiveClass(active);
34611         if(!active){
34612             this.fireEvent("deactivate", this);
34613         }else{
34614             this.fireEvent("activate", this);
34615         }
34616     },
34617     /**
34618      * Updates this panel's element
34619      * @param {String} content The new content
34620      * @param {Boolean} loadScripts (optional) true to look for and process scripts
34621     */
34622     setContent : function(content, loadScripts){
34623         this.el.update(content, loadScripts);
34624     },
34625
34626     ignoreResize : function(w, h){
34627         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
34628             return true;
34629         }else{
34630             this.lastSize = {width: w, height: h};
34631             return false;
34632         }
34633     },
34634     /**
34635      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
34636      * @return {Roo.UpdateManager} The UpdateManager
34637      */
34638     getUpdateManager : function(){
34639         return this.el.getUpdateManager();
34640     },
34641      /**
34642      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
34643      * @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:
34644 <pre><code>
34645 panel.load({
34646     url: "your-url.php",
34647     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
34648     callback: yourFunction,
34649     scope: yourObject, //(optional scope)
34650     discardUrl: false,
34651     nocache: false,
34652     text: "Loading...",
34653     timeout: 30,
34654     scripts: false
34655 });
34656 </code></pre>
34657      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
34658      * 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.
34659      * @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}
34660      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
34661      * @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.
34662      * @return {Roo.ContentPanel} this
34663      */
34664     load : function(){
34665         var um = this.el.getUpdateManager();
34666         um.update.apply(um, arguments);
34667         return this;
34668     },
34669
34670
34671     /**
34672      * 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.
34673      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
34674      * @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)
34675      * @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)
34676      * @return {Roo.UpdateManager} The UpdateManager
34677      */
34678     setUrl : function(url, params, loadOnce){
34679         if(this.refreshDelegate){
34680             this.removeListener("activate", this.refreshDelegate);
34681         }
34682         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34683         this.on("activate", this.refreshDelegate);
34684         return this.el.getUpdateManager();
34685     },
34686     
34687     _handleRefresh : function(url, params, loadOnce){
34688         if(!loadOnce || !this.loaded){
34689             var updater = this.el.getUpdateManager();
34690             updater.update(url, params, this._setLoaded.createDelegate(this));
34691         }
34692     },
34693     
34694     _setLoaded : function(){
34695         this.loaded = true;
34696     }, 
34697     
34698     /**
34699      * Returns this panel's id
34700      * @return {String} 
34701      */
34702     getId : function(){
34703         return this.el.id;
34704     },
34705     
34706     /** 
34707      * Returns this panel's element - used by regiosn to add.
34708      * @return {Roo.Element} 
34709      */
34710     getEl : function(){
34711         return this.wrapEl || this.el;
34712     },
34713     
34714    
34715     
34716     adjustForComponents : function(width, height)
34717     {
34718         //Roo.log('adjustForComponents ');
34719         if(this.resizeEl != this.el){
34720             width -= this.el.getFrameWidth('lr');
34721             height -= this.el.getFrameWidth('tb');
34722         }
34723         if(this.toolbar){
34724             var te = this.toolbar.getEl();
34725             height -= te.getHeight();
34726             te.setWidth(width);
34727         }
34728         if(this.footer){
34729             var te = this.footer.getEl();
34730             Roo.log("footer:" + te.getHeight());
34731             
34732             height -= te.getHeight();
34733             te.setWidth(width);
34734         }
34735         
34736         
34737         if(this.adjustments){
34738             width += this.adjustments[0];
34739             height += this.adjustments[1];
34740         }
34741         return {"width": width, "height": height};
34742     },
34743     
34744     setSize : function(width, height){
34745         if(this.fitToFrame && !this.ignoreResize(width, height)){
34746             if(this.fitContainer && this.resizeEl != this.el){
34747                 this.el.setSize(width, height);
34748             }
34749             var size = this.adjustForComponents(width, height);
34750             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
34751             this.fireEvent('resize', this, size.width, size.height);
34752         }
34753     },
34754     
34755     /**
34756      * Returns this panel's title
34757      * @return {String} 
34758      */
34759     getTitle : function(){
34760         return this.title;
34761     },
34762     
34763     /**
34764      * Set this panel's title
34765      * @param {String} title
34766      */
34767     setTitle : function(title){
34768         this.title = title;
34769         if(this.region){
34770             this.region.updatePanelTitle(this, title);
34771         }
34772     },
34773     
34774     /**
34775      * Returns true is this panel was configured to be closable
34776      * @return {Boolean} 
34777      */
34778     isClosable : function(){
34779         return this.closable;
34780     },
34781     
34782     beforeSlide : function(){
34783         this.el.clip();
34784         this.resizeEl.clip();
34785     },
34786     
34787     afterSlide : function(){
34788         this.el.unclip();
34789         this.resizeEl.unclip();
34790     },
34791     
34792     /**
34793      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
34794      *   Will fail silently if the {@link #setUrl} method has not been called.
34795      *   This does not activate the panel, just updates its content.
34796      */
34797     refresh : function(){
34798         if(this.refreshDelegate){
34799            this.loaded = false;
34800            this.refreshDelegate();
34801         }
34802     },
34803     
34804     /**
34805      * Destroys this panel
34806      */
34807     destroy : function(){
34808         this.el.removeAllListeners();
34809         var tempEl = document.createElement("span");
34810         tempEl.appendChild(this.el.dom);
34811         tempEl.innerHTML = "";
34812         this.el.remove();
34813         this.el = null;
34814     },
34815     
34816     /**
34817      * form - if the content panel contains a form - this is a reference to it.
34818      * @type {Roo.form.Form}
34819      */
34820     form : false,
34821     /**
34822      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
34823      *    This contains a reference to it.
34824      * @type {Roo.View}
34825      */
34826     view : false,
34827     
34828       /**
34829      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
34830      * <pre><code>
34831
34832 layout.addxtype({
34833        xtype : 'Form',
34834        items: [ .... ]
34835    }
34836 );
34837
34838 </code></pre>
34839      * @param {Object} cfg Xtype definition of item to add.
34840      */
34841     
34842     
34843     getChildContainer: function () {
34844         return this.getEl();
34845     }
34846     
34847     
34848     /*
34849         var  ret = new Roo.factory(cfg);
34850         return ret;
34851         
34852         
34853         // add form..
34854         if (cfg.xtype.match(/^Form$/)) {
34855             
34856             var el;
34857             //if (this.footer) {
34858             //    el = this.footer.container.insertSibling(false, 'before');
34859             //} else {
34860                 el = this.el.createChild();
34861             //}
34862
34863             this.form = new  Roo.form.Form(cfg);
34864             
34865             
34866             if ( this.form.allItems.length) {
34867                 this.form.render(el.dom);
34868             }
34869             return this.form;
34870         }
34871         // should only have one of theses..
34872         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
34873             // views.. should not be just added - used named prop 'view''
34874             
34875             cfg.el = this.el.appendChild(document.createElement("div"));
34876             // factory?
34877             
34878             var ret = new Roo.factory(cfg);
34879              
34880              ret.render && ret.render(false, ''); // render blank..
34881             this.view = ret;
34882             return ret;
34883         }
34884         return false;
34885     }
34886     \*/
34887 });
34888  
34889 /**
34890  * @class Roo.bootstrap.panel.Grid
34891  * @extends Roo.bootstrap.panel.Content
34892  * @constructor
34893  * Create a new GridPanel.
34894  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
34895  * @param {Object} config A the config object
34896   
34897  */
34898
34899
34900
34901 Roo.bootstrap.panel.Grid = function(config)
34902 {
34903     
34904       
34905     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
34906         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
34907
34908     config.el = this.wrapper;
34909     //this.el = this.wrapper;
34910     
34911       if (config.container) {
34912         // ctor'ed from a Border/panel.grid
34913         
34914         
34915         this.wrapper.setStyle("overflow", "hidden");
34916         this.wrapper.addClass('roo-grid-container');
34917
34918     }
34919     
34920     
34921     if(config.toolbar){
34922         var tool_el = this.wrapper.createChild();    
34923         this.toolbar = Roo.factory(config.toolbar);
34924         var ti = [];
34925         if (config.toolbar.items) {
34926             ti = config.toolbar.items ;
34927             delete config.toolbar.items ;
34928         }
34929         
34930         var nitems = [];
34931         this.toolbar.render(tool_el);
34932         for(var i =0;i < ti.length;i++) {
34933           //  Roo.log(['add child', items[i]]);
34934             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34935         }
34936         this.toolbar.items = nitems;
34937         
34938         delete config.toolbar;
34939     }
34940     
34941     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
34942     config.grid.scrollBody = true;;
34943     config.grid.monitorWindowResize = false; // turn off autosizing
34944     config.grid.autoHeight = false;
34945     config.grid.autoWidth = false;
34946     
34947     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
34948     
34949     if (config.background) {
34950         // render grid on panel activation (if panel background)
34951         this.on('activate', function(gp) {
34952             if (!gp.grid.rendered) {
34953                 gp.grid.render(this.wrapper);
34954                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
34955             }
34956         });
34957             
34958     } else {
34959         this.grid.render(this.wrapper);
34960         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
34961
34962     }
34963     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
34964     // ??? needed ??? config.el = this.wrapper;
34965     
34966     
34967     
34968   
34969     // xtype created footer. - not sure if will work as we normally have to render first..
34970     if (this.footer && !this.footer.el && this.footer.xtype) {
34971         
34972         var ctr = this.grid.getView().getFooterPanel(true);
34973         this.footer.dataSource = this.grid.dataSource;
34974         this.footer = Roo.factory(this.footer, Roo);
34975         this.footer.render(ctr);
34976         
34977     }
34978     
34979     
34980     
34981     
34982      
34983 };
34984
34985 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
34986     getId : function(){
34987         return this.grid.id;
34988     },
34989     
34990     /**
34991      * Returns the grid for this panel
34992      * @return {Roo.bootstrap.Table} 
34993      */
34994     getGrid : function(){
34995         return this.grid;    
34996     },
34997     
34998     setSize : function(width, height){
34999         if(!this.ignoreResize(width, height)){
35000             var grid = this.grid;
35001             var size = this.adjustForComponents(width, height);
35002             var gridel = grid.getGridEl();
35003             gridel.setSize(size.width, size.height);
35004             /*
35005             var thd = grid.getGridEl().select('thead',true).first();
35006             var tbd = grid.getGridEl().select('tbody', true).first();
35007             if (tbd) {
35008                 tbd.setSize(width, height - thd.getHeight());
35009             }
35010             */
35011             grid.autoSize();
35012         }
35013     },
35014      
35015     
35016     
35017     beforeSlide : function(){
35018         this.grid.getView().scroller.clip();
35019     },
35020     
35021     afterSlide : function(){
35022         this.grid.getView().scroller.unclip();
35023     },
35024     
35025     destroy : function(){
35026         this.grid.destroy();
35027         delete this.grid;
35028         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
35029     }
35030 });
35031
35032 /**
35033  * @class Roo.bootstrap.panel.Nest
35034  * @extends Roo.bootstrap.panel.Content
35035  * @constructor
35036  * Create a new Panel, that can contain a layout.Border.
35037  * 
35038  * 
35039  * @param {Roo.BorderLayout} layout The layout for this panel
35040  * @param {String/Object} config A string to set only the title or a config object
35041  */
35042 Roo.bootstrap.panel.Nest = function(config)
35043 {
35044     // construct with only one argument..
35045     /* FIXME - implement nicer consturctors
35046     if (layout.layout) {
35047         config = layout;
35048         layout = config.layout;
35049         delete config.layout;
35050     }
35051     if (layout.xtype && !layout.getEl) {
35052         // then layout needs constructing..
35053         layout = Roo.factory(layout, Roo);
35054     }
35055     */
35056     
35057     config.el =  config.layout.getEl();
35058     
35059     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
35060     
35061     config.layout.monitorWindowResize = false; // turn off autosizing
35062     this.layout = config.layout;
35063     this.layout.getEl().addClass("roo-layout-nested-layout");
35064     
35065     
35066     
35067     
35068 };
35069
35070 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
35071
35072     setSize : function(width, height){
35073         if(!this.ignoreResize(width, height)){
35074             var size = this.adjustForComponents(width, height);
35075             var el = this.layout.getEl();
35076             if (size.height < 1) {
35077                 el.setWidth(size.width);   
35078             } else {
35079                 el.setSize(size.width, size.height);
35080             }
35081             var touch = el.dom.offsetWidth;
35082             this.layout.layout();
35083             // ie requires a double layout on the first pass
35084             if(Roo.isIE && !this.initialized){
35085                 this.initialized = true;
35086                 this.layout.layout();
35087             }
35088         }
35089     },
35090     
35091     // activate all subpanels if not currently active..
35092     
35093     setActiveState : function(active){
35094         this.active = active;
35095         this.setActiveClass(active);
35096         
35097         if(!active){
35098             this.fireEvent("deactivate", this);
35099             return;
35100         }
35101         
35102         this.fireEvent("activate", this);
35103         // not sure if this should happen before or after..
35104         if (!this.layout) {
35105             return; // should not happen..
35106         }
35107         var reg = false;
35108         for (var r in this.layout.regions) {
35109             reg = this.layout.getRegion(r);
35110             if (reg.getActivePanel()) {
35111                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
35112                 reg.setActivePanel(reg.getActivePanel());
35113                 continue;
35114             }
35115             if (!reg.panels.length) {
35116                 continue;
35117             }
35118             reg.showPanel(reg.getPanel(0));
35119         }
35120         
35121         
35122         
35123         
35124     },
35125     
35126     /**
35127      * Returns the nested BorderLayout for this panel
35128      * @return {Roo.BorderLayout} 
35129      */
35130     getLayout : function(){
35131         return this.layout;
35132     },
35133     
35134      /**
35135      * Adds a xtype elements to the layout of the nested panel
35136      * <pre><code>
35137
35138 panel.addxtype({
35139        xtype : 'ContentPanel',
35140        region: 'west',
35141        items: [ .... ]
35142    }
35143 );
35144
35145 panel.addxtype({
35146         xtype : 'NestedLayoutPanel',
35147         region: 'west',
35148         layout: {
35149            center: { },
35150            west: { }   
35151         },
35152         items : [ ... list of content panels or nested layout panels.. ]
35153    }
35154 );
35155 </code></pre>
35156      * @param {Object} cfg Xtype definition of item to add.
35157      */
35158     addxtype : function(cfg) {
35159         return this.layout.addxtype(cfg);
35160     
35161     }
35162 });        /*
35163  * Based on:
35164  * Ext JS Library 1.1.1
35165  * Copyright(c) 2006-2007, Ext JS, LLC.
35166  *
35167  * Originally Released Under LGPL - original licence link has changed is not relivant.
35168  *
35169  * Fork - LGPL
35170  * <script type="text/javascript">
35171  */
35172 /**
35173  * @class Roo.TabPanel
35174  * @extends Roo.util.Observable
35175  * A lightweight tab container.
35176  * <br><br>
35177  * Usage:
35178  * <pre><code>
35179 // basic tabs 1, built from existing content
35180 var tabs = new Roo.TabPanel("tabs1");
35181 tabs.addTab("script", "View Script");
35182 tabs.addTab("markup", "View Markup");
35183 tabs.activate("script");
35184
35185 // more advanced tabs, built from javascript
35186 var jtabs = new Roo.TabPanel("jtabs");
35187 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
35188
35189 // set up the UpdateManager
35190 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
35191 var updater = tab2.getUpdateManager();
35192 updater.setDefaultUrl("ajax1.htm");
35193 tab2.on('activate', updater.refresh, updater, true);
35194
35195 // Use setUrl for Ajax loading
35196 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
35197 tab3.setUrl("ajax2.htm", null, true);
35198
35199 // Disabled tab
35200 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
35201 tab4.disable();
35202
35203 jtabs.activate("jtabs-1");
35204  * </code></pre>
35205  * @constructor
35206  * Create a new TabPanel.
35207  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
35208  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
35209  */
35210 Roo.bootstrap.panel.Tabs = function(config){
35211     /**
35212     * The container element for this TabPanel.
35213     * @type Roo.Element
35214     */
35215     this.el = Roo.get(config.el);
35216     delete config.el;
35217     if(config){
35218         if(typeof config == "boolean"){
35219             this.tabPosition = config ? "bottom" : "top";
35220         }else{
35221             Roo.apply(this, config);
35222         }
35223     }
35224     
35225     if(this.tabPosition == "bottom"){
35226         this.bodyEl = Roo.get(this.createBody(this.el.dom));
35227         this.el.addClass("roo-tabs-bottom");
35228     }
35229     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
35230     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
35231     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
35232     if(Roo.isIE){
35233         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
35234     }
35235     if(this.tabPosition != "bottom"){
35236         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
35237          * @type Roo.Element
35238          */
35239         this.bodyEl = Roo.get(this.createBody(this.el.dom));
35240         this.el.addClass("roo-tabs-top");
35241     }
35242     this.items = [];
35243
35244     this.bodyEl.setStyle("position", "relative");
35245
35246     this.active = null;
35247     this.activateDelegate = this.activate.createDelegate(this);
35248
35249     this.addEvents({
35250         /**
35251          * @event tabchange
35252          * Fires when the active tab changes
35253          * @param {Roo.TabPanel} this
35254          * @param {Roo.TabPanelItem} activePanel The new active tab
35255          */
35256         "tabchange": true,
35257         /**
35258          * @event beforetabchange
35259          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
35260          * @param {Roo.TabPanel} this
35261          * @param {Object} e Set cancel to true on this object to cancel the tab change
35262          * @param {Roo.TabPanelItem} tab The tab being changed to
35263          */
35264         "beforetabchange" : true
35265     });
35266
35267     Roo.EventManager.onWindowResize(this.onResize, this);
35268     this.cpad = this.el.getPadding("lr");
35269     this.hiddenCount = 0;
35270
35271
35272     // toolbar on the tabbar support...
35273     if (this.toolbar) {
35274         alert("no toolbar support yet");
35275         this.toolbar  = false;
35276         /*
35277         var tcfg = this.toolbar;
35278         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
35279         this.toolbar = new Roo.Toolbar(tcfg);
35280         if (Roo.isSafari) {
35281             var tbl = tcfg.container.child('table', true);
35282             tbl.setAttribute('width', '100%');
35283         }
35284         */
35285         
35286     }
35287    
35288
35289
35290     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
35291 };
35292
35293 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
35294     /*
35295      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
35296      */
35297     tabPosition : "top",
35298     /*
35299      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
35300      */
35301     currentTabWidth : 0,
35302     /*
35303      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
35304      */
35305     minTabWidth : 40,
35306     /*
35307      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
35308      */
35309     maxTabWidth : 250,
35310     /*
35311      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
35312      */
35313     preferredTabWidth : 175,
35314     /*
35315      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
35316      */
35317     resizeTabs : false,
35318     /*
35319      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
35320      */
35321     monitorResize : true,
35322     /*
35323      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
35324      */
35325     toolbar : false,
35326
35327     /**
35328      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
35329      * @param {String} id The id of the div to use <b>or create</b>
35330      * @param {String} text The text for the tab
35331      * @param {String} content (optional) Content to put in the TabPanelItem body
35332      * @param {Boolean} closable (optional) True to create a close icon on the tab
35333      * @return {Roo.TabPanelItem} The created TabPanelItem
35334      */
35335     addTab : function(id, text, content, closable, tpl)
35336     {
35337         var item = new Roo.bootstrap.panel.TabItem({
35338             panel: this,
35339             id : id,
35340             text : text,
35341             closable : closable,
35342             tpl : tpl
35343         });
35344         this.addTabItem(item);
35345         if(content){
35346             item.setContent(content);
35347         }
35348         return item;
35349     },
35350
35351     /**
35352      * Returns the {@link Roo.TabPanelItem} with the specified id/index
35353      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
35354      * @return {Roo.TabPanelItem}
35355      */
35356     getTab : function(id){
35357         return this.items[id];
35358     },
35359
35360     /**
35361      * Hides the {@link Roo.TabPanelItem} with the specified id/index
35362      * @param {String/Number} id The id or index of the TabPanelItem to hide.
35363      */
35364     hideTab : function(id){
35365         var t = this.items[id];
35366         if(!t.isHidden()){
35367            t.setHidden(true);
35368            this.hiddenCount++;
35369            this.autoSizeTabs();
35370         }
35371     },
35372
35373     /**
35374      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
35375      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
35376      */
35377     unhideTab : function(id){
35378         var t = this.items[id];
35379         if(t.isHidden()){
35380            t.setHidden(false);
35381            this.hiddenCount--;
35382            this.autoSizeTabs();
35383         }
35384     },
35385
35386     /**
35387      * Adds an existing {@link Roo.TabPanelItem}.
35388      * @param {Roo.TabPanelItem} item The TabPanelItem to add
35389      */
35390     addTabItem : function(item){
35391         this.items[item.id] = item;
35392         this.items.push(item);
35393       //  if(this.resizeTabs){
35394     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
35395   //         this.autoSizeTabs();
35396 //        }else{
35397 //            item.autoSize();
35398        // }
35399     },
35400
35401     /**
35402      * Removes a {@link Roo.TabPanelItem}.
35403      * @param {String/Number} id The id or index of the TabPanelItem to remove.
35404      */
35405     removeTab : function(id){
35406         var items = this.items;
35407         var tab = items[id];
35408         if(!tab) { return; }
35409         var index = items.indexOf(tab);
35410         if(this.active == tab && items.length > 1){
35411             var newTab = this.getNextAvailable(index);
35412             if(newTab) {
35413                 newTab.activate();
35414             }
35415         }
35416         this.stripEl.dom.removeChild(tab.pnode.dom);
35417         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
35418             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
35419         }
35420         items.splice(index, 1);
35421         delete this.items[tab.id];
35422         tab.fireEvent("close", tab);
35423         tab.purgeListeners();
35424         this.autoSizeTabs();
35425     },
35426
35427     getNextAvailable : function(start){
35428         var items = this.items;
35429         var index = start;
35430         // look for a next tab that will slide over to
35431         // replace the one being removed
35432         while(index < items.length){
35433             var item = items[++index];
35434             if(item && !item.isHidden()){
35435                 return item;
35436             }
35437         }
35438         // if one isn't found select the previous tab (on the left)
35439         index = start;
35440         while(index >= 0){
35441             var item = items[--index];
35442             if(item && !item.isHidden()){
35443                 return item;
35444             }
35445         }
35446         return null;
35447     },
35448
35449     /**
35450      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
35451      * @param {String/Number} id The id or index of the TabPanelItem to disable.
35452      */
35453     disableTab : function(id){
35454         var tab = this.items[id];
35455         if(tab && this.active != tab){
35456             tab.disable();
35457         }
35458     },
35459
35460     /**
35461      * Enables a {@link Roo.TabPanelItem} that is disabled.
35462      * @param {String/Number} id The id or index of the TabPanelItem to enable.
35463      */
35464     enableTab : function(id){
35465         var tab = this.items[id];
35466         tab.enable();
35467     },
35468
35469     /**
35470      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
35471      * @param {String/Number} id The id or index of the TabPanelItem to activate.
35472      * @return {Roo.TabPanelItem} The TabPanelItem.
35473      */
35474     activate : function(id){
35475         var tab = this.items[id];
35476         if(!tab){
35477             return null;
35478         }
35479         if(tab == this.active || tab.disabled){
35480             return tab;
35481         }
35482         var e = {};
35483         this.fireEvent("beforetabchange", this, e, tab);
35484         if(e.cancel !== true && !tab.disabled){
35485             if(this.active){
35486                 this.active.hide();
35487             }
35488             this.active = this.items[id];
35489             this.active.show();
35490             this.fireEvent("tabchange", this, this.active);
35491         }
35492         return tab;
35493     },
35494
35495     /**
35496      * Gets the active {@link Roo.TabPanelItem}.
35497      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
35498      */
35499     getActiveTab : function(){
35500         return this.active;
35501     },
35502
35503     /**
35504      * Updates the tab body element to fit the height of the container element
35505      * for overflow scrolling
35506      * @param {Number} targetHeight (optional) Override the starting height from the elements height
35507      */
35508     syncHeight : function(targetHeight){
35509         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35510         var bm = this.bodyEl.getMargins();
35511         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
35512         this.bodyEl.setHeight(newHeight);
35513         return newHeight;
35514     },
35515
35516     onResize : function(){
35517         if(this.monitorResize){
35518             this.autoSizeTabs();
35519         }
35520     },
35521
35522     /**
35523      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
35524      */
35525     beginUpdate : function(){
35526         this.updating = true;
35527     },
35528
35529     /**
35530      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
35531      */
35532     endUpdate : function(){
35533         this.updating = false;
35534         this.autoSizeTabs();
35535     },
35536
35537     /**
35538      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
35539      */
35540     autoSizeTabs : function(){
35541         var count = this.items.length;
35542         var vcount = count - this.hiddenCount;
35543         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
35544             return;
35545         }
35546         var w = Math.max(this.el.getWidth() - this.cpad, 10);
35547         var availWidth = Math.floor(w / vcount);
35548         var b = this.stripBody;
35549         if(b.getWidth() > w){
35550             var tabs = this.items;
35551             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
35552             if(availWidth < this.minTabWidth){
35553                 /*if(!this.sleft){    // incomplete scrolling code
35554                     this.createScrollButtons();
35555                 }
35556                 this.showScroll();
35557                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
35558             }
35559         }else{
35560             if(this.currentTabWidth < this.preferredTabWidth){
35561                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
35562             }
35563         }
35564     },
35565
35566     /**
35567      * Returns the number of tabs in this TabPanel.
35568      * @return {Number}
35569      */
35570      getCount : function(){
35571          return this.items.length;
35572      },
35573
35574     /**
35575      * Resizes all the tabs to the passed width
35576      * @param {Number} The new width
35577      */
35578     setTabWidth : function(width){
35579         this.currentTabWidth = width;
35580         for(var i = 0, len = this.items.length; i < len; i++) {
35581                 if(!this.items[i].isHidden()) {
35582                 this.items[i].setWidth(width);
35583             }
35584         }
35585     },
35586
35587     /**
35588      * Destroys this TabPanel
35589      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
35590      */
35591     destroy : function(removeEl){
35592         Roo.EventManager.removeResizeListener(this.onResize, this);
35593         for(var i = 0, len = this.items.length; i < len; i++){
35594             this.items[i].purgeListeners();
35595         }
35596         if(removeEl === true){
35597             this.el.update("");
35598             this.el.remove();
35599         }
35600     },
35601     
35602     createStrip : function(container)
35603     {
35604         var strip = document.createElement("nav");
35605         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
35606         container.appendChild(strip);
35607         return strip;
35608     },
35609     
35610     createStripList : function(strip)
35611     {
35612         // div wrapper for retard IE
35613         // returns the "tr" element.
35614         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
35615         //'<div class="x-tabs-strip-wrap">'+
35616           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
35617           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
35618         return strip.firstChild; //.firstChild.firstChild.firstChild;
35619     },
35620     createBody : function(container)
35621     {
35622         var body = document.createElement("div");
35623         Roo.id(body, "tab-body");
35624         //Roo.fly(body).addClass("x-tabs-body");
35625         Roo.fly(body).addClass("tab-content");
35626         container.appendChild(body);
35627         return body;
35628     },
35629     createItemBody :function(bodyEl, id){
35630         var body = Roo.getDom(id);
35631         if(!body){
35632             body = document.createElement("div");
35633             body.id = id;
35634         }
35635         //Roo.fly(body).addClass("x-tabs-item-body");
35636         Roo.fly(body).addClass("tab-pane");
35637          bodyEl.insertBefore(body, bodyEl.firstChild);
35638         return body;
35639     },
35640     /** @private */
35641     createStripElements :  function(stripEl, text, closable, tpl)
35642     {
35643         var td = document.createElement("li"); // was td..
35644         
35645         
35646         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
35647         
35648         
35649         stripEl.appendChild(td);
35650         /*if(closable){
35651             td.className = "x-tabs-closable";
35652             if(!this.closeTpl){
35653                 this.closeTpl = new Roo.Template(
35654                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
35655                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
35656                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
35657                 );
35658             }
35659             var el = this.closeTpl.overwrite(td, {"text": text});
35660             var close = el.getElementsByTagName("div")[0];
35661             var inner = el.getElementsByTagName("em")[0];
35662             return {"el": el, "close": close, "inner": inner};
35663         } else {
35664         */
35665         // not sure what this is..
35666 //            if(!this.tabTpl){
35667                 //this.tabTpl = new Roo.Template(
35668                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
35669                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
35670                 //);
35671 //                this.tabTpl = new Roo.Template(
35672 //                   '<a href="#">' +
35673 //                   '<span unselectable="on"' +
35674 //                            (this.disableTooltips ? '' : ' title="{text}"') +
35675 //                            ' >{text}</span></a>'
35676 //                );
35677 //                
35678 //            }
35679
35680
35681             var template = tpl || this.tabTpl || false;
35682             
35683             if(!template){
35684                 
35685                 template = new Roo.Template(
35686                    '<a href="#">' +
35687                    '<span unselectable="on"' +
35688                             (this.disableTooltips ? '' : ' title="{text}"') +
35689                             ' >{text}</span></a>'
35690                 );
35691             }
35692             
35693             switch (typeof(template)) {
35694                 case 'object' :
35695                     break;
35696                 case 'string' :
35697                     template = new Roo.Template(template);
35698                     break;
35699                 default :
35700                     break;
35701             }
35702             
35703             var el = template.overwrite(td, {"text": text});
35704             
35705             var inner = el.getElementsByTagName("span")[0];
35706             
35707             return {"el": el, "inner": inner};
35708             
35709     }
35710         
35711     
35712 });
35713
35714 /**
35715  * @class Roo.TabPanelItem
35716  * @extends Roo.util.Observable
35717  * Represents an individual item (tab plus body) in a TabPanel.
35718  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
35719  * @param {String} id The id of this TabPanelItem
35720  * @param {String} text The text for the tab of this TabPanelItem
35721  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
35722  */
35723 Roo.bootstrap.panel.TabItem = function(config){
35724     /**
35725      * The {@link Roo.TabPanel} this TabPanelItem belongs to
35726      * @type Roo.TabPanel
35727      */
35728     this.tabPanel = config.panel;
35729     /**
35730      * The id for this TabPanelItem
35731      * @type String
35732      */
35733     this.id = config.id;
35734     /** @private */
35735     this.disabled = false;
35736     /** @private */
35737     this.text = config.text;
35738     /** @private */
35739     this.loaded = false;
35740     this.closable = config.closable;
35741
35742     /**
35743      * The body element for this TabPanelItem.
35744      * @type Roo.Element
35745      */
35746     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
35747     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
35748     this.bodyEl.setStyle("display", "block");
35749     this.bodyEl.setStyle("zoom", "1");
35750     //this.hideAction();
35751
35752     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
35753     /** @private */
35754     this.el = Roo.get(els.el);
35755     this.inner = Roo.get(els.inner, true);
35756     this.textEl = Roo.get(this.el.dom.firstChild, true);
35757     this.pnode = Roo.get(els.el.parentNode, true);
35758     this.el.on("mousedown", this.onTabMouseDown, this);
35759     this.el.on("click", this.onTabClick, this);
35760     /** @private */
35761     if(config.closable){
35762         var c = Roo.get(els.close, true);
35763         c.dom.title = this.closeText;
35764         c.addClassOnOver("close-over");
35765         c.on("click", this.closeClick, this);
35766      }
35767
35768     this.addEvents({
35769          /**
35770          * @event activate
35771          * Fires when this tab becomes the active tab.
35772          * @param {Roo.TabPanel} tabPanel The parent TabPanel
35773          * @param {Roo.TabPanelItem} this
35774          */
35775         "activate": true,
35776         /**
35777          * @event beforeclose
35778          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
35779          * @param {Roo.TabPanelItem} this
35780          * @param {Object} e Set cancel to true on this object to cancel the close.
35781          */
35782         "beforeclose": true,
35783         /**
35784          * @event close
35785          * Fires when this tab is closed.
35786          * @param {Roo.TabPanelItem} this
35787          */
35788          "close": true,
35789         /**
35790          * @event deactivate
35791          * Fires when this tab is no longer the active tab.
35792          * @param {Roo.TabPanel} tabPanel The parent TabPanel
35793          * @param {Roo.TabPanelItem} this
35794          */
35795          "deactivate" : true
35796     });
35797     this.hidden = false;
35798
35799     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
35800 };
35801
35802 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
35803            {
35804     purgeListeners : function(){
35805        Roo.util.Observable.prototype.purgeListeners.call(this);
35806        this.el.removeAllListeners();
35807     },
35808     /**
35809      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
35810      */
35811     show : function(){
35812         this.pnode.addClass("active");
35813         this.showAction();
35814         if(Roo.isOpera){
35815             this.tabPanel.stripWrap.repaint();
35816         }
35817         this.fireEvent("activate", this.tabPanel, this);
35818     },
35819
35820     /**
35821      * Returns true if this tab is the active tab.
35822      * @return {Boolean}
35823      */
35824     isActive : function(){
35825         return this.tabPanel.getActiveTab() == this;
35826     },
35827
35828     /**
35829      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
35830      */
35831     hide : function(){
35832         this.pnode.removeClass("active");
35833         this.hideAction();
35834         this.fireEvent("deactivate", this.tabPanel, this);
35835     },
35836
35837     hideAction : function(){
35838         this.bodyEl.hide();
35839         this.bodyEl.setStyle("position", "absolute");
35840         this.bodyEl.setLeft("-20000px");
35841         this.bodyEl.setTop("-20000px");
35842     },
35843
35844     showAction : function(){
35845         this.bodyEl.setStyle("position", "relative");
35846         this.bodyEl.setTop("");
35847         this.bodyEl.setLeft("");
35848         this.bodyEl.show();
35849     },
35850
35851     /**
35852      * Set the tooltip for the tab.
35853      * @param {String} tooltip The tab's tooltip
35854      */
35855     setTooltip : function(text){
35856         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
35857             this.textEl.dom.qtip = text;
35858             this.textEl.dom.removeAttribute('title');
35859         }else{
35860             this.textEl.dom.title = text;
35861         }
35862     },
35863
35864     onTabClick : function(e){
35865         e.preventDefault();
35866         this.tabPanel.activate(this.id);
35867     },
35868
35869     onTabMouseDown : function(e){
35870         e.preventDefault();
35871         this.tabPanel.activate(this.id);
35872     },
35873 /*
35874     getWidth : function(){
35875         return this.inner.getWidth();
35876     },
35877
35878     setWidth : function(width){
35879         var iwidth = width - this.pnode.getPadding("lr");
35880         this.inner.setWidth(iwidth);
35881         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
35882         this.pnode.setWidth(width);
35883     },
35884 */
35885     /**
35886      * Show or hide the tab
35887      * @param {Boolean} hidden True to hide or false to show.
35888      */
35889     setHidden : function(hidden){
35890         this.hidden = hidden;
35891         this.pnode.setStyle("display", hidden ? "none" : "");
35892     },
35893
35894     /**
35895      * Returns true if this tab is "hidden"
35896      * @return {Boolean}
35897      */
35898     isHidden : function(){
35899         return this.hidden;
35900     },
35901
35902     /**
35903      * Returns the text for this tab
35904      * @return {String}
35905      */
35906     getText : function(){
35907         return this.text;
35908     },
35909     /*
35910     autoSize : function(){
35911         //this.el.beginMeasure();
35912         this.textEl.setWidth(1);
35913         /*
35914          *  #2804 [new] Tabs in Roojs
35915          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
35916          */
35917         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
35918         //this.el.endMeasure();
35919     //},
35920
35921     /**
35922      * Sets the text for the tab (Note: this also sets the tooltip text)
35923      * @param {String} text The tab's text and tooltip
35924      */
35925     setText : function(text){
35926         this.text = text;
35927         this.textEl.update(text);
35928         this.setTooltip(text);
35929         //if(!this.tabPanel.resizeTabs){
35930         //    this.autoSize();
35931         //}
35932     },
35933     /**
35934      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
35935      */
35936     activate : function(){
35937         this.tabPanel.activate(this.id);
35938     },
35939
35940     /**
35941      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
35942      */
35943     disable : function(){
35944         if(this.tabPanel.active != this){
35945             this.disabled = true;
35946             this.pnode.addClass("disabled");
35947         }
35948     },
35949
35950     /**
35951      * Enables this TabPanelItem if it was previously disabled.
35952      */
35953     enable : function(){
35954         this.disabled = false;
35955         this.pnode.removeClass("disabled");
35956     },
35957
35958     /**
35959      * Sets the content for this TabPanelItem.
35960      * @param {String} content The content
35961      * @param {Boolean} loadScripts true to look for and load scripts
35962      */
35963     setContent : function(content, loadScripts){
35964         this.bodyEl.update(content, loadScripts);
35965     },
35966
35967     /**
35968      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
35969      * @return {Roo.UpdateManager} The UpdateManager
35970      */
35971     getUpdateManager : function(){
35972         return this.bodyEl.getUpdateManager();
35973     },
35974
35975     /**
35976      * Set a URL to be used to load the content for this TabPanelItem.
35977      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
35978      * @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)
35979      * @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)
35980      * @return {Roo.UpdateManager} The UpdateManager
35981      */
35982     setUrl : function(url, params, loadOnce){
35983         if(this.refreshDelegate){
35984             this.un('activate', this.refreshDelegate);
35985         }
35986         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35987         this.on("activate", this.refreshDelegate);
35988         return this.bodyEl.getUpdateManager();
35989     },
35990
35991     /** @private */
35992     _handleRefresh : function(url, params, loadOnce){
35993         if(!loadOnce || !this.loaded){
35994             var updater = this.bodyEl.getUpdateManager();
35995             updater.update(url, params, this._setLoaded.createDelegate(this));
35996         }
35997     },
35998
35999     /**
36000      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
36001      *   Will fail silently if the setUrl method has not been called.
36002      *   This does not activate the panel, just updates its content.
36003      */
36004     refresh : function(){
36005         if(this.refreshDelegate){
36006            this.loaded = false;
36007            this.refreshDelegate();
36008         }
36009     },
36010
36011     /** @private */
36012     _setLoaded : function(){
36013         this.loaded = true;
36014     },
36015
36016     /** @private */
36017     closeClick : function(e){
36018         var o = {};
36019         e.stopEvent();
36020         this.fireEvent("beforeclose", this, o);
36021         if(o.cancel !== true){
36022             this.tabPanel.removeTab(this.id);
36023         }
36024     },
36025     /**
36026      * The text displayed in the tooltip for the close icon.
36027      * @type String
36028      */
36029     closeText : "Close this tab"
36030 });