commit
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         
105         cfg.id = this.id || Roo.id();
106         
107         // fill in the extra attributes 
108         if (this.xattr && typeof(this.xattr) =='object') {
109             for (var i in this.xattr) {
110                 cfg[i] = this.xattr[i];
111             }
112         }
113         
114         if(this.dataId){
115             cfg.dataId = this.dataId;
116         }
117         
118         if (this.cls) {
119             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
120         }
121         
122         if (this.style) { // fixme needs to support more complex style data.
123             cfg.style = this.style;
124         }
125         
126         if(this.name){
127             cfg.name = this.name;
128         }
129         
130         this.el = ct.createChild(cfg, position);
131         
132         if (this.tooltip) {
133             this.tooltipEl().attr('tooltip', this.tooltip);
134         }
135         
136         if(this.tabIndex !== undefined){
137             this.el.dom.setAttribute('tabIndex', this.tabIndex);
138         }
139         
140         this.initEvents();
141         
142     },
143     /**
144      * Fetch the element to add children to
145      * @return {Roo.Element} defaults to this.el
146      */
147     getChildContainer : function()
148     {
149         return this.el;
150     },
151     /**
152      * Fetch the element to display the tooltip on.
153      * @return {Roo.Element} defaults to this.el
154      */
155     tooltipEl : function()
156     {
157         return this.el;
158     },
159         
160     addxtype  : function(tree,cntr)
161     {
162         var cn = this;
163         
164         cn = Roo.factory(tree);
165         //Roo.log(['addxtype', cn]);
166            
167         cn.parentType = this.xtype; //??
168         cn.parentId = this.id;
169         
170         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171         if (typeof(cn.container_method) == 'string') {
172             cntr = cn.container_method;
173         }
174         
175         
176         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
177         
178         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
179         
180         var build_from_html =  Roo.XComponent.build_from_html;
181           
182         var is_body  = (tree.xtype == 'Body') ;
183           
184         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
185           
186         var self_cntr_el = Roo.get(this[cntr](false));
187         
188         // do not try and build conditional elements 
189         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
190             return false;
191         }
192         
193         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195                 return this.addxtypeChild(tree,cntr, is_body);
196             }
197             
198             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
199                 
200             if(echild){
201                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
202             }
203             
204             Roo.log('skipping render');
205             return cn;
206             
207         }
208         
209         var ret = false;
210         if (!build_from_html) {
211             return false;
212         }
213         
214         // this i think handles overlaying multiple children of the same type
215         // with the sam eelement.. - which might be buggy..
216         while (true) {
217             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
218             
219             if (!echild) {
220                 break;
221             }
222             
223             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
224                 break;
225             }
226             
227             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
228         }
229        
230         return ret;
231     },
232     
233     
234     addxtypeChild : function (tree, cntr, is_body)
235     {
236         Roo.debug && Roo.log('addxtypeChild:' + cntr);
237         var cn = this;
238         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
239         
240         
241         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
242                     (typeof(tree['flexy:foreach']) != 'undefined');
243           
244         
245         
246          skip_children = false;
247         // render the element if it's not BODY.
248         if (!is_body) {
249            
250             cn = Roo.factory(tree);
251            
252             cn.parentType = this.xtype; //??
253             cn.parentId = this.id;
254             
255             var build_from_html =  Roo.XComponent.build_from_html;
256             
257             
258             // does the container contain child eleemnts with 'xtype' attributes.
259             // that match this xtype..
260             // note - when we render we create these as well..
261             // so we should check to see if body has xtype set.
262             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
263                
264                 var self_cntr_el = Roo.get(this[cntr](false));
265                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
266                 if (echild) { 
267                     //Roo.log(Roo.XComponent.build_from_html);
268                     //Roo.log("got echild:");
269                     //Roo.log(echild);
270                 }
271                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
272                 // and are not displayed -this causes this to use up the wrong element when matching.
273                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
274                 
275                 
276                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
277                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
278                   
279                   
280                   
281                     cn.el = echild;
282                   //  Roo.log("GOT");
283                     //echild.dom.removeAttribute('xtype');
284                 } else {
285                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
286                     Roo.debug && Roo.log(self_cntr_el);
287                     Roo.debug && Roo.log(echild);
288                     Roo.debug && Roo.log(cn);
289                 }
290             }
291            
292             
293            
294             // if object has flexy:if - then it may or may not be rendered.
295             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
296                 // skip a flexy if element.
297                 Roo.debug && Roo.log('skipping render');
298                 Roo.debug && Roo.log(tree);
299                 if (!cn.el) {
300                     Roo.debug && Roo.log('skipping all children');
301                     skip_children = true;
302                 }
303                 
304              } else {
305                  
306                 // actually if flexy:foreach is found, we really want to create 
307                 // multiple copies here...
308                 //Roo.log('render');
309                 //Roo.log(this[cntr]());
310                 // some elements do not have render methods.. like the layouts...
311                 cn.render && cn.render(this[cntr](true));
312              }
313             // then add the element..
314         }
315         
316         
317         // handle the kids..
318         
319         var nitems = [];
320         /*
321         if (typeof (tree.menu) != 'undefined') {
322             tree.menu.parentType = cn.xtype;
323             tree.menu.triggerEl = cn.el;
324             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
325             
326         }
327         */
328         if (!tree.items || !tree.items.length) {
329             cn.items = nitems;
330             //Roo.log(["no children", this]);
331             
332             return cn;
333         }
334          
335         var items = tree.items;
336         delete tree.items;
337         
338         //Roo.log(items.length);
339             // add the items..
340         if (!skip_children) {    
341             for(var i =0;i < items.length;i++) {
342               //  Roo.log(['add child', items[i]]);
343                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
344             }
345         }
346         
347         cn.items = nitems;
348         
349         //Roo.log("fire childrenrendered");
350         
351         cn.fireEvent('childrenrendered', this);
352         
353         return cn;
354     },
355     /**
356      * Show a component - removes 'hidden' class
357      */
358     show : function()
359     {
360         if (this.el) {
361             this.el.removeClass('hidden');
362         }
363     },
364     /**
365      * Hide a component - adds 'hidden' class
366      */
367     hide: function()
368     {
369         if (this.el && !this.el.hasClass('hidden')) {
370             this.el.addClass('hidden');
371         }
372         
373     }
374 });
375
376  /*
377  * - LGPL
378  *
379  * Body
380  *
381  */
382
383 /**
384  * @class Roo.bootstrap.Body
385  * @extends Roo.bootstrap.Component
386  * Bootstrap Body class
387  *
388  * @constructor
389  * Create a new body
390  * @param {Object} config The config object
391  */
392
393 Roo.bootstrap.Body = function(config){
394
395     config = config || {};
396
397     Roo.bootstrap.Body.superclass.constructor.call(this, config);
398     this.el = Roo.get(config.el ? config.el : document.body );
399     if (this.cls && this.cls.length) {
400         Roo.get(document.body).addClass(this.cls);
401     }
402 };
403
404 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
405
406     is_body : true,// just to make sure it's constructed?
407
408         autoCreate : {
409         cls: 'container'
410     },
411     onRender : function(ct, position)
412     {
413        /* Roo.log("Roo.bootstrap.Body - onRender");
414         if (this.cls && this.cls.length) {
415             Roo.get(document.body).addClass(this.cls);
416         }
417         // style??? xttr???
418         */
419     }
420
421
422
423
424 });
425 /*
426  * - LGPL
427  *
428  * button group
429  * 
430  */
431
432
433 /**
434  * @class Roo.bootstrap.ButtonGroup
435  * @extends Roo.bootstrap.Component
436  * Bootstrap ButtonGroup class
437  * @cfg {String} size lg | sm | xs (default empty normal)
438  * @cfg {String} align vertical | justified  (default none)
439  * @cfg {String} direction up | down (default down)
440  * @cfg {Boolean} toolbar false | true
441  * @cfg {Boolean} btn true | false
442  * 
443  * 
444  * @constructor
445  * Create a new Input
446  * @param {Object} config The config object
447  */
448
449 Roo.bootstrap.ButtonGroup = function(config){
450     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
451 };
452
453 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
454     
455     size: '',
456     align: '',
457     direction: '',
458     toolbar: false,
459     btn: true,
460
461     getAutoCreate : function(){
462         var cfg = {
463             cls: 'btn-group',
464             html : null
465         };
466         
467         cfg.html = this.html || cfg.html;
468         
469         if (this.toolbar) {
470             cfg = {
471                 cls: 'btn-toolbar',
472                 html: null
473             };
474             
475             return cfg;
476         }
477         
478         if (['vertical','justified'].indexOf(this.align)!==-1) {
479             cfg.cls = 'btn-group-' + this.align;
480             
481             if (this.align == 'justified') {
482                 console.log(this.items);
483             }
484         }
485         
486         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
487             cfg.cls += ' btn-group-' + this.size;
488         }
489         
490         if (this.direction == 'up') {
491             cfg.cls += ' dropup' ;
492         }
493         
494         return cfg;
495     }
496    
497 });
498
499  /*
500  * - LGPL
501  *
502  * button
503  * 
504  */
505
506 /**
507  * @class Roo.bootstrap.Button
508  * @extends Roo.bootstrap.Component
509  * Bootstrap Button class
510  * @cfg {String} html The button content
511  * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default 
512  * @cfg {String} size ( lg | sm | xs)
513  * @cfg {String} tag ( a | input | submit)
514  * @cfg {String} href empty or href
515  * @cfg {Boolean} disabled default false;
516  * @cfg {Boolean} isClose default false;
517  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
518  * @cfg {String} badge text for badge
519  * @cfg {String} theme default 
520  * @cfg {Boolean} inverse 
521  * @cfg {Boolean} toggle 
522  * @cfg {String} ontext text for on toggle state
523  * @cfg {String} offtext text for off toggle state
524  * @cfg {Boolean} defaulton 
525  * @cfg {Boolean} preventDefault  default true
526  * @cfg {Boolean} removeClass remove the standard class..
527  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
528  * 
529  * @constructor
530  * Create a new button
531  * @param {Object} config The config object
532  */
533
534
535 Roo.bootstrap.Button = function(config){
536     Roo.bootstrap.Button.superclass.constructor.call(this, config);
537     this.addEvents({
538         // raw events
539         /**
540          * @event click
541          * When a butotn is pressed
542          * @param {Roo.bootstrap.Button} this
543          * @param {Roo.EventObject} e
544          */
545         "click" : true,
546          /**
547          * @event toggle
548          * After the button has been toggles
549          * @param {Roo.EventObject} e
550          * @param {boolean} pressed (also available as button.pressed)
551          */
552         "toggle" : true
553     });
554 };
555
556 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
557     html: false,
558     active: false,
559     weight: '',
560     size: '',
561     tag: 'button',
562     href: '',
563     disabled: false,
564     isClose: false,
565     glyphicon: '',
566     badge: '',
567     theme: 'default',
568     inverse: false,
569     
570     toggle: false,
571     ontext: 'ON',
572     offtext: 'OFF',
573     defaulton: true,
574     preventDefault: true,
575     removeClass: false,
576     name: false,
577     target: false,
578     
579     
580     pressed : null,
581      
582     
583     getAutoCreate : function(){
584         
585         var cfg = {
586             tag : 'button',
587             cls : 'roo-button',
588             html: ''
589         };
590         
591         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
592             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
593             this.tag = 'button';
594         } else {
595             cfg.tag = this.tag;
596         }
597         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
598         
599         if (this.toggle == true) {
600             cfg={
601                 tag: 'div',
602                 cls: 'slider-frame roo-button',
603                 cn: [
604                     {
605                         tag: 'span',
606                         'data-on-text':'ON',
607                         'data-off-text':'OFF',
608                         cls: 'slider-button',
609                         html: this.offtext
610                     }
611                 ]
612             };
613             
614             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
615                 cfg.cls += ' '+this.weight;
616             }
617             
618             return cfg;
619         }
620         
621         if (this.isClose) {
622             cfg.cls += ' close';
623             
624             cfg["aria-hidden"] = true;
625             
626             cfg.html = "&times;";
627             
628             return cfg;
629         }
630         
631          
632         if (this.theme==='default') {
633             cfg.cls = 'btn roo-button';
634             
635             //if (this.parentType != 'Navbar') {
636             this.weight = this.weight.length ?  this.weight : 'default';
637             //}
638             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
639                 
640                 cfg.cls += ' btn-' + this.weight;
641             }
642         } else if (this.theme==='glow') {
643             
644             cfg.tag = 'a';
645             cfg.cls = 'btn-glow roo-button';
646             
647             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
648                 
649                 cfg.cls += ' ' + this.weight;
650             }
651         }
652    
653         
654         if (this.inverse) {
655             this.cls += ' inverse';
656         }
657         
658         
659         if (this.active) {
660             cfg.cls += ' active';
661         }
662         
663         if (this.disabled) {
664             cfg.disabled = 'disabled';
665         }
666         
667         if (this.items) {
668             Roo.log('changing to ul' );
669             cfg.tag = 'ul';
670             this.glyphicon = 'caret';
671         }
672         
673         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
674          
675         //gsRoo.log(this.parentType);
676         if (this.parentType === 'Navbar' && !this.parent().bar) {
677             Roo.log('changing to li?');
678             
679             cfg.tag = 'li';
680             
681             cfg.cls = '';
682             cfg.cn =  [{
683                 tag : 'a',
684                 cls : 'roo-button',
685                 html : this.html,
686                 href : this.href || '#'
687             }];
688             if (this.menu) {
689                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
690                 cfg.cls += ' dropdown';
691             }   
692             
693             delete cfg.html;
694             
695         }
696         
697        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
698         
699         if (this.glyphicon) {
700             cfg.html = ' ' + cfg.html;
701             
702             cfg.cn = [
703                 {
704                     tag: 'span',
705                     cls: 'glyphicon glyphicon-' + this.glyphicon
706                 }
707             ];
708         }
709         
710         if (this.badge) {
711             cfg.html += ' ';
712             
713             cfg.tag = 'a';
714             
715 //            cfg.cls='btn roo-button';
716             
717             cfg.href=this.href;
718             
719             var value = cfg.html;
720             
721             if(this.glyphicon){
722                 value = {
723                             tag: 'span',
724                             cls: 'glyphicon glyphicon-' + this.glyphicon,
725                             html: this.html
726                         };
727                 
728             }
729             
730             cfg.cn = [
731                 value,
732                 {
733                     tag: 'span',
734                     cls: 'badge',
735                     html: this.badge
736                 }
737             ];
738             
739             cfg.html='';
740         }
741         
742         if (this.menu) {
743             cfg.cls += ' dropdown';
744             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
745         }
746         
747         if (cfg.tag !== 'a' && this.href !== '') {
748             throw "Tag must be a to set href.";
749         } else if (this.href.length > 0) {
750             cfg.href = this.href;
751         }
752         
753         if(this.removeClass){
754             cfg.cls = '';
755         }
756         
757         if(this.target){
758             cfg.target = this.target;
759         }
760         
761         return cfg;
762     },
763     initEvents: function() {
764        // Roo.log('init events?');
765 //        Roo.log(this.el.dom);
766         // add the menu...
767         
768         if (typeof (this.menu) != 'undefined') {
769             this.menu.parentType = this.xtype;
770             this.menu.triggerEl = this.el;
771             this.addxtype(Roo.apply({}, this.menu));
772         }
773
774
775        if (this.el.hasClass('roo-button')) {
776             this.el.on('click', this.onClick, this);
777        } else {
778             this.el.select('.roo-button').on('click', this.onClick, this);
779        }
780        
781        if(this.removeClass){
782            this.el.on('click', this.onClick, this);
783        }
784        
785        this.el.enableDisplayMode();
786         
787     },
788     onClick : function(e)
789     {
790         if (this.disabled) {
791             return;
792         }
793         
794         
795         Roo.log('button on click ');
796         if(this.preventDefault){
797             e.preventDefault();
798         }
799         if (this.pressed === true || this.pressed === false) {
800             this.pressed = !this.pressed;
801             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
802             this.fireEvent('toggle', this, e, this.pressed);
803         }
804         
805         
806         this.fireEvent('click', this, e);
807     },
808     
809     /**
810      * Enables this button
811      */
812     enable : function()
813     {
814         this.disabled = false;
815         this.el.removeClass('disabled');
816     },
817     
818     /**
819      * Disable this button
820      */
821     disable : function()
822     {
823         this.disabled = true;
824         this.el.addClass('disabled');
825     },
826      /**
827      * sets the active state on/off, 
828      * @param {Boolean} state (optional) Force a particular state
829      */
830     setActive : function(v) {
831         
832         this.el[v ? 'addClass' : 'removeClass']('active');
833     },
834      /**
835      * toggles the current active state 
836      */
837     toggleActive : function()
838     {
839        var active = this.el.hasClass('active');
840        this.setActive(!active);
841        
842         
843     },
844     setText : function(str)
845     {
846         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
847     },
848     getText : function()
849     {
850         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
851     },
852     hide: function() {
853        
854      
855         this.el.hide();   
856     },
857     show: function() {
858        
859         this.el.show();   
860     }
861     
862     
863 });
864
865  /*
866  * - LGPL
867  *
868  * column
869  * 
870  */
871
872 /**
873  * @class Roo.bootstrap.Column
874  * @extends Roo.bootstrap.Component
875  * Bootstrap Column class
876  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
877  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
878  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
879  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
880  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
881  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
882  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
883  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
884  *
885  * 
886  * @cfg {Boolean} hidden (true|false) hide the element
887  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
888  * @cfg {String} fa (ban|check|...) font awesome icon
889  * @cfg {Number} fasize (1|2|....) font awsome size
890
891  * @cfg {String} icon (info-sign|check|...) glyphicon name
892
893  * @cfg {String} html content of column.
894  * 
895  * @constructor
896  * Create a new Column
897  * @param {Object} config The config object
898  */
899
900 Roo.bootstrap.Column = function(config){
901     Roo.bootstrap.Column.superclass.constructor.call(this, config);
902 };
903
904 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
905     
906     xs: false,
907     sm: false,
908     md: false,
909     lg: false,
910     xsoff: false,
911     smoff: false,
912     mdoff: false,
913     lgoff: false,
914     html: '',
915     offset: 0,
916     alert: false,
917     fa: false,
918     icon : false,
919     hidden : false,
920     fasize : 1,
921     
922     getAutoCreate : function(){
923         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
924         
925         cfg = {
926             tag: 'div',
927             cls: 'column'
928         };
929         
930         var settings=this;
931         ['xs','sm','md','lg'].map(function(size){
932             //Roo.log( size + ':' + settings[size]);
933             
934             if (settings[size+'off'] !== false) {
935                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
936             }
937             
938             if (settings[size] === false) {
939                 return;
940             }
941             
942             if (!settings[size]) { // 0 = hidden
943                 cfg.cls += ' hidden-' + size;
944                 return;
945             }
946             cfg.cls += ' col-' + size + '-' + settings[size];
947             
948         });
949         
950         if (this.hidden) {
951             cfg.cls += ' hidden';
952         }
953         
954         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
955             cfg.cls +=' alert alert-' + this.alert;
956         }
957         
958         
959         if (this.html.length) {
960             cfg.html = this.html;
961         }
962         if (this.fa) {
963             var fasize = '';
964             if (this.fasize > 1) {
965                 fasize = ' fa-' + this.fasize + 'x';
966             }
967             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
968             
969             
970         }
971         if (this.icon) {
972             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
973         }
974         
975         return cfg;
976     }
977    
978 });
979
980  
981
982  /*
983  * - LGPL
984  *
985  * page container.
986  * 
987  */
988
989
990 /**
991  * @class Roo.bootstrap.Container
992  * @extends Roo.bootstrap.Component
993  * Bootstrap Container class
994  * @cfg {Boolean} jumbotron is it a jumbotron element
995  * @cfg {String} html content of element
996  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
997  * @cfg {String} panel (primary|success|info|warning|danger) render as panel  - type - primary/success.....
998  * @cfg {String} header content of header (for panel)
999  * @cfg {String} footer content of footer (for panel)
1000  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1001  * @cfg {String} tag (header|aside|section) type of HTML tag.
1002  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1003  * @cfg {String} fa font awesome icon
1004  * @cfg {String} icon (info-sign|check|...) glyphicon name
1005  * @cfg {Boolean} hidden (true|false) hide the element
1006  * @cfg {Boolean} expandable (true|false) default false
1007  * @cfg {Boolean} expanded (true|false) default true
1008  * @cfg {String} rheader contet on the right of header
1009  * @cfg {Boolean} clickable (true|false) default false
1010
1011  *     
1012  * @constructor
1013  * Create a new Container
1014  * @param {Object} config The config object
1015  */
1016
1017 Roo.bootstrap.Container = function(config){
1018     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1019     
1020     this.addEvents({
1021         // raw events
1022          /**
1023          * @event expand
1024          * After the panel has been expand
1025          * 
1026          * @param {Roo.bootstrap.Container} this
1027          */
1028         "expand" : true,
1029         /**
1030          * @event collapse
1031          * After the panel has been collapsed
1032          * 
1033          * @param {Roo.bootstrap.Container} this
1034          */
1035         "collapse" : true,
1036         /**
1037          * @event click
1038          * When a element is chick
1039          * @param {Roo.bootstrap.Container} this
1040          * @param {Roo.EventObject} e
1041          */
1042         "click" : true
1043     });
1044 };
1045
1046 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1047     
1048     jumbotron : false,
1049     well: '',
1050     panel : '',
1051     header: '',
1052     footer : '',
1053     sticky: '',
1054     tag : false,
1055     alert : false,
1056     fa: false,
1057     icon : false,
1058     expandable : false,
1059     rheader : '',
1060     expanded : true,
1061     clickable: false,
1062   
1063      
1064     getChildContainer : function() {
1065         
1066         if(!this.el){
1067             return false;
1068         }
1069         
1070         if (this.panel.length) {
1071             return this.el.select('.panel-body',true).first();
1072         }
1073         
1074         return this.el;
1075     },
1076     
1077     
1078     getAutoCreate : function(){
1079         
1080         var cfg = {
1081             tag : this.tag || 'div',
1082             html : '',
1083             cls : ''
1084         };
1085         if (this.jumbotron) {
1086             cfg.cls = 'jumbotron';
1087         }
1088         
1089         
1090         
1091         // - this is applied by the parent..
1092         //if (this.cls) {
1093         //    cfg.cls = this.cls + '';
1094         //}
1095         
1096         if (this.sticky.length) {
1097             
1098             var bd = Roo.get(document.body);
1099             if (!bd.hasClass('bootstrap-sticky')) {
1100                 bd.addClass('bootstrap-sticky');
1101                 Roo.select('html',true).setStyle('height', '100%');
1102             }
1103              
1104             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1105         }
1106         
1107         
1108         if (this.well.length) {
1109             switch (this.well) {
1110                 case 'lg':
1111                 case 'sm':
1112                     cfg.cls +=' well well-' +this.well;
1113                     break;
1114                 default:
1115                     cfg.cls +=' well';
1116                     break;
1117             }
1118         }
1119         
1120         if (this.hidden) {
1121             cfg.cls += ' hidden';
1122         }
1123         
1124         
1125         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1126             cfg.cls +=' alert alert-' + this.alert;
1127         }
1128         
1129         var body = cfg;
1130         
1131         if (this.panel.length) {
1132             cfg.cls += ' panel panel-' + this.panel;
1133             cfg.cn = [];
1134             if (this.header.length) {
1135                 
1136                 var h = [];
1137                 
1138                 if(this.expandable){
1139                     
1140                     cfg.cls = cfg.cls + ' expandable';
1141                     
1142                     h.push({
1143                         tag: 'i',
1144                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1145                     });
1146                     
1147                 }
1148                 
1149                 h.push(
1150                     {
1151                         tag: 'span',
1152                         cls : 'panel-title',
1153                         html : (this.expandable ? '&nbsp;' : '') + this.header
1154                     },
1155                     {
1156                         tag: 'span',
1157                         cls: 'panel-header-right',
1158                         html: this.rheader
1159                     }
1160                 );
1161                 
1162                 cfg.cn.push({
1163                     cls : 'panel-heading',
1164                     style : this.expandable ? 'cursor: pointer' : '',
1165                     cn : h
1166                 });
1167                 
1168             }
1169             
1170             body = false;
1171             cfg.cn.push({
1172                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1173                 html : this.html
1174             });
1175             
1176             
1177             if (this.footer.length) {
1178                 cfg.cn.push({
1179                     cls : 'panel-footer',
1180                     html : this.footer
1181                     
1182                 });
1183             }
1184             
1185         }
1186         
1187         if (body) {
1188             body.html = this.html || cfg.html;
1189             // prefix with the icons..
1190             if (this.fa) {
1191                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1192             }
1193             if (this.icon) {
1194                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1195             }
1196             
1197             
1198         }
1199         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1200             cfg.cls =  'container';
1201         }
1202         
1203         return cfg;
1204     },
1205     
1206     initEvents: function() 
1207     {
1208         if(this.expandable){
1209             var headerEl = this.headerEl();
1210         
1211             if(headerEl){
1212                 headerEl.on('click', this.onToggleClick, this);
1213             }
1214         }
1215         
1216         if(this.clickable){
1217             this.el.on('click', this.onClick, this);
1218         }
1219         
1220     },
1221     
1222     onToggleClick : function()
1223     {
1224         var headerEl = this.headerEl();
1225         
1226         if(!headerEl){
1227             return;
1228         }
1229         
1230         if(this.expanded){
1231             this.collapse();
1232             return;
1233         }
1234         
1235         this.expand();
1236     },
1237     
1238     expand : function()
1239     {
1240         if(this.fireEvent('expand', this)) {
1241             
1242             this.expanded = true;
1243             
1244             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1245             
1246             this.el.select('.panel-body',true).first().removeClass('hide');
1247             
1248             var toggleEl = this.toggleEl();
1249
1250             if(!toggleEl){
1251                 return;
1252             }
1253
1254             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1255         }
1256         
1257     },
1258     
1259     collapse : function()
1260     {
1261         if(this.fireEvent('collapse', this)) {
1262             
1263             this.expanded = false;
1264             
1265             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1266             this.el.select('.panel-body',true).first().addClass('hide');
1267         
1268             var toggleEl = this.toggleEl();
1269
1270             if(!toggleEl){
1271                 return;
1272             }
1273
1274             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1275         }
1276     },
1277     
1278     toggleEl : function()
1279     {
1280         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1281             return;
1282         }
1283         
1284         return this.el.select('.panel-heading .fa',true).first();
1285     },
1286     
1287     headerEl : function()
1288     {
1289         if(!this.el || !this.panel.length || !this.header.length){
1290             return;
1291         }
1292         
1293         return this.el.select('.panel-heading',true).first()
1294     },
1295     
1296     titleEl : function()
1297     {
1298         if(!this.el || !this.panel.length || !this.header.length){
1299             return;
1300         }
1301         
1302         return this.el.select('.panel-title',true).first();
1303     },
1304     
1305     setTitle : function(v)
1306     {
1307         var titleEl = this.titleEl();
1308         
1309         if(!titleEl){
1310             return;
1311         }
1312         
1313         titleEl.dom.innerHTML = v;
1314     },
1315     
1316     getTitle : function()
1317     {
1318         
1319         var titleEl = this.titleEl();
1320         
1321         if(!titleEl){
1322             return '';
1323         }
1324         
1325         return titleEl.dom.innerHTML;
1326     },
1327     
1328     setRightTitle : function(v)
1329     {
1330         var t = this.el.select('.panel-header-right',true).first();
1331         
1332         if(!t){
1333             return;
1334         }
1335         
1336         t.dom.innerHTML = v;
1337     },
1338     
1339     onClick : function(e)
1340     {
1341         e.preventDefault();
1342         
1343         this.fireEvent('click', this, e);
1344     }
1345    
1346 });
1347
1348  /*
1349  * - LGPL
1350  *
1351  * image
1352  * 
1353  */
1354
1355
1356 /**
1357  * @class Roo.bootstrap.Img
1358  * @extends Roo.bootstrap.Component
1359  * Bootstrap Img class
1360  * @cfg {Boolean} imgResponsive false | true
1361  * @cfg {String} border rounded | circle | thumbnail
1362  * @cfg {String} src image source
1363  * @cfg {String} alt image alternative text
1364  * @cfg {String} href a tag href
1365  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1366  * @cfg {String} xsUrl xs image source
1367  * @cfg {String} smUrl sm image source
1368  * @cfg {String} mdUrl md image source
1369  * @cfg {String} lgUrl lg image source
1370  * 
1371  * @constructor
1372  * Create a new Input
1373  * @param {Object} config The config object
1374  */
1375
1376 Roo.bootstrap.Img = function(config){
1377     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1378     
1379     this.addEvents({
1380         // img events
1381         /**
1382          * @event click
1383          * The img click event for the img.
1384          * @param {Roo.EventObject} e
1385          */
1386         "click" : true
1387     });
1388 };
1389
1390 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1391     
1392     imgResponsive: true,
1393     border: '',
1394     src: 'about:blank',
1395     href: false,
1396     target: false,
1397     xsUrl: '',
1398     smUrl: '',
1399     mdUrl: '',
1400     lgUrl: '',
1401
1402     getAutoCreate : function()
1403     {   
1404         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1405             return this.createSingleImg();
1406         }
1407         
1408         var cfg = {
1409             tag: 'div',
1410             cls: 'roo-image-responsive-group',
1411             cn: []
1412         };
1413         var _this = this;
1414         
1415         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1416             
1417             if(!_this[size + 'Url']){
1418                 return;
1419             }
1420             
1421             var img = {
1422                 tag: 'img',
1423                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1424                 html: _this.html || cfg.html,
1425                 src: _this[size + 'Url']
1426             };
1427             
1428             img.cls += ' roo-image-responsive-' + size;
1429             
1430             var s = ['xs', 'sm', 'md', 'lg'];
1431             
1432             s.splice(s.indexOf(size), 1);
1433             
1434             Roo.each(s, function(ss){
1435                 img.cls += ' hidden-' + ss;
1436             });
1437             
1438             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1439                 cfg.cls += ' img-' + _this.border;
1440             }
1441             
1442             if(_this.alt){
1443                 cfg.alt = _this.alt;
1444             }
1445             
1446             if(_this.href){
1447                 var a = {
1448                     tag: 'a',
1449                     href: _this.href,
1450                     cn: [
1451                         img
1452                     ]
1453                 };
1454
1455                 if(this.target){
1456                     a.target = _this.target;
1457                 }
1458             }
1459             
1460             cfg.cn.push((_this.href) ? a : img);
1461             
1462         });
1463         
1464         return cfg;
1465     },
1466     
1467     createSingleImg : function()
1468     {
1469         var cfg = {
1470             tag: 'img',
1471             cls: (this.imgResponsive) ? 'img-responsive' : '',
1472             html : null,
1473             src : 'about:blank'  // just incase src get's set to undefined?!?
1474         };
1475         
1476         cfg.html = this.html || cfg.html;
1477         
1478         cfg.src = this.src || cfg.src;
1479         
1480         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1481             cfg.cls += ' img-' + this.border;
1482         }
1483         
1484         if(this.alt){
1485             cfg.alt = this.alt;
1486         }
1487         
1488         if(this.href){
1489             var a = {
1490                 tag: 'a',
1491                 href: this.href,
1492                 cn: [
1493                     cfg
1494                 ]
1495             };
1496             
1497             if(this.target){
1498                 a.target = this.target;
1499             }
1500             
1501         }
1502         
1503         return (this.href) ? a : cfg;
1504     },
1505     
1506     initEvents: function() 
1507     {
1508         if(!this.href){
1509             this.el.on('click', this.onClick, this);
1510         }
1511         
1512     },
1513     
1514     onClick : function(e)
1515     {
1516         Roo.log('img onclick');
1517         this.fireEvent('click', this, e);
1518     },
1519     /**
1520      * Sets the url of the image - used to update it
1521      * @param {String} url the url of the image
1522      */
1523     
1524     setSrc : function(url)
1525     {
1526         this.src =  url;
1527         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             
24786             Roo.debug && Roo.log(dom);
24787             //Roo.log(dom);
24788             //Roo.log(this.currentEl);
24789             //Roo.log(this.currentEl.contains(dom));
24790             if (this.currentEl == el) {
24791                 return;
24792             }
24793             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24794                 return;
24795             }
24796
24797         }
24798         
24799         if (this.currentTip.el) {
24800             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24801         }    
24802         //Roo.log(ev);
24803         
24804         if(!el || el.dom == document){
24805             return;
24806         }
24807         
24808         var bindEl = el;
24809         
24810         // you can not look for children, as if el is the body.. then everythign is the child..
24811         if (!el.attr('tooltip')) { //
24812             if (!el.select("[tooltip]").elements.length) {
24813                 return;
24814             }
24815             // is the mouse over this child...?
24816             bindEl = el.select("[tooltip]").first();
24817             var xy = ev.getXY();
24818             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24819                 //Roo.log("not in region.");
24820                 return;
24821             }
24822             //Roo.log("child element over..");
24823             
24824         }
24825         this.currentEl = bindEl;
24826         this.currentTip.bind(bindEl);
24827         this.currentRegion = Roo.lib.Region.getRegion(dom);
24828         this.currentTip.enter();
24829         
24830     },
24831     leave : function(ev)
24832     {
24833         var dom = ev.getTarget();
24834         //Roo.log(['leave',dom]);
24835         if (!this.currentEl) {
24836             return;
24837         }
24838         
24839         
24840         if (dom != this.currentEl.dom) {
24841             return;
24842         }
24843         var xy = ev.getXY();
24844         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
24845             return;
24846         }
24847         // only activate leave if mouse cursor is outside... bounding box..
24848         
24849         
24850         
24851         
24852         if (this.currentTip) {
24853             this.currentTip.leave();
24854         }
24855         //Roo.log('clear currentEl');
24856         this.currentEl = false;
24857         
24858         
24859     },
24860     alignment : {
24861         'left' : ['r-l', [-2,0], 'right'],
24862         'right' : ['l-r', [2,0], 'left'],
24863         'bottom' : ['t-b', [0,2], 'top'],
24864         'top' : [ 'b-t', [0,-2], 'bottom']
24865     }
24866     
24867 });
24868
24869
24870 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
24871     
24872     
24873     bindEl : false,
24874     
24875     delay : null, // can be { show : 300 , hide: 500}
24876     
24877     timeout : null,
24878     
24879     hoverState : null, //???
24880     
24881     placement : 'bottom', 
24882     
24883     getAutoCreate : function(){
24884     
24885         var cfg = {
24886            cls : 'tooltip',
24887            role : 'tooltip',
24888            cn : [
24889                 {
24890                     cls : 'tooltip-arrow'
24891                 },
24892                 {
24893                     cls : 'tooltip-inner'
24894                 }
24895            ]
24896         };
24897         
24898         return cfg;
24899     },
24900     bind : function(el)
24901     {
24902         this.bindEl = el;
24903     },
24904       
24905     
24906     enter : function () {
24907        
24908         if (this.timeout != null) {
24909             clearTimeout(this.timeout);
24910         }
24911         
24912         this.hoverState = 'in';
24913          //Roo.log("enter - show");
24914         if (!this.delay || !this.delay.show) {
24915             this.show();
24916             return;
24917         }
24918         var _t = this;
24919         this.timeout = setTimeout(function () {
24920             if (_t.hoverState == 'in') {
24921                 _t.show();
24922             }
24923         }, this.delay.show);
24924     },
24925     leave : function()
24926     {
24927         clearTimeout(this.timeout);
24928     
24929         this.hoverState = 'out';
24930          if (!this.delay || !this.delay.hide) {
24931             this.hide();
24932             return;
24933         }
24934        
24935         var _t = this;
24936         this.timeout = setTimeout(function () {
24937             //Roo.log("leave - timeout");
24938             
24939             if (_t.hoverState == 'out') {
24940                 _t.hide();
24941                 Roo.bootstrap.Tooltip.currentEl = false;
24942             }
24943         }, delay);
24944     },
24945     
24946     show : function ()
24947     {
24948         if (!this.el) {
24949             this.render(document.body);
24950         }
24951         // set content.
24952         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24953         
24954         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24955         
24956         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24957         
24958         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24959         
24960         var placement = typeof this.placement == 'function' ?
24961             this.placement.call(this, this.el, on_el) :
24962             this.placement;
24963             
24964         var autoToken = /\s?auto?\s?/i;
24965         var autoPlace = autoToken.test(placement);
24966         if (autoPlace) {
24967             placement = placement.replace(autoToken, '') || 'top';
24968         }
24969         
24970         //this.el.detach()
24971         //this.el.setXY([0,0]);
24972         this.el.show();
24973         //this.el.dom.style.display='block';
24974         
24975         //this.el.appendTo(on_el);
24976         
24977         var p = this.getPosition();
24978         var box = this.el.getBox();
24979         
24980         if (autoPlace) {
24981             // fixme..
24982         }
24983         
24984         var align = Roo.bootstrap.Tooltip.alignment[placement];
24985         
24986         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
24987         
24988         if(placement == 'top' || placement == 'bottom'){
24989             if(xy[0] < 0){
24990                 placement = 'right';
24991             }
24992             
24993             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
24994                 placement = 'left';
24995             }
24996             
24997             var scroll = Roo.select('body', true).first().getScroll();
24998             
24999             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25000                 placement = 'top';
25001             }
25002             
25003         }
25004         
25005         align = Roo.bootstrap.Tooltip.alignment[placement];
25006         
25007         this.el.alignTo(this.bindEl, align[0],align[1]);
25008         //var arrow = this.el.select('.arrow',true).first();
25009         //arrow.set(align[2], 
25010         
25011         this.el.addClass(placement);
25012         
25013         this.el.addClass('in fade');
25014         
25015         this.hoverState = null;
25016         
25017         if (this.el.hasClass('fade')) {
25018             // fade it?
25019         }
25020         
25021     },
25022     hide : function()
25023     {
25024          
25025         if (!this.el) {
25026             return;
25027         }
25028         //this.el.setXY([0,0]);
25029         this.el.removeClass('in');
25030         //this.el.hide();
25031         
25032     }
25033     
25034 });
25035  
25036
25037  /*
25038  * - LGPL
25039  *
25040  * Location Picker
25041  * 
25042  */
25043
25044 /**
25045  * @class Roo.bootstrap.LocationPicker
25046  * @extends Roo.bootstrap.Component
25047  * Bootstrap LocationPicker class
25048  * @cfg {Number} latitude Position when init default 0
25049  * @cfg {Number} longitude Position when init default 0
25050  * @cfg {Number} zoom default 15
25051  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25052  * @cfg {Boolean} mapTypeControl default false
25053  * @cfg {Boolean} disableDoubleClickZoom default false
25054  * @cfg {Boolean} scrollwheel default true
25055  * @cfg {Boolean} streetViewControl default false
25056  * @cfg {Number} radius default 0
25057  * @cfg {String} locationName
25058  * @cfg {Boolean} draggable default true
25059  * @cfg {Boolean} enableAutocomplete default false
25060  * @cfg {Boolean} enableReverseGeocode default true
25061  * @cfg {String} markerTitle
25062  * 
25063  * @constructor
25064  * Create a new LocationPicker
25065  * @param {Object} config The config object
25066  */
25067
25068
25069 Roo.bootstrap.LocationPicker = function(config){
25070     
25071     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25072     
25073     this.addEvents({
25074         /**
25075          * @event initial
25076          * Fires when the picker initialized.
25077          * @param {Roo.bootstrap.LocationPicker} this
25078          * @param {Google Location} location
25079          */
25080         initial : true,
25081         /**
25082          * @event positionchanged
25083          * Fires when the picker position changed.
25084          * @param {Roo.bootstrap.LocationPicker} this
25085          * @param {Google Location} location
25086          */
25087         positionchanged : true,
25088         /**
25089          * @event resize
25090          * Fires when the map resize.
25091          * @param {Roo.bootstrap.LocationPicker} this
25092          */
25093         resize : true,
25094         /**
25095          * @event show
25096          * Fires when the map show.
25097          * @param {Roo.bootstrap.LocationPicker} this
25098          */
25099         show : true,
25100         /**
25101          * @event hide
25102          * Fires when the map hide.
25103          * @param {Roo.bootstrap.LocationPicker} this
25104          */
25105         hide : true,
25106         /**
25107          * @event mapClick
25108          * Fires when click the map.
25109          * @param {Roo.bootstrap.LocationPicker} this
25110          * @param {Map event} e
25111          */
25112         mapClick : true,
25113         /**
25114          * @event mapRightClick
25115          * Fires when right click the map.
25116          * @param {Roo.bootstrap.LocationPicker} this
25117          * @param {Map event} e
25118          */
25119         mapRightClick : true,
25120         /**
25121          * @event markerClick
25122          * Fires when click the marker.
25123          * @param {Roo.bootstrap.LocationPicker} this
25124          * @param {Map event} e
25125          */
25126         markerClick : true,
25127         /**
25128          * @event markerRightClick
25129          * Fires when right click the marker.
25130          * @param {Roo.bootstrap.LocationPicker} this
25131          * @param {Map event} e
25132          */
25133         markerRightClick : true,
25134         /**
25135          * @event OverlayViewDraw
25136          * Fires when OverlayView Draw
25137          * @param {Roo.bootstrap.LocationPicker} this
25138          */
25139         OverlayViewDraw : true,
25140         /**
25141          * @event OverlayViewOnAdd
25142          * Fires when OverlayView Draw
25143          * @param {Roo.bootstrap.LocationPicker} this
25144          */
25145         OverlayViewOnAdd : true,
25146         /**
25147          * @event OverlayViewOnRemove
25148          * Fires when OverlayView Draw
25149          * @param {Roo.bootstrap.LocationPicker} this
25150          */
25151         OverlayViewOnRemove : true,
25152         /**
25153          * @event OverlayViewShow
25154          * Fires when OverlayView Draw
25155          * @param {Roo.bootstrap.LocationPicker} this
25156          * @param {Pixel} cpx
25157          */
25158         OverlayViewShow : true,
25159         /**
25160          * @event OverlayViewHide
25161          * Fires when OverlayView Draw
25162          * @param {Roo.bootstrap.LocationPicker} this
25163          */
25164         OverlayViewHide : true,
25165         /**
25166          * @event loadexception
25167          * Fires when load google lib failed.
25168          * @param {Roo.bootstrap.LocationPicker} this
25169          */
25170         loadexception : true
25171     });
25172         
25173 };
25174
25175 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
25176     
25177     gMapContext: false,
25178     
25179     latitude: 0,
25180     longitude: 0,
25181     zoom: 15,
25182     mapTypeId: false,
25183     mapTypeControl: false,
25184     disableDoubleClickZoom: false,
25185     scrollwheel: true,
25186     streetViewControl: false,
25187     radius: 0,
25188     locationName: '',
25189     draggable: true,
25190     enableAutocomplete: false,
25191     enableReverseGeocode: true,
25192     markerTitle: '',
25193     
25194     getAutoCreate: function()
25195     {
25196
25197         var cfg = {
25198             tag: 'div',
25199             cls: 'roo-location-picker'
25200         };
25201         
25202         return cfg
25203     },
25204     
25205     initEvents: function(ct, position)
25206     {       
25207         if(!this.el.getWidth() || this.isApplied()){
25208             return;
25209         }
25210         
25211         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25212         
25213         this.initial();
25214     },
25215     
25216     initial: function()
25217     {
25218         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
25219             this.fireEvent('loadexception', this);
25220             return;
25221         }
25222         
25223         if(!this.mapTypeId){
25224             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
25225         }
25226         
25227         this.gMapContext = this.GMapContext();
25228         
25229         this.initOverlayView();
25230         
25231         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
25232         
25233         var _this = this;
25234                 
25235         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
25236             _this.setPosition(_this.gMapContext.marker.position);
25237         });
25238         
25239         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
25240             _this.fireEvent('mapClick', this, event);
25241             
25242         });
25243
25244         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
25245             _this.fireEvent('mapRightClick', this, event);
25246             
25247         });
25248         
25249         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
25250             _this.fireEvent('markerClick', this, event);
25251             
25252         });
25253
25254         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
25255             _this.fireEvent('markerRightClick', this, event);
25256             
25257         });
25258         
25259         this.setPosition(this.gMapContext.location);
25260         
25261         this.fireEvent('initial', this, this.gMapContext.location);
25262     },
25263     
25264     initOverlayView: function()
25265     {
25266         var _this = this;
25267         
25268         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
25269             
25270             draw: function()
25271             {
25272                 _this.fireEvent('OverlayViewDraw', _this);
25273             },
25274             
25275             onAdd: function()
25276             {
25277                 _this.fireEvent('OverlayViewOnAdd', _this);
25278             },
25279             
25280             onRemove: function()
25281             {
25282                 _this.fireEvent('OverlayViewOnRemove', _this);
25283             },
25284             
25285             show: function(cpx)
25286             {
25287                 _this.fireEvent('OverlayViewShow', _this, cpx);
25288             },
25289             
25290             hide: function()
25291             {
25292                 _this.fireEvent('OverlayViewHide', _this);
25293             }
25294             
25295         });
25296     },
25297     
25298     fromLatLngToContainerPixel: function(event)
25299     {
25300         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
25301     },
25302     
25303     isApplied: function() 
25304     {
25305         return this.getGmapContext() == false ? false : true;
25306     },
25307     
25308     getGmapContext: function() 
25309     {
25310         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
25311     },
25312     
25313     GMapContext: function() 
25314     {
25315         var position = new google.maps.LatLng(this.latitude, this.longitude);
25316         
25317         var _map = new google.maps.Map(this.el.dom, {
25318             center: position,
25319             zoom: this.zoom,
25320             mapTypeId: this.mapTypeId,
25321             mapTypeControl: this.mapTypeControl,
25322             disableDoubleClickZoom: this.disableDoubleClickZoom,
25323             scrollwheel: this.scrollwheel,
25324             streetViewControl: this.streetViewControl,
25325             locationName: this.locationName,
25326             draggable: this.draggable,
25327             enableAutocomplete: this.enableAutocomplete,
25328             enableReverseGeocode: this.enableReverseGeocode
25329         });
25330         
25331         var _marker = new google.maps.Marker({
25332             position: position,
25333             map: _map,
25334             title: this.markerTitle,
25335             draggable: this.draggable
25336         });
25337         
25338         return {
25339             map: _map,
25340             marker: _marker,
25341             circle: null,
25342             location: position,
25343             radius: this.radius,
25344             locationName: this.locationName,
25345             addressComponents: {
25346                 formatted_address: null,
25347                 addressLine1: null,
25348                 addressLine2: null,
25349                 streetName: null,
25350                 streetNumber: null,
25351                 city: null,
25352                 district: null,
25353                 state: null,
25354                 stateOrProvince: null
25355             },
25356             settings: this,
25357             domContainer: this.el.dom,
25358             geodecoder: new google.maps.Geocoder()
25359         };
25360     },
25361     
25362     drawCircle: function(center, radius, options) 
25363     {
25364         if (this.gMapContext.circle != null) {
25365             this.gMapContext.circle.setMap(null);
25366         }
25367         if (radius > 0) {
25368             radius *= 1;
25369             options = Roo.apply({}, options, {
25370                 strokeColor: "#0000FF",
25371                 strokeOpacity: .35,
25372                 strokeWeight: 2,
25373                 fillColor: "#0000FF",
25374                 fillOpacity: .2
25375             });
25376             
25377             options.map = this.gMapContext.map;
25378             options.radius = radius;
25379             options.center = center;
25380             this.gMapContext.circle = new google.maps.Circle(options);
25381             return this.gMapContext.circle;
25382         }
25383         
25384         return null;
25385     },
25386     
25387     setPosition: function(location) 
25388     {
25389         this.gMapContext.location = location;
25390         this.gMapContext.marker.setPosition(location);
25391         this.gMapContext.map.panTo(location);
25392         this.drawCircle(location, this.gMapContext.radius, {});
25393         
25394         var _this = this;
25395         
25396         if (this.gMapContext.settings.enableReverseGeocode) {
25397             this.gMapContext.geodecoder.geocode({
25398                 latLng: this.gMapContext.location
25399             }, function(results, status) {
25400                 
25401                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
25402                     _this.gMapContext.locationName = results[0].formatted_address;
25403                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
25404                     
25405                     _this.fireEvent('positionchanged', this, location);
25406                 }
25407             });
25408             
25409             return;
25410         }
25411         
25412         this.fireEvent('positionchanged', this, location);
25413     },
25414     
25415     resize: function()
25416     {
25417         google.maps.event.trigger(this.gMapContext.map, "resize");
25418         
25419         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
25420         
25421         this.fireEvent('resize', this);
25422     },
25423     
25424     setPositionByLatLng: function(latitude, longitude)
25425     {
25426         this.setPosition(new google.maps.LatLng(latitude, longitude));
25427     },
25428     
25429     getCurrentPosition: function() 
25430     {
25431         return {
25432             latitude: this.gMapContext.location.lat(),
25433             longitude: this.gMapContext.location.lng()
25434         };
25435     },
25436     
25437     getAddressName: function() 
25438     {
25439         return this.gMapContext.locationName;
25440     },
25441     
25442     getAddressComponents: function() 
25443     {
25444         return this.gMapContext.addressComponents;
25445     },
25446     
25447     address_component_from_google_geocode: function(address_components) 
25448     {
25449         var result = {};
25450         
25451         for (var i = 0; i < address_components.length; i++) {
25452             var component = address_components[i];
25453             if (component.types.indexOf("postal_code") >= 0) {
25454                 result.postalCode = component.short_name;
25455             } else if (component.types.indexOf("street_number") >= 0) {
25456                 result.streetNumber = component.short_name;
25457             } else if (component.types.indexOf("route") >= 0) {
25458                 result.streetName = component.short_name;
25459             } else if (component.types.indexOf("neighborhood") >= 0) {
25460                 result.city = component.short_name;
25461             } else if (component.types.indexOf("locality") >= 0) {
25462                 result.city = component.short_name;
25463             } else if (component.types.indexOf("sublocality") >= 0) {
25464                 result.district = component.short_name;
25465             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
25466                 result.stateOrProvince = component.short_name;
25467             } else if (component.types.indexOf("country") >= 0) {
25468                 result.country = component.short_name;
25469             }
25470         }
25471         
25472         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
25473         result.addressLine2 = "";
25474         return result;
25475     },
25476     
25477     setZoomLevel: function(zoom)
25478     {
25479         this.gMapContext.map.setZoom(zoom);
25480     },
25481     
25482     show: function()
25483     {
25484         if(!this.el){
25485             return;
25486         }
25487         
25488         this.el.show();
25489         
25490         this.resize();
25491         
25492         this.fireEvent('show', this);
25493     },
25494     
25495     hide: function()
25496     {
25497         if(!this.el){
25498             return;
25499         }
25500         
25501         this.el.hide();
25502         
25503         this.fireEvent('hide', this);
25504     }
25505     
25506 });
25507
25508 Roo.apply(Roo.bootstrap.LocationPicker, {
25509     
25510     OverlayView : function(map, options)
25511     {
25512         options = options || {};
25513         
25514         this.setMap(map);
25515     }
25516     
25517     
25518 });/*
25519  * - LGPL
25520  *
25521  * Alert
25522  * 
25523  */
25524
25525 /**
25526  * @class Roo.bootstrap.Alert
25527  * @extends Roo.bootstrap.Component
25528  * Bootstrap Alert class
25529  * @cfg {String} title The title of alert
25530  * @cfg {String} html The content of alert
25531  * @cfg {String} weight (  success | info | warning | danger )
25532  * @cfg {String} faicon font-awesomeicon
25533  * 
25534  * @constructor
25535  * Create a new alert
25536  * @param {Object} config The config object
25537  */
25538
25539
25540 Roo.bootstrap.Alert = function(config){
25541     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
25542     
25543 };
25544
25545 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
25546     
25547     title: '',
25548     html: '',
25549     weight: false,
25550     faicon: false,
25551     
25552     getAutoCreate : function()
25553     {
25554         
25555         var cfg = {
25556             tag : 'div',
25557             cls : 'alert',
25558             cn : [
25559                 {
25560                     tag : 'i',
25561                     cls : 'roo-alert-icon'
25562                     
25563                 },
25564                 {
25565                     tag : 'b',
25566                     cls : 'roo-alert-title',
25567                     html : this.title
25568                 },
25569                 {
25570                     tag : 'span',
25571                     cls : 'roo-alert-text',
25572                     html : this.html
25573                 }
25574             ]
25575         };
25576         
25577         if(this.faicon){
25578             cfg.cn[0].cls += ' fa ' + this.faicon;
25579         }
25580         
25581         if(this.weight){
25582             cfg.cls += ' alert-' + this.weight;
25583         }
25584         
25585         return cfg;
25586     },
25587     
25588     initEvents: function() 
25589     {
25590         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25591     },
25592     
25593     setTitle : function(str)
25594     {
25595         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
25596     },
25597     
25598     setText : function(str)
25599     {
25600         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
25601     },
25602     
25603     setWeight : function(weight)
25604     {
25605         if(this.weight){
25606             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
25607         }
25608         
25609         this.weight = weight;
25610         
25611         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
25612     },
25613     
25614     setIcon : function(icon)
25615     {
25616         if(this.faicon){
25617             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
25618         }
25619         
25620         this.faicon = icon;
25621         
25622         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
25623     },
25624     
25625     hide: function() 
25626     {
25627         this.el.hide();   
25628     },
25629     
25630     show: function() 
25631     {  
25632         this.el.show();   
25633     }
25634     
25635 });
25636
25637  
25638 /*
25639 * Licence: LGPL
25640 */
25641
25642 /**
25643  * @class Roo.bootstrap.UploadCropbox
25644  * @extends Roo.bootstrap.Component
25645  * Bootstrap UploadCropbox class
25646  * @cfg {String} emptyText show when image has been loaded
25647  * @cfg {String} rotateNotify show when image too small to rotate
25648  * @cfg {Number} errorTimeout default 3000
25649  * @cfg {Number} minWidth default 300
25650  * @cfg {Number} minHeight default 300
25651  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
25652  * @cfg {Boolean} isDocument (true|false) default false
25653  * @cfg {String} url action url
25654  * @cfg {String} paramName default 'imageUpload'
25655  * @cfg {String} method default POST
25656  * @cfg {Boolean} loadMask (true|false) default true
25657  * @cfg {Boolean} loadingText default 'Loading...'
25658  * 
25659  * @constructor
25660  * Create a new UploadCropbox
25661  * @param {Object} config The config object
25662  */
25663
25664 Roo.bootstrap.UploadCropbox = function(config){
25665     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
25666     
25667     this.addEvents({
25668         /**
25669          * @event beforeselectfile
25670          * Fire before select file
25671          * @param {Roo.bootstrap.UploadCropbox} this
25672          */
25673         "beforeselectfile" : true,
25674         /**
25675          * @event initial
25676          * Fire after initEvent
25677          * @param {Roo.bootstrap.UploadCropbox} this
25678          */
25679         "initial" : true,
25680         /**
25681          * @event crop
25682          * Fire after initEvent
25683          * @param {Roo.bootstrap.UploadCropbox} this
25684          * @param {String} data
25685          */
25686         "crop" : true,
25687         /**
25688          * @event prepare
25689          * Fire when preparing the file data
25690          * @param {Roo.bootstrap.UploadCropbox} this
25691          * @param {Object} file
25692          */
25693         "prepare" : true,
25694         /**
25695          * @event exception
25696          * Fire when get exception
25697          * @param {Roo.bootstrap.UploadCropbox} this
25698          * @param {XMLHttpRequest} xhr
25699          */
25700         "exception" : true,
25701         /**
25702          * @event beforeloadcanvas
25703          * Fire before load the canvas
25704          * @param {Roo.bootstrap.UploadCropbox} this
25705          * @param {String} src
25706          */
25707         "beforeloadcanvas" : true,
25708         /**
25709          * @event trash
25710          * Fire when trash image
25711          * @param {Roo.bootstrap.UploadCropbox} this
25712          */
25713         "trash" : true,
25714         /**
25715          * @event download
25716          * Fire when download the image
25717          * @param {Roo.bootstrap.UploadCropbox} this
25718          */
25719         "download" : true,
25720         /**
25721          * @event footerbuttonclick
25722          * Fire when footerbuttonclick
25723          * @param {Roo.bootstrap.UploadCropbox} this
25724          * @param {String} type
25725          */
25726         "footerbuttonclick" : true,
25727         /**
25728          * @event resize
25729          * Fire when resize
25730          * @param {Roo.bootstrap.UploadCropbox} this
25731          */
25732         "resize" : true,
25733         /**
25734          * @event rotate
25735          * Fire when rotate the image
25736          * @param {Roo.bootstrap.UploadCropbox} this
25737          * @param {String} pos
25738          */
25739         "rotate" : true,
25740         /**
25741          * @event inspect
25742          * Fire when inspect the file
25743          * @param {Roo.bootstrap.UploadCropbox} this
25744          * @param {Object} file
25745          */
25746         "inspect" : true,
25747         /**
25748          * @event upload
25749          * Fire when xhr upload the file
25750          * @param {Roo.bootstrap.UploadCropbox} this
25751          * @param {Object} data
25752          */
25753         "upload" : true,
25754         /**
25755          * @event arrange
25756          * Fire when arrange the file data
25757          * @param {Roo.bootstrap.UploadCropbox} this
25758          * @param {Object} formData
25759          */
25760         "arrange" : true
25761     });
25762     
25763     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25764 };
25765
25766 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
25767     
25768     emptyText : 'Click to upload image',
25769     rotateNotify : 'Image is too small to rotate',
25770     errorTimeout : 3000,
25771     scale : 0,
25772     baseScale : 1,
25773     rotate : 0,
25774     dragable : false,
25775     pinching : false,
25776     mouseX : 0,
25777     mouseY : 0,
25778     cropData : false,
25779     minWidth : 300,
25780     minHeight : 300,
25781     file : false,
25782     exif : {},
25783     baseRotate : 1,
25784     cropType : 'image/jpeg',
25785     buttons : false,
25786     canvasLoaded : false,
25787     isDocument : false,
25788     method : 'POST',
25789     paramName : 'imageUpload',
25790     loadMask : true,
25791     loadingText : 'Loading...',
25792     maskEl : false,
25793     
25794     getAutoCreate : function()
25795     {
25796         var cfg = {
25797             tag : 'div',
25798             cls : 'roo-upload-cropbox',
25799             cn : [
25800                 {
25801                     tag : 'input',
25802                     cls : 'roo-upload-cropbox-selector',
25803                     type : 'file'
25804                 },
25805                 {
25806                     tag : 'div',
25807                     cls : 'roo-upload-cropbox-body',
25808                     style : 'cursor:pointer',
25809                     cn : [
25810                         {
25811                             tag : 'div',
25812                             cls : 'roo-upload-cropbox-preview'
25813                         },
25814                         {
25815                             tag : 'div',
25816                             cls : 'roo-upload-cropbox-thumb'
25817                         },
25818                         {
25819                             tag : 'div',
25820                             cls : 'roo-upload-cropbox-empty-notify',
25821                             html : this.emptyText
25822                         },
25823                         {
25824                             tag : 'div',
25825                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25826                             html : this.rotateNotify
25827                         }
25828                     ]
25829                 },
25830                 {
25831                     tag : 'div',
25832                     cls : 'roo-upload-cropbox-footer',
25833                     cn : {
25834                         tag : 'div',
25835                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25836                         cn : []
25837                     }
25838                 }
25839             ]
25840         };
25841         
25842         return cfg;
25843     },
25844     
25845     onRender : function(ct, position)
25846     {
25847         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25848         
25849         if (this.buttons.length) {
25850             
25851             Roo.each(this.buttons, function(bb) {
25852                 
25853                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25854                 
25855                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25856                 
25857             }, this);
25858         }
25859         
25860         if(this.loadMask){
25861             this.maskEl = this.el;
25862         }
25863     },
25864     
25865     initEvents : function()
25866     {
25867         this.urlAPI = (window.createObjectURL && window) || 
25868                                 (window.URL && URL.revokeObjectURL && URL) || 
25869                                 (window.webkitURL && webkitURL);
25870                         
25871         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25872         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25873         
25874         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25875         this.selectorEl.hide();
25876         
25877         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25878         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25879         
25880         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25881         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25882         this.thumbEl.hide();
25883         
25884         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25885         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25886         
25887         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25888         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25889         this.errorEl.hide();
25890         
25891         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25892         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25893         this.footerEl.hide();
25894         
25895         this.setThumbBoxSize();
25896         
25897         this.bind();
25898         
25899         this.resize();
25900         
25901         this.fireEvent('initial', this);
25902     },
25903
25904     bind : function()
25905     {
25906         var _this = this;
25907         
25908         window.addEventListener("resize", function() { _this.resize(); } );
25909         
25910         this.bodyEl.on('click', this.beforeSelectFile, this);
25911         
25912         if(Roo.isTouch){
25913             this.bodyEl.on('touchstart', this.onTouchStart, this);
25914             this.bodyEl.on('touchmove', this.onTouchMove, this);
25915             this.bodyEl.on('touchend', this.onTouchEnd, this);
25916         }
25917         
25918         if(!Roo.isTouch){
25919             this.bodyEl.on('mousedown', this.onMouseDown, this);
25920             this.bodyEl.on('mousemove', this.onMouseMove, this);
25921             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25922             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25923             Roo.get(document).on('mouseup', this.onMouseUp, this);
25924         }
25925         
25926         this.selectorEl.on('change', this.onFileSelected, this);
25927     },
25928     
25929     reset : function()
25930     {    
25931         this.scale = 0;
25932         this.baseScale = 1;
25933         this.rotate = 0;
25934         this.baseRotate = 1;
25935         this.dragable = false;
25936         this.pinching = false;
25937         this.mouseX = 0;
25938         this.mouseY = 0;
25939         this.cropData = false;
25940         this.notifyEl.dom.innerHTML = this.emptyText;
25941         
25942         this.selectorEl.dom.value = '';
25943         
25944     },
25945     
25946     resize : function()
25947     {
25948         if(this.fireEvent('resize', this) != false){
25949             this.setThumbBoxPosition();
25950             this.setCanvasPosition();
25951         }
25952     },
25953     
25954     onFooterButtonClick : function(e, el, o, type)
25955     {
25956         switch (type) {
25957             case 'rotate-left' :
25958                 this.onRotateLeft(e);
25959                 break;
25960             case 'rotate-right' :
25961                 this.onRotateRight(e);
25962                 break;
25963             case 'picture' :
25964                 this.beforeSelectFile(e);
25965                 break;
25966             case 'trash' :
25967                 this.trash(e);
25968                 break;
25969             case 'crop' :
25970                 this.crop(e);
25971                 break;
25972             case 'download' :
25973                 this.download(e);
25974                 break;
25975             default :
25976                 break;
25977         }
25978         
25979         this.fireEvent('footerbuttonclick', this, type);
25980     },
25981     
25982     beforeSelectFile : function(e)
25983     {
25984         e.preventDefault();
25985         
25986         if(this.fireEvent('beforeselectfile', this) != false){
25987             this.selectorEl.dom.click();
25988         }
25989     },
25990     
25991     onFileSelected : function(e)
25992     {
25993         e.preventDefault();
25994         
25995         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25996             return;
25997         }
25998         
25999         var file = this.selectorEl.dom.files[0];
26000         
26001         if(this.fireEvent('inspect', this, file) != false){
26002             this.prepare(file);
26003         }
26004         
26005     },
26006     
26007     trash : function(e)
26008     {
26009         this.fireEvent('trash', this);
26010     },
26011     
26012     download : function(e)
26013     {
26014         this.fireEvent('download', this);
26015     },
26016     
26017     loadCanvas : function(src)
26018     {   
26019         if(this.fireEvent('beforeloadcanvas', this, src) != false){
26020             
26021             this.reset();
26022             
26023             this.imageEl = document.createElement('img');
26024             
26025             var _this = this;
26026             
26027             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26028             
26029             this.imageEl.src = src;
26030         }
26031     },
26032     
26033     onLoadCanvas : function()
26034     {   
26035         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26036         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26037         
26038         this.bodyEl.un('click', this.beforeSelectFile, this);
26039         
26040         this.notifyEl.hide();
26041         this.thumbEl.show();
26042         this.footerEl.show();
26043         
26044         this.baseRotateLevel();
26045         
26046         if(this.isDocument){
26047             this.setThumbBoxSize();
26048         }
26049         
26050         this.setThumbBoxPosition();
26051         
26052         this.baseScaleLevel();
26053         
26054         this.draw();
26055         
26056         this.resize();
26057         
26058         this.canvasLoaded = true;
26059         
26060         if(this.loadMask){
26061             this.maskEl.unmask();
26062         }
26063         
26064     },
26065     
26066     setCanvasPosition : function()
26067     {   
26068         if(!this.canvasEl){
26069             return;
26070         }
26071         
26072         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26073         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26074         
26075         this.previewEl.setLeft(pw);
26076         this.previewEl.setTop(ph);
26077         
26078     },
26079     
26080     onMouseDown : function(e)
26081     {   
26082         e.stopEvent();
26083         
26084         this.dragable = true;
26085         this.pinching = false;
26086         
26087         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26088             this.dragable = false;
26089             return;
26090         }
26091         
26092         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26093         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26094         
26095     },
26096     
26097     onMouseMove : function(e)
26098     {   
26099         e.stopEvent();
26100         
26101         if(!this.canvasLoaded){
26102             return;
26103         }
26104         
26105         if (!this.dragable){
26106             return;
26107         }
26108         
26109         var minX = Math.ceil(this.thumbEl.getLeft(true));
26110         var minY = Math.ceil(this.thumbEl.getTop(true));
26111         
26112         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
26113         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
26114         
26115         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26116         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26117         
26118         x = x - this.mouseX;
26119         y = y - this.mouseY;
26120         
26121         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
26122         var bgY = Math.ceil(y + this.previewEl.getTop(true));
26123         
26124         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
26125         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
26126         
26127         this.previewEl.setLeft(bgX);
26128         this.previewEl.setTop(bgY);
26129         
26130         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26131         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26132     },
26133     
26134     onMouseUp : function(e)
26135     {   
26136         e.stopEvent();
26137         
26138         this.dragable = false;
26139     },
26140     
26141     onMouseWheel : function(e)
26142     {   
26143         e.stopEvent();
26144         
26145         this.startScale = this.scale;
26146         
26147         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
26148         
26149         if(!this.zoomable()){
26150             this.scale = this.startScale;
26151             return;
26152         }
26153         
26154         this.draw();
26155         
26156         return;
26157     },
26158     
26159     zoomable : function()
26160     {
26161         var minScale = this.thumbEl.getWidth() / this.minWidth;
26162         
26163         if(this.minWidth < this.minHeight){
26164             minScale = this.thumbEl.getHeight() / this.minHeight;
26165         }
26166         
26167         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
26168         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
26169         
26170         if(
26171                 this.isDocument &&
26172                 (this.rotate == 0 || this.rotate == 180) && 
26173                 (
26174                     width > this.imageEl.OriginWidth || 
26175                     height > this.imageEl.OriginHeight ||
26176                     (width < this.minWidth && height < this.minHeight)
26177                 )
26178         ){
26179             return false;
26180         }
26181         
26182         if(
26183                 this.isDocument &&
26184                 (this.rotate == 90 || this.rotate == 270) && 
26185                 (
26186                     width > this.imageEl.OriginWidth || 
26187                     height > this.imageEl.OriginHeight ||
26188                     (width < this.minHeight && height < this.minWidth)
26189                 )
26190         ){
26191             return false;
26192         }
26193         
26194         if(
26195                 !this.isDocument &&
26196                 (this.rotate == 0 || this.rotate == 180) && 
26197                 (
26198                     width < this.minWidth || 
26199                     width > this.imageEl.OriginWidth || 
26200                     height < this.minHeight || 
26201                     height > this.imageEl.OriginHeight
26202                 )
26203         ){
26204             return false;
26205         }
26206         
26207         if(
26208                 !this.isDocument &&
26209                 (this.rotate == 90 || this.rotate == 270) && 
26210                 (
26211                     width < this.minHeight || 
26212                     width > this.imageEl.OriginWidth || 
26213                     height < this.minWidth || 
26214                     height > this.imageEl.OriginHeight
26215                 )
26216         ){
26217             return false;
26218         }
26219         
26220         return true;
26221         
26222     },
26223     
26224     onRotateLeft : function(e)
26225     {   
26226         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26227             
26228             var minScale = this.thumbEl.getWidth() / this.minWidth;
26229             
26230             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26231             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26232             
26233             this.startScale = this.scale;
26234             
26235             while (this.getScaleLevel() < minScale){
26236             
26237                 this.scale = this.scale + 1;
26238                 
26239                 if(!this.zoomable()){
26240                     break;
26241                 }
26242                 
26243                 if(
26244                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26245                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26246                 ){
26247                     continue;
26248                 }
26249                 
26250                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26251
26252                 this.draw();
26253                 
26254                 return;
26255             }
26256             
26257             this.scale = this.startScale;
26258             
26259             this.onRotateFail();
26260             
26261             return false;
26262         }
26263         
26264         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26265
26266         if(this.isDocument){
26267             this.setThumbBoxSize();
26268             this.setThumbBoxPosition();
26269             this.setCanvasPosition();
26270         }
26271         
26272         this.draw();
26273         
26274         this.fireEvent('rotate', this, 'left');
26275         
26276     },
26277     
26278     onRotateRight : function(e)
26279     {
26280         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26281             
26282             var minScale = this.thumbEl.getWidth() / this.minWidth;
26283         
26284             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26285             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26286             
26287             this.startScale = this.scale;
26288             
26289             while (this.getScaleLevel() < minScale){
26290             
26291                 this.scale = this.scale + 1;
26292                 
26293                 if(!this.zoomable()){
26294                     break;
26295                 }
26296                 
26297                 if(
26298                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26299                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26300                 ){
26301                     continue;
26302                 }
26303                 
26304                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26305
26306                 this.draw();
26307                 
26308                 return;
26309             }
26310             
26311             this.scale = this.startScale;
26312             
26313             this.onRotateFail();
26314             
26315             return false;
26316         }
26317         
26318         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26319
26320         if(this.isDocument){
26321             this.setThumbBoxSize();
26322             this.setThumbBoxPosition();
26323             this.setCanvasPosition();
26324         }
26325         
26326         this.draw();
26327         
26328         this.fireEvent('rotate', this, 'right');
26329     },
26330     
26331     onRotateFail : function()
26332     {
26333         this.errorEl.show(true);
26334         
26335         var _this = this;
26336         
26337         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
26338     },
26339     
26340     draw : function()
26341     {
26342         this.previewEl.dom.innerHTML = '';
26343         
26344         var canvasEl = document.createElement("canvas");
26345         
26346         var contextEl = canvasEl.getContext("2d");
26347         
26348         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26349         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26350         var center = this.imageEl.OriginWidth / 2;
26351         
26352         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
26353             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26354             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26355             center = this.imageEl.OriginHeight / 2;
26356         }
26357         
26358         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
26359         
26360         contextEl.translate(center, center);
26361         contextEl.rotate(this.rotate * Math.PI / 180);
26362
26363         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26364         
26365         this.canvasEl = document.createElement("canvas");
26366         
26367         this.contextEl = this.canvasEl.getContext("2d");
26368         
26369         switch (this.rotate) {
26370             case 0 :
26371                 
26372                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26373                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26374                 
26375                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26376                 
26377                 break;
26378             case 90 : 
26379                 
26380                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26381                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26382                 
26383                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26384                     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);
26385                     break;
26386                 }
26387                 
26388                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26389                 
26390                 break;
26391             case 180 :
26392                 
26393                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26394                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26395                 
26396                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26397                     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);
26398                     break;
26399                 }
26400                 
26401                 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);
26402                 
26403                 break;
26404             case 270 :
26405                 
26406                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26407                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26408         
26409                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26410                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26411                     break;
26412                 }
26413                 
26414                 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);
26415                 
26416                 break;
26417             default : 
26418                 break;
26419         }
26420         
26421         this.previewEl.appendChild(this.canvasEl);
26422         
26423         this.setCanvasPosition();
26424     },
26425     
26426     crop : function()
26427     {
26428         if(!this.canvasLoaded){
26429             return;
26430         }
26431         
26432         var imageCanvas = document.createElement("canvas");
26433         
26434         var imageContext = imageCanvas.getContext("2d");
26435         
26436         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26437         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26438         
26439         var center = imageCanvas.width / 2;
26440         
26441         imageContext.translate(center, center);
26442         
26443         imageContext.rotate(this.rotate * Math.PI / 180);
26444         
26445         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26446         
26447         var canvas = document.createElement("canvas");
26448         
26449         var context = canvas.getContext("2d");
26450                 
26451         canvas.width = this.minWidth;
26452         canvas.height = this.minHeight;
26453
26454         switch (this.rotate) {
26455             case 0 :
26456                 
26457                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26458                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26459                 
26460                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26461                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26462                 
26463                 var targetWidth = this.minWidth - 2 * x;
26464                 var targetHeight = this.minHeight - 2 * y;
26465                 
26466                 var scale = 1;
26467                 
26468                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26469                     scale = targetWidth / width;
26470                 }
26471                 
26472                 if(x > 0 && y == 0){
26473                     scale = targetHeight / height;
26474                 }
26475                 
26476                 if(x > 0 && y > 0){
26477                     scale = targetWidth / width;
26478                     
26479                     if(width < height){
26480                         scale = targetHeight / height;
26481                     }
26482                 }
26483                 
26484                 context.scale(scale, scale);
26485                 
26486                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26487                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26488
26489                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26490                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26491
26492                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26493                 
26494                 break;
26495             case 90 : 
26496                 
26497                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26498                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26499                 
26500                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26501                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26502                 
26503                 var targetWidth = this.minWidth - 2 * x;
26504                 var targetHeight = this.minHeight - 2 * y;
26505                 
26506                 var scale = 1;
26507                 
26508                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26509                     scale = targetWidth / width;
26510                 }
26511                 
26512                 if(x > 0 && y == 0){
26513                     scale = targetHeight / height;
26514                 }
26515                 
26516                 if(x > 0 && y > 0){
26517                     scale = targetWidth / width;
26518                     
26519                     if(width < height){
26520                         scale = targetHeight / height;
26521                     }
26522                 }
26523                 
26524                 context.scale(scale, scale);
26525                 
26526                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26527                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26528
26529                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26530                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26531                 
26532                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26533                 
26534                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26535                 
26536                 break;
26537             case 180 :
26538                 
26539                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26540                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26541                 
26542                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26543                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26544                 
26545                 var targetWidth = this.minWidth - 2 * x;
26546                 var targetHeight = this.minHeight - 2 * y;
26547                 
26548                 var scale = 1;
26549                 
26550                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26551                     scale = targetWidth / width;
26552                 }
26553                 
26554                 if(x > 0 && y == 0){
26555                     scale = targetHeight / height;
26556                 }
26557                 
26558                 if(x > 0 && y > 0){
26559                     scale = targetWidth / width;
26560                     
26561                     if(width < height){
26562                         scale = targetHeight / height;
26563                     }
26564                 }
26565                 
26566                 context.scale(scale, scale);
26567                 
26568                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26569                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26570
26571                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26572                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26573
26574                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26575                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26576                 
26577                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26578                 
26579                 break;
26580             case 270 :
26581                 
26582                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26583                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26584                 
26585                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26586                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26587                 
26588                 var targetWidth = this.minWidth - 2 * x;
26589                 var targetHeight = this.minHeight - 2 * y;
26590                 
26591                 var scale = 1;
26592                 
26593                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26594                     scale = targetWidth / width;
26595                 }
26596                 
26597                 if(x > 0 && y == 0){
26598                     scale = targetHeight / height;
26599                 }
26600                 
26601                 if(x > 0 && y > 0){
26602                     scale = targetWidth / width;
26603                     
26604                     if(width < height){
26605                         scale = targetHeight / height;
26606                     }
26607                 }
26608                 
26609                 context.scale(scale, scale);
26610                 
26611                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26612                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26613
26614                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26615                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26616                 
26617                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26618                 
26619                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26620                 
26621                 break;
26622             default : 
26623                 break;
26624         }
26625         
26626         this.cropData = canvas.toDataURL(this.cropType);
26627         
26628         if(this.fireEvent('crop', this, this.cropData) !== false){
26629             this.process(this.file, this.cropData);
26630         }
26631         
26632         return;
26633         
26634     },
26635     
26636     setThumbBoxSize : function()
26637     {
26638         var width, height;
26639         
26640         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
26641             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
26642             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
26643             
26644             this.minWidth = width;
26645             this.minHeight = height;
26646             
26647             if(this.rotate == 90 || this.rotate == 270){
26648                 this.minWidth = height;
26649                 this.minHeight = width;
26650             }
26651         }
26652         
26653         height = 300;
26654         width = Math.ceil(this.minWidth * height / this.minHeight);
26655         
26656         if(this.minWidth > this.minHeight){
26657             width = 300;
26658             height = Math.ceil(this.minHeight * width / this.minWidth);
26659         }
26660         
26661         this.thumbEl.setStyle({
26662             width : width + 'px',
26663             height : height + 'px'
26664         });
26665
26666         return;
26667             
26668     },
26669     
26670     setThumbBoxPosition : function()
26671     {
26672         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
26673         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
26674         
26675         this.thumbEl.setLeft(x);
26676         this.thumbEl.setTop(y);
26677         
26678     },
26679     
26680     baseRotateLevel : function()
26681     {
26682         this.baseRotate = 1;
26683         
26684         if(
26685                 typeof(this.exif) != 'undefined' &&
26686                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
26687                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
26688         ){
26689             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
26690         }
26691         
26692         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
26693         
26694     },
26695     
26696     baseScaleLevel : function()
26697     {
26698         var width, height;
26699         
26700         if(this.isDocument){
26701             
26702             if(this.baseRotate == 6 || this.baseRotate == 8){
26703             
26704                 height = this.thumbEl.getHeight();
26705                 this.baseScale = height / this.imageEl.OriginWidth;
26706
26707                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
26708                     width = this.thumbEl.getWidth();
26709                     this.baseScale = width / this.imageEl.OriginHeight;
26710                 }
26711
26712                 return;
26713             }
26714
26715             height = this.thumbEl.getHeight();
26716             this.baseScale = height / this.imageEl.OriginHeight;
26717
26718             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
26719                 width = this.thumbEl.getWidth();
26720                 this.baseScale = width / this.imageEl.OriginWidth;
26721             }
26722
26723             return;
26724         }
26725         
26726         if(this.baseRotate == 6 || this.baseRotate == 8){
26727             
26728             width = this.thumbEl.getHeight();
26729             this.baseScale = width / this.imageEl.OriginHeight;
26730             
26731             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
26732                 height = this.thumbEl.getWidth();
26733                 this.baseScale = height / this.imageEl.OriginHeight;
26734             }
26735             
26736             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26737                 height = this.thumbEl.getWidth();
26738                 this.baseScale = height / this.imageEl.OriginHeight;
26739                 
26740                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
26741                     width = this.thumbEl.getHeight();
26742                     this.baseScale = width / this.imageEl.OriginWidth;
26743                 }
26744             }
26745             
26746             return;
26747         }
26748         
26749         width = this.thumbEl.getWidth();
26750         this.baseScale = width / this.imageEl.OriginWidth;
26751         
26752         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
26753             height = this.thumbEl.getHeight();
26754             this.baseScale = height / this.imageEl.OriginHeight;
26755         }
26756         
26757         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26758             
26759             height = this.thumbEl.getHeight();
26760             this.baseScale = height / this.imageEl.OriginHeight;
26761             
26762             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26763                 width = this.thumbEl.getWidth();
26764                 this.baseScale = width / this.imageEl.OriginWidth;
26765             }
26766             
26767         }
26768         
26769         return;
26770     },
26771     
26772     getScaleLevel : function()
26773     {
26774         return this.baseScale * Math.pow(1.1, this.scale);
26775     },
26776     
26777     onTouchStart : function(e)
26778     {
26779         if(!this.canvasLoaded){
26780             this.beforeSelectFile(e);
26781             return;
26782         }
26783         
26784         var touches = e.browserEvent.touches;
26785         
26786         if(!touches){
26787             return;
26788         }
26789         
26790         if(touches.length == 1){
26791             this.onMouseDown(e);
26792             return;
26793         }
26794         
26795         if(touches.length != 2){
26796             return;
26797         }
26798         
26799         var coords = [];
26800         
26801         for(var i = 0, finger; finger = touches[i]; i++){
26802             coords.push(finger.pageX, finger.pageY);
26803         }
26804         
26805         var x = Math.pow(coords[0] - coords[2], 2);
26806         var y = Math.pow(coords[1] - coords[3], 2);
26807         
26808         this.startDistance = Math.sqrt(x + y);
26809         
26810         this.startScale = this.scale;
26811         
26812         this.pinching = true;
26813         this.dragable = false;
26814         
26815     },
26816     
26817     onTouchMove : function(e)
26818     {
26819         if(!this.pinching && !this.dragable){
26820             return;
26821         }
26822         
26823         var touches = e.browserEvent.touches;
26824         
26825         if(!touches){
26826             return;
26827         }
26828         
26829         if(this.dragable){
26830             this.onMouseMove(e);
26831             return;
26832         }
26833         
26834         var coords = [];
26835         
26836         for(var i = 0, finger; finger = touches[i]; i++){
26837             coords.push(finger.pageX, finger.pageY);
26838         }
26839         
26840         var x = Math.pow(coords[0] - coords[2], 2);
26841         var y = Math.pow(coords[1] - coords[3], 2);
26842         
26843         this.endDistance = Math.sqrt(x + y);
26844         
26845         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26846         
26847         if(!this.zoomable()){
26848             this.scale = this.startScale;
26849             return;
26850         }
26851         
26852         this.draw();
26853         
26854     },
26855     
26856     onTouchEnd : function(e)
26857     {
26858         this.pinching = false;
26859         this.dragable = false;
26860         
26861     },
26862     
26863     process : function(file, crop)
26864     {
26865         if(this.loadMask){
26866             this.maskEl.mask(this.loadingText);
26867         }
26868         
26869         this.xhr = new XMLHttpRequest();
26870         
26871         file.xhr = this.xhr;
26872
26873         this.xhr.open(this.method, this.url, true);
26874         
26875         var headers = {
26876             "Accept": "application/json",
26877             "Cache-Control": "no-cache",
26878             "X-Requested-With": "XMLHttpRequest"
26879         };
26880         
26881         for (var headerName in headers) {
26882             var headerValue = headers[headerName];
26883             if (headerValue) {
26884                 this.xhr.setRequestHeader(headerName, headerValue);
26885             }
26886         }
26887         
26888         var _this = this;
26889         
26890         this.xhr.onload = function()
26891         {
26892             _this.xhrOnLoad(_this.xhr);
26893         }
26894         
26895         this.xhr.onerror = function()
26896         {
26897             _this.xhrOnError(_this.xhr);
26898         }
26899         
26900         var formData = new FormData();
26901
26902         formData.append('returnHTML', 'NO');
26903         
26904         if(crop){
26905             formData.append('crop', crop);
26906         }
26907         
26908         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26909             formData.append(this.paramName, file, file.name);
26910         }
26911         
26912         if(typeof(file.filename) != 'undefined'){
26913             formData.append('filename', file.filename);
26914         }
26915         
26916         if(typeof(file.mimetype) != 'undefined'){
26917             formData.append('mimetype', file.mimetype);
26918         }
26919         
26920         if(this.fireEvent('arrange', this, formData) != false){
26921             this.xhr.send(formData);
26922         };
26923     },
26924     
26925     xhrOnLoad : function(xhr)
26926     {
26927         if(this.loadMask){
26928             this.maskEl.unmask();
26929         }
26930         
26931         if (xhr.readyState !== 4) {
26932             this.fireEvent('exception', this, xhr);
26933             return;
26934         }
26935
26936         var response = Roo.decode(xhr.responseText);
26937         
26938         if(!response.success){
26939             this.fireEvent('exception', this, xhr);
26940             return;
26941         }
26942         
26943         var response = Roo.decode(xhr.responseText);
26944         
26945         this.fireEvent('upload', this, response);
26946         
26947     },
26948     
26949     xhrOnError : function()
26950     {
26951         if(this.loadMask){
26952             this.maskEl.unmask();
26953         }
26954         
26955         Roo.log('xhr on error');
26956         
26957         var response = Roo.decode(xhr.responseText);
26958           
26959         Roo.log(response);
26960         
26961     },
26962     
26963     prepare : function(file)
26964     {   
26965         if(this.loadMask){
26966             this.maskEl.mask(this.loadingText);
26967         }
26968         
26969         this.file = false;
26970         this.exif = {};
26971         
26972         if(typeof(file) === 'string'){
26973             this.loadCanvas(file);
26974             return;
26975         }
26976         
26977         if(!file || !this.urlAPI){
26978             return;
26979         }
26980         
26981         this.file = file;
26982         this.cropType = file.type;
26983         
26984         var _this = this;
26985         
26986         if(this.fireEvent('prepare', this, this.file) != false){
26987             
26988             var reader = new FileReader();
26989             
26990             reader.onload = function (e) {
26991                 if (e.target.error) {
26992                     Roo.log(e.target.error);
26993                     return;
26994                 }
26995                 
26996                 var buffer = e.target.result,
26997                     dataView = new DataView(buffer),
26998                     offset = 2,
26999                     maxOffset = dataView.byteLength - 4,
27000                     markerBytes,
27001                     markerLength;
27002                 
27003                 if (dataView.getUint16(0) === 0xffd8) {
27004                     while (offset < maxOffset) {
27005                         markerBytes = dataView.getUint16(offset);
27006                         
27007                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27008                             markerLength = dataView.getUint16(offset + 2) + 2;
27009                             if (offset + markerLength > dataView.byteLength) {
27010                                 Roo.log('Invalid meta data: Invalid segment size.');
27011                                 break;
27012                             }
27013                             
27014                             if(markerBytes == 0xffe1){
27015                                 _this.parseExifData(
27016                                     dataView,
27017                                     offset,
27018                                     markerLength
27019                                 );
27020                             }
27021                             
27022                             offset += markerLength;
27023                             
27024                             continue;
27025                         }
27026                         
27027                         break;
27028                     }
27029                     
27030                 }
27031                 
27032                 var url = _this.urlAPI.createObjectURL(_this.file);
27033                 
27034                 _this.loadCanvas(url);
27035                 
27036                 return;
27037             }
27038             
27039             reader.readAsArrayBuffer(this.file);
27040             
27041         }
27042         
27043     },
27044     
27045     parseExifData : function(dataView, offset, length)
27046     {
27047         var tiffOffset = offset + 10,
27048             littleEndian,
27049             dirOffset;
27050     
27051         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27052             // No Exif data, might be XMP data instead
27053             return;
27054         }
27055         
27056         // Check for the ASCII code for "Exif" (0x45786966):
27057         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27058             // No Exif data, might be XMP data instead
27059             return;
27060         }
27061         if (tiffOffset + 8 > dataView.byteLength) {
27062             Roo.log('Invalid Exif data: Invalid segment size.');
27063             return;
27064         }
27065         // Check for the two null bytes:
27066         if (dataView.getUint16(offset + 8) !== 0x0000) {
27067             Roo.log('Invalid Exif data: Missing byte alignment offset.');
27068             return;
27069         }
27070         // Check the byte alignment:
27071         switch (dataView.getUint16(tiffOffset)) {
27072         case 0x4949:
27073             littleEndian = true;
27074             break;
27075         case 0x4D4D:
27076             littleEndian = false;
27077             break;
27078         default:
27079             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27080             return;
27081         }
27082         // Check for the TIFF tag marker (0x002A):
27083         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27084             Roo.log('Invalid Exif data: Missing TIFF marker.');
27085             return;
27086         }
27087         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27088         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27089         
27090         this.parseExifTags(
27091             dataView,
27092             tiffOffset,
27093             tiffOffset + dirOffset,
27094             littleEndian
27095         );
27096     },
27097     
27098     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27099     {
27100         var tagsNumber,
27101             dirEndOffset,
27102             i;
27103         if (dirOffset + 6 > dataView.byteLength) {
27104             Roo.log('Invalid Exif data: Invalid directory offset.');
27105             return;
27106         }
27107         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
27108         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
27109         if (dirEndOffset + 4 > dataView.byteLength) {
27110             Roo.log('Invalid Exif data: Invalid directory size.');
27111             return;
27112         }
27113         for (i = 0; i < tagsNumber; i += 1) {
27114             this.parseExifTag(
27115                 dataView,
27116                 tiffOffset,
27117                 dirOffset + 2 + 12 * i, // tag offset
27118                 littleEndian
27119             );
27120         }
27121         // Return the offset to the next directory:
27122         return dataView.getUint32(dirEndOffset, littleEndian);
27123     },
27124     
27125     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
27126     {
27127         var tag = dataView.getUint16(offset, littleEndian);
27128         
27129         this.exif[tag] = this.getExifValue(
27130             dataView,
27131             tiffOffset,
27132             offset,
27133             dataView.getUint16(offset + 2, littleEndian), // tag type
27134             dataView.getUint32(offset + 4, littleEndian), // tag length
27135             littleEndian
27136         );
27137     },
27138     
27139     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
27140     {
27141         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
27142             tagSize,
27143             dataOffset,
27144             values,
27145             i,
27146             str,
27147             c;
27148     
27149         if (!tagType) {
27150             Roo.log('Invalid Exif data: Invalid tag type.');
27151             return;
27152         }
27153         
27154         tagSize = tagType.size * length;
27155         // Determine if the value is contained in the dataOffset bytes,
27156         // or if the value at the dataOffset is a pointer to the actual data:
27157         dataOffset = tagSize > 4 ?
27158                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
27159         if (dataOffset + tagSize > dataView.byteLength) {
27160             Roo.log('Invalid Exif data: Invalid data offset.');
27161             return;
27162         }
27163         if (length === 1) {
27164             return tagType.getValue(dataView, dataOffset, littleEndian);
27165         }
27166         values = [];
27167         for (i = 0; i < length; i += 1) {
27168             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
27169         }
27170         
27171         if (tagType.ascii) {
27172             str = '';
27173             // Concatenate the chars:
27174             for (i = 0; i < values.length; i += 1) {
27175                 c = values[i];
27176                 // Ignore the terminating NULL byte(s):
27177                 if (c === '\u0000') {
27178                     break;
27179                 }
27180                 str += c;
27181             }
27182             return str;
27183         }
27184         return values;
27185     }
27186     
27187 });
27188
27189 Roo.apply(Roo.bootstrap.UploadCropbox, {
27190     tags : {
27191         'Orientation': 0x0112
27192     },
27193     
27194     Orientation: {
27195             1: 0, //'top-left',
27196 //            2: 'top-right',
27197             3: 180, //'bottom-right',
27198 //            4: 'bottom-left',
27199 //            5: 'left-top',
27200             6: 90, //'right-top',
27201 //            7: 'right-bottom',
27202             8: 270 //'left-bottom'
27203     },
27204     
27205     exifTagTypes : {
27206         // byte, 8-bit unsigned int:
27207         1: {
27208             getValue: function (dataView, dataOffset) {
27209                 return dataView.getUint8(dataOffset);
27210             },
27211             size: 1
27212         },
27213         // ascii, 8-bit byte:
27214         2: {
27215             getValue: function (dataView, dataOffset) {
27216                 return String.fromCharCode(dataView.getUint8(dataOffset));
27217             },
27218             size: 1,
27219             ascii: true
27220         },
27221         // short, 16 bit int:
27222         3: {
27223             getValue: function (dataView, dataOffset, littleEndian) {
27224                 return dataView.getUint16(dataOffset, littleEndian);
27225             },
27226             size: 2
27227         },
27228         // long, 32 bit int:
27229         4: {
27230             getValue: function (dataView, dataOffset, littleEndian) {
27231                 return dataView.getUint32(dataOffset, littleEndian);
27232             },
27233             size: 4
27234         },
27235         // rational = two long values, first is numerator, second is denominator:
27236         5: {
27237             getValue: function (dataView, dataOffset, littleEndian) {
27238                 return dataView.getUint32(dataOffset, littleEndian) /
27239                     dataView.getUint32(dataOffset + 4, littleEndian);
27240             },
27241             size: 8
27242         },
27243         // slong, 32 bit signed int:
27244         9: {
27245             getValue: function (dataView, dataOffset, littleEndian) {
27246                 return dataView.getInt32(dataOffset, littleEndian);
27247             },
27248             size: 4
27249         },
27250         // srational, two slongs, first is numerator, second is denominator:
27251         10: {
27252             getValue: function (dataView, dataOffset, littleEndian) {
27253                 return dataView.getInt32(dataOffset, littleEndian) /
27254                     dataView.getInt32(dataOffset + 4, littleEndian);
27255             },
27256             size: 8
27257         }
27258     },
27259     
27260     footer : {
27261         STANDARD : [
27262             {
27263                 tag : 'div',
27264                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27265                 action : 'rotate-left',
27266                 cn : [
27267                     {
27268                         tag : 'button',
27269                         cls : 'btn btn-default',
27270                         html : '<i class="fa fa-undo"></i>'
27271                     }
27272                 ]
27273             },
27274             {
27275                 tag : 'div',
27276                 cls : 'btn-group roo-upload-cropbox-picture',
27277                 action : 'picture',
27278                 cn : [
27279                     {
27280                         tag : 'button',
27281                         cls : 'btn btn-default',
27282                         html : '<i class="fa fa-picture-o"></i>'
27283                     }
27284                 ]
27285             },
27286             {
27287                 tag : 'div',
27288                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27289                 action : 'rotate-right',
27290                 cn : [
27291                     {
27292                         tag : 'button',
27293                         cls : 'btn btn-default',
27294                         html : '<i class="fa fa-repeat"></i>'
27295                     }
27296                 ]
27297             }
27298         ],
27299         DOCUMENT : [
27300             {
27301                 tag : 'div',
27302                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27303                 action : 'rotate-left',
27304                 cn : [
27305                     {
27306                         tag : 'button',
27307                         cls : 'btn btn-default',
27308                         html : '<i class="fa fa-undo"></i>'
27309                     }
27310                 ]
27311             },
27312             {
27313                 tag : 'div',
27314                 cls : 'btn-group roo-upload-cropbox-download',
27315                 action : 'download',
27316                 cn : [
27317                     {
27318                         tag : 'button',
27319                         cls : 'btn btn-default',
27320                         html : '<i class="fa fa-download"></i>'
27321                     }
27322                 ]
27323             },
27324             {
27325                 tag : 'div',
27326                 cls : 'btn-group roo-upload-cropbox-crop',
27327                 action : 'crop',
27328                 cn : [
27329                     {
27330                         tag : 'button',
27331                         cls : 'btn btn-default',
27332                         html : '<i class="fa fa-crop"></i>'
27333                     }
27334                 ]
27335             },
27336             {
27337                 tag : 'div',
27338                 cls : 'btn-group roo-upload-cropbox-trash',
27339                 action : 'trash',
27340                 cn : [
27341                     {
27342                         tag : 'button',
27343                         cls : 'btn btn-default',
27344                         html : '<i class="fa fa-trash"></i>'
27345                     }
27346                 ]
27347             },
27348             {
27349                 tag : 'div',
27350                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27351                 action : 'rotate-right',
27352                 cn : [
27353                     {
27354                         tag : 'button',
27355                         cls : 'btn btn-default',
27356                         html : '<i class="fa fa-repeat"></i>'
27357                     }
27358                 ]
27359             }
27360         ],
27361         ROTATOR : [
27362             {
27363                 tag : 'div',
27364                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27365                 action : 'rotate-left',
27366                 cn : [
27367                     {
27368                         tag : 'button',
27369                         cls : 'btn btn-default',
27370                         html : '<i class="fa fa-undo"></i>'
27371                     }
27372                 ]
27373             },
27374             {
27375                 tag : 'div',
27376                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27377                 action : 'rotate-right',
27378                 cn : [
27379                     {
27380                         tag : 'button',
27381                         cls : 'btn btn-default',
27382                         html : '<i class="fa fa-repeat"></i>'
27383                     }
27384                 ]
27385             }
27386         ]
27387     }
27388 });
27389
27390 /*
27391 * Licence: LGPL
27392 */
27393
27394 /**
27395  * @class Roo.bootstrap.DocumentManager
27396  * @extends Roo.bootstrap.Component
27397  * Bootstrap DocumentManager class
27398  * @cfg {String} paramName default 'imageUpload'
27399  * @cfg {String} method default POST
27400  * @cfg {String} url action url
27401  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
27402  * @cfg {Boolean} multiple multiple upload default true
27403  * @cfg {Number} thumbSize default 300
27404  * @cfg {String} fieldLabel
27405  * @cfg {Number} labelWidth default 4
27406  * @cfg {String} labelAlign (left|top) default left
27407  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
27408  * 
27409  * @constructor
27410  * Create a new DocumentManager
27411  * @param {Object} config The config object
27412  */
27413
27414 Roo.bootstrap.DocumentManager = function(config){
27415     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
27416     
27417     this.files = [];
27418     this.delegates = [];
27419     
27420     this.addEvents({
27421         /**
27422          * @event initial
27423          * Fire when initial the DocumentManager
27424          * @param {Roo.bootstrap.DocumentManager} this
27425          */
27426         "initial" : true,
27427         /**
27428          * @event inspect
27429          * inspect selected file
27430          * @param {Roo.bootstrap.DocumentManager} this
27431          * @param {File} file
27432          */
27433         "inspect" : true,
27434         /**
27435          * @event exception
27436          * Fire when xhr load exception
27437          * @param {Roo.bootstrap.DocumentManager} this
27438          * @param {XMLHttpRequest} xhr
27439          */
27440         "exception" : true,
27441         /**
27442          * @event afterupload
27443          * Fire when xhr load exception
27444          * @param {Roo.bootstrap.DocumentManager} this
27445          * @param {XMLHttpRequest} xhr
27446          */
27447         "afterupload" : true,
27448         /**
27449          * @event prepare
27450          * prepare the form data
27451          * @param {Roo.bootstrap.DocumentManager} this
27452          * @param {Object} formData
27453          */
27454         "prepare" : true,
27455         /**
27456          * @event remove
27457          * Fire when remove the file
27458          * @param {Roo.bootstrap.DocumentManager} this
27459          * @param {Object} file
27460          */
27461         "remove" : true,
27462         /**
27463          * @event refresh
27464          * Fire after refresh the file
27465          * @param {Roo.bootstrap.DocumentManager} this
27466          */
27467         "refresh" : true,
27468         /**
27469          * @event click
27470          * Fire after click the image
27471          * @param {Roo.bootstrap.DocumentManager} this
27472          * @param {Object} file
27473          */
27474         "click" : true,
27475         /**
27476          * @event edit
27477          * Fire when upload a image and editable set to true
27478          * @param {Roo.bootstrap.DocumentManager} this
27479          * @param {Object} file
27480          */
27481         "edit" : true,
27482         /**
27483          * @event beforeselectfile
27484          * Fire before select file
27485          * @param {Roo.bootstrap.DocumentManager} this
27486          */
27487         "beforeselectfile" : true,
27488         /**
27489          * @event process
27490          * Fire before process file
27491          * @param {Roo.bootstrap.DocumentManager} this
27492          * @param {Object} file
27493          */
27494         "process" : true
27495         
27496     });
27497 };
27498
27499 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
27500     
27501     boxes : 0,
27502     inputName : '',
27503     thumbSize : 300,
27504     multiple : true,
27505     files : false,
27506     method : 'POST',
27507     url : '',
27508     paramName : 'imageUpload',
27509     fieldLabel : '',
27510     labelWidth : 4,
27511     labelAlign : 'left',
27512     editable : true,
27513     delegates : false,
27514     
27515     
27516     xhr : false, 
27517     
27518     getAutoCreate : function()
27519     {   
27520         var managerWidget = {
27521             tag : 'div',
27522             cls : 'roo-document-manager',
27523             cn : [
27524                 {
27525                     tag : 'input',
27526                     cls : 'roo-document-manager-selector',
27527                     type : 'file'
27528                 },
27529                 {
27530                     tag : 'div',
27531                     cls : 'roo-document-manager-uploader',
27532                     cn : [
27533                         {
27534                             tag : 'div',
27535                             cls : 'roo-document-manager-upload-btn',
27536                             html : '<i class="fa fa-plus"></i>'
27537                         }
27538                     ]
27539                     
27540                 }
27541             ]
27542         };
27543         
27544         var content = [
27545             {
27546                 tag : 'div',
27547                 cls : 'column col-md-12',
27548                 cn : managerWidget
27549             }
27550         ];
27551         
27552         if(this.fieldLabel.length){
27553             
27554             content = [
27555                 {
27556                     tag : 'div',
27557                     cls : 'column col-md-12',
27558                     html : this.fieldLabel
27559                 },
27560                 {
27561                     tag : 'div',
27562                     cls : 'column col-md-12',
27563                     cn : managerWidget
27564                 }
27565             ];
27566
27567             if(this.labelAlign == 'left'){
27568                 content = [
27569                     {
27570                         tag : 'div',
27571                         cls : 'column col-md-' + this.labelWidth,
27572                         html : this.fieldLabel
27573                     },
27574                     {
27575                         tag : 'div',
27576                         cls : 'column col-md-' + (12 - this.labelWidth),
27577                         cn : managerWidget
27578                     }
27579                 ];
27580                 
27581             }
27582         }
27583         
27584         var cfg = {
27585             tag : 'div',
27586             cls : 'row clearfix',
27587             cn : content
27588         };
27589         
27590         return cfg;
27591         
27592     },
27593     
27594     initEvents : function()
27595     {
27596         this.managerEl = this.el.select('.roo-document-manager', true).first();
27597         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27598         
27599         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
27600         this.selectorEl.hide();
27601         
27602         if(this.multiple){
27603             this.selectorEl.attr('multiple', 'multiple');
27604         }
27605         
27606         this.selectorEl.on('change', this.onFileSelected, this);
27607         
27608         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
27609         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27610         
27611         this.uploader.on('click', this.onUploaderClick, this);
27612         
27613         this.renderProgressDialog();
27614         
27615         var _this = this;
27616         
27617         window.addEventListener("resize", function() { _this.refresh(); } );
27618         
27619         this.fireEvent('initial', this);
27620     },
27621     
27622     renderProgressDialog : function()
27623     {
27624         var _this = this;
27625         
27626         this.progressDialog = new Roo.bootstrap.Modal({
27627             cls : 'roo-document-manager-progress-dialog',
27628             allow_close : false,
27629             title : '',
27630             buttons : [
27631                 {
27632                     name  :'cancel',
27633                     weight : 'danger',
27634                     html : 'Cancel'
27635                 }
27636             ], 
27637             listeners : { 
27638                 btnclick : function() {
27639                     _this.uploadCancel();
27640                     this.hide();
27641                 }
27642             }
27643         });
27644          
27645         this.progressDialog.render(Roo.get(document.body));
27646          
27647         this.progress = new Roo.bootstrap.Progress({
27648             cls : 'roo-document-manager-progress',
27649             active : true,
27650             striped : true
27651         });
27652         
27653         this.progress.render(this.progressDialog.getChildContainer());
27654         
27655         this.progressBar = new Roo.bootstrap.ProgressBar({
27656             cls : 'roo-document-manager-progress-bar',
27657             aria_valuenow : 0,
27658             aria_valuemin : 0,
27659             aria_valuemax : 12,
27660             panel : 'success'
27661         });
27662         
27663         this.progressBar.render(this.progress.getChildContainer());
27664     },
27665     
27666     onUploaderClick : function(e)
27667     {
27668         e.preventDefault();
27669      
27670         if(this.fireEvent('beforeselectfile', this) != false){
27671             this.selectorEl.dom.click();
27672         }
27673         
27674     },
27675     
27676     onFileSelected : function(e)
27677     {
27678         e.preventDefault();
27679         
27680         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27681             return;
27682         }
27683         
27684         Roo.each(this.selectorEl.dom.files, function(file){
27685             if(this.fireEvent('inspect', this, file) != false){
27686                 this.files.push(file);
27687             }
27688         }, this);
27689         
27690         this.queue();
27691         
27692     },
27693     
27694     queue : function()
27695     {
27696         this.selectorEl.dom.value = '';
27697         
27698         if(!this.files.length){
27699             return;
27700         }
27701         
27702         if(this.boxes > 0 && this.files.length > this.boxes){
27703             this.files = this.files.slice(0, this.boxes);
27704         }
27705         
27706         this.uploader.show();
27707         
27708         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27709             this.uploader.hide();
27710         }
27711         
27712         var _this = this;
27713         
27714         var files = [];
27715         
27716         var docs = [];
27717         
27718         Roo.each(this.files, function(file){
27719             
27720             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27721                 var f = this.renderPreview(file);
27722                 files.push(f);
27723                 return;
27724             }
27725             
27726             if(file.type.indexOf('image') != -1){
27727                 this.delegates.push(
27728                     (function(){
27729                         _this.process(file);
27730                     }).createDelegate(this)
27731                 );
27732         
27733                 return;
27734             }
27735             
27736             docs.push(
27737                 (function(){
27738                     _this.process(file);
27739                 }).createDelegate(this)
27740             );
27741             
27742         }, this);
27743         
27744         this.files = files;
27745         
27746         this.delegates = this.delegates.concat(docs);
27747         
27748         if(!this.delegates.length){
27749             this.refresh();
27750             return;
27751         }
27752         
27753         this.progressBar.aria_valuemax = this.delegates.length;
27754         
27755         this.arrange();
27756         
27757         return;
27758     },
27759     
27760     arrange : function()
27761     {
27762         if(!this.delegates.length){
27763             this.progressDialog.hide();
27764             this.refresh();
27765             return;
27766         }
27767         
27768         var delegate = this.delegates.shift();
27769         
27770         this.progressDialog.show();
27771         
27772         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27773         
27774         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27775         
27776         delegate();
27777     },
27778     
27779     refresh : function()
27780     {
27781         this.uploader.show();
27782         
27783         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27784             this.uploader.hide();
27785         }
27786         
27787         Roo.isTouch ? this.closable(false) : this.closable(true);
27788         
27789         this.fireEvent('refresh', this);
27790     },
27791     
27792     onRemove : function(e, el, o)
27793     {
27794         e.preventDefault();
27795         
27796         this.fireEvent('remove', this, o);
27797         
27798     },
27799     
27800     remove : function(o)
27801     {
27802         var files = [];
27803         
27804         Roo.each(this.files, function(file){
27805             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27806                 files.push(file);
27807                 return;
27808             }
27809
27810             o.target.remove();
27811
27812         }, this);
27813         
27814         this.files = files;
27815         
27816         this.refresh();
27817     },
27818     
27819     clear : function()
27820     {
27821         Roo.each(this.files, function(file){
27822             if(!file.target){
27823                 return;
27824             }
27825             
27826             file.target.remove();
27827
27828         }, this);
27829         
27830         this.files = [];
27831         
27832         this.refresh();
27833     },
27834     
27835     onClick : function(e, el, o)
27836     {
27837         e.preventDefault();
27838         
27839         this.fireEvent('click', this, o);
27840         
27841     },
27842     
27843     closable : function(closable)
27844     {
27845         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27846             
27847             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27848             
27849             if(closable){
27850                 el.show();
27851                 return;
27852             }
27853             
27854             el.hide();
27855             
27856         }, this);
27857     },
27858     
27859     xhrOnLoad : function(xhr)
27860     {
27861         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27862             el.remove();
27863         }, this);
27864         
27865         if (xhr.readyState !== 4) {
27866             this.arrange();
27867             this.fireEvent('exception', this, xhr);
27868             return;
27869         }
27870
27871         var response = Roo.decode(xhr.responseText);
27872         
27873         if(!response.success){
27874             this.arrange();
27875             this.fireEvent('exception', this, xhr);
27876             return;
27877         }
27878         
27879         var file = this.renderPreview(response.data);
27880         
27881         this.files.push(file);
27882         
27883         this.arrange();
27884         
27885         this.fireEvent('afterupload', this, xhr);
27886         
27887     },
27888     
27889     xhrOnError : function(xhr)
27890     {
27891         Roo.log('xhr on error');
27892         
27893         var response = Roo.decode(xhr.responseText);
27894           
27895         Roo.log(response);
27896         
27897         this.arrange();
27898     },
27899     
27900     process : function(file)
27901     {
27902         if(this.fireEvent('process', this, file) !== false){
27903             if(this.editable && file.type.indexOf('image') != -1){
27904                 this.fireEvent('edit', this, file);
27905                 return;
27906             }
27907
27908             this.uploadStart(file, false);
27909
27910             return;
27911         }
27912         
27913     },
27914     
27915     uploadStart : function(file, crop)
27916     {
27917         this.xhr = new XMLHttpRequest();
27918         
27919         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27920             this.arrange();
27921             return;
27922         }
27923         
27924         file.xhr = this.xhr;
27925             
27926         this.managerEl.createChild({
27927             tag : 'div',
27928             cls : 'roo-document-manager-loading',
27929             cn : [
27930                 {
27931                     tag : 'div',
27932                     tooltip : file.name,
27933                     cls : 'roo-document-manager-thumb',
27934                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27935                 }
27936             ]
27937
27938         });
27939
27940         this.xhr.open(this.method, this.url, true);
27941         
27942         var headers = {
27943             "Accept": "application/json",
27944             "Cache-Control": "no-cache",
27945             "X-Requested-With": "XMLHttpRequest"
27946         };
27947         
27948         for (var headerName in headers) {
27949             var headerValue = headers[headerName];
27950             if (headerValue) {
27951                 this.xhr.setRequestHeader(headerName, headerValue);
27952             }
27953         }
27954         
27955         var _this = this;
27956         
27957         this.xhr.onload = function()
27958         {
27959             _this.xhrOnLoad(_this.xhr);
27960         }
27961         
27962         this.xhr.onerror = function()
27963         {
27964             _this.xhrOnError(_this.xhr);
27965         }
27966         
27967         var formData = new FormData();
27968
27969         formData.append('returnHTML', 'NO');
27970         
27971         if(crop){
27972             formData.append('crop', crop);
27973         }
27974         
27975         formData.append(this.paramName, file, file.name);
27976         
27977         var options = {
27978             file : file, 
27979             manually : false
27980         };
27981         
27982         if(this.fireEvent('prepare', this, formData, options) != false){
27983             
27984             if(options.manually){
27985                 return;
27986             }
27987             
27988             this.xhr.send(formData);
27989             return;
27990         };
27991         
27992         this.uploadCancel();
27993     },
27994     
27995     uploadCancel : function()
27996     {
27997         if (this.xhr) {
27998             this.xhr.abort();
27999         }
28000         
28001         this.delegates = [];
28002         
28003         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28004             el.remove();
28005         }, this);
28006         
28007         this.arrange();
28008     },
28009     
28010     renderPreview : function(file)
28011     {
28012         if(typeof(file.target) != 'undefined' && file.target){
28013             return file;
28014         }
28015         
28016         var previewEl = this.managerEl.createChild({
28017             tag : 'div',
28018             cls : 'roo-document-manager-preview',
28019             cn : [
28020                 {
28021                     tag : 'div',
28022                     tooltip : file.filename,
28023                     cls : 'roo-document-manager-thumb',
28024                     html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
28025                 },
28026                 {
28027                     tag : 'button',
28028                     cls : 'close',
28029                     html : '<i class="fa fa-times-circle"></i>'
28030                 }
28031             ]
28032         });
28033
28034         var close = previewEl.select('button.close', true).first();
28035
28036         close.on('click', this.onRemove, this, file);
28037
28038         file.target = previewEl;
28039
28040         var image = previewEl.select('img', true).first();
28041         
28042         var _this = this;
28043         
28044         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28045         
28046         image.on('click', this.onClick, this, file);
28047         
28048         return file;
28049         
28050     },
28051     
28052     onPreviewLoad : function(file, image)
28053     {
28054         if(typeof(file.target) == 'undefined' || !file.target){
28055             return;
28056         }
28057         
28058         var width = image.dom.naturalWidth || image.dom.width;
28059         var height = image.dom.naturalHeight || image.dom.height;
28060         
28061         if(width > height){
28062             file.target.addClass('wide');
28063             return;
28064         }
28065         
28066         file.target.addClass('tall');
28067         return;
28068         
28069     },
28070     
28071     uploadFromSource : function(file, crop)
28072     {
28073         this.xhr = new XMLHttpRequest();
28074         
28075         this.managerEl.createChild({
28076             tag : 'div',
28077             cls : 'roo-document-manager-loading',
28078             cn : [
28079                 {
28080                     tag : 'div',
28081                     tooltip : file.name,
28082                     cls : 'roo-document-manager-thumb',
28083                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28084                 }
28085             ]
28086
28087         });
28088
28089         this.xhr.open(this.method, this.url, true);
28090         
28091         var headers = {
28092             "Accept": "application/json",
28093             "Cache-Control": "no-cache",
28094             "X-Requested-With": "XMLHttpRequest"
28095         };
28096         
28097         for (var headerName in headers) {
28098             var headerValue = headers[headerName];
28099             if (headerValue) {
28100                 this.xhr.setRequestHeader(headerName, headerValue);
28101             }
28102         }
28103         
28104         var _this = this;
28105         
28106         this.xhr.onload = function()
28107         {
28108             _this.xhrOnLoad(_this.xhr);
28109         }
28110         
28111         this.xhr.onerror = function()
28112         {
28113             _this.xhrOnError(_this.xhr);
28114         }
28115         
28116         var formData = new FormData();
28117
28118         formData.append('returnHTML', 'NO');
28119         
28120         formData.append('crop', crop);
28121         
28122         if(typeof(file.filename) != 'undefined'){
28123             formData.append('filename', file.filename);
28124         }
28125         
28126         if(typeof(file.mimetype) != 'undefined'){
28127             formData.append('mimetype', file.mimetype);
28128         }
28129         
28130         if(this.fireEvent('prepare', this, formData) != false){
28131             this.xhr.send(formData);
28132         };
28133     }
28134 });
28135
28136 /*
28137 * Licence: LGPL
28138 */
28139
28140 /**
28141  * @class Roo.bootstrap.DocumentViewer
28142  * @extends Roo.bootstrap.Component
28143  * Bootstrap DocumentViewer class
28144  * @cfg {Boolean} showDownload (true|false) show download button (default true)
28145  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
28146  * 
28147  * @constructor
28148  * Create a new DocumentViewer
28149  * @param {Object} config The config object
28150  */
28151
28152 Roo.bootstrap.DocumentViewer = function(config){
28153     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
28154     
28155     this.addEvents({
28156         /**
28157          * @event initial
28158          * Fire after initEvent
28159          * @param {Roo.bootstrap.DocumentViewer} this
28160          */
28161         "initial" : true,
28162         /**
28163          * @event click
28164          * Fire after click
28165          * @param {Roo.bootstrap.DocumentViewer} this
28166          */
28167         "click" : true,
28168         /**
28169          * @event download
28170          * Fire after download button
28171          * @param {Roo.bootstrap.DocumentViewer} this
28172          */
28173         "download" : true,
28174         /**
28175          * @event trash
28176          * Fire after trash button
28177          * @param {Roo.bootstrap.DocumentViewer} this
28178          */
28179         "trash" : true
28180         
28181     });
28182 };
28183
28184 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
28185     
28186     showDownload : true,
28187     
28188     showTrash : true,
28189     
28190     getAutoCreate : function()
28191     {
28192         var cfg = {
28193             tag : 'div',
28194             cls : 'roo-document-viewer',
28195             cn : [
28196                 {
28197                     tag : 'div',
28198                     cls : 'roo-document-viewer-body',
28199                     cn : [
28200                         {
28201                             tag : 'div',
28202                             cls : 'roo-document-viewer-thumb',
28203                             cn : [
28204                                 {
28205                                     tag : 'img',
28206                                     cls : 'roo-document-viewer-image'
28207                                 }
28208                             ]
28209                         }
28210                     ]
28211                 },
28212                 {
28213                     tag : 'div',
28214                     cls : 'roo-document-viewer-footer',
28215                     cn : {
28216                         tag : 'div',
28217                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
28218                         cn : [
28219                             {
28220                                 tag : 'div',
28221                                 cls : 'btn-group roo-document-viewer-download',
28222                                 cn : [
28223                                     {
28224                                         tag : 'button',
28225                                         cls : 'btn btn-default',
28226                                         html : '<i class="fa fa-download"></i>'
28227                                     }
28228                                 ]
28229                             },
28230                             {
28231                                 tag : 'div',
28232                                 cls : 'btn-group roo-document-viewer-trash',
28233                                 cn : [
28234                                     {
28235                                         tag : 'button',
28236                                         cls : 'btn btn-default',
28237                                         html : '<i class="fa fa-trash"></i>'
28238                                     }
28239                                 ]
28240                             }
28241                         ]
28242                     }
28243                 }
28244             ]
28245         };
28246         
28247         return cfg;
28248     },
28249     
28250     initEvents : function()
28251     {
28252         
28253         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
28254         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
28255         
28256         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
28257         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
28258         
28259         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
28260         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
28261         
28262         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
28263         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
28264         
28265         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
28266         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
28267         
28268         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
28269         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
28270         
28271         this.bodyEl.on('click', this.onClick, this);
28272         this.downloadBtn.on('click', this.onDownload, this);
28273         this.trashBtn.on('click', this.onTrash, this);
28274         
28275         this.downloadBtn.hide();
28276         this.trashBtn.hide();
28277         
28278         if(this.showDownload){
28279             this.downloadBtn.show();
28280         }
28281         
28282         if(this.showTrash){
28283             this.trashBtn.show();
28284         }
28285         
28286         if(!this.showDownload && !this.showTrash) {
28287             this.footerEl.hide();
28288         }
28289         
28290     },
28291     
28292     initial : function()
28293     {
28294 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
28295         
28296         
28297         this.fireEvent('initial', this);
28298         
28299     },
28300     
28301     onClick : function(e)
28302     {
28303         e.preventDefault();
28304         
28305         this.fireEvent('click', this);
28306     },
28307     
28308     onDownload : function(e)
28309     {
28310         e.preventDefault();
28311         
28312         this.fireEvent('download', this);
28313     },
28314     
28315     onTrash : function(e)
28316     {
28317         e.preventDefault();
28318         
28319         this.fireEvent('trash', this);
28320     }
28321     
28322 });
28323 /*
28324  * - LGPL
28325  *
28326  * nav progress bar
28327  * 
28328  */
28329
28330 /**
28331  * @class Roo.bootstrap.NavProgressBar
28332  * @extends Roo.bootstrap.Component
28333  * Bootstrap NavProgressBar class
28334  * 
28335  * @constructor
28336  * Create a new nav progress bar
28337  * @param {Object} config The config object
28338  */
28339
28340 Roo.bootstrap.NavProgressBar = function(config){
28341     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
28342
28343     this.bullets = this.bullets || [];
28344    
28345 //    Roo.bootstrap.NavProgressBar.register(this);
28346      this.addEvents({
28347         /**
28348              * @event changed
28349              * Fires when the active item changes
28350              * @param {Roo.bootstrap.NavProgressBar} this
28351              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
28352              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
28353          */
28354         'changed': true
28355      });
28356     
28357 };
28358
28359 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
28360     
28361     bullets : [],
28362     barItems : [],
28363     
28364     getAutoCreate : function()
28365     {
28366         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
28367         
28368         cfg = {
28369             tag : 'div',
28370             cls : 'roo-navigation-bar-group',
28371             cn : [
28372                 {
28373                     tag : 'div',
28374                     cls : 'roo-navigation-top-bar'
28375                 },
28376                 {
28377                     tag : 'div',
28378                     cls : 'roo-navigation-bullets-bar',
28379                     cn : [
28380                         {
28381                             tag : 'ul',
28382                             cls : 'roo-navigation-bar'
28383                         }
28384                     ]
28385                 },
28386                 
28387                 {
28388                     tag : 'div',
28389                     cls : 'roo-navigation-bottom-bar'
28390                 }
28391             ]
28392             
28393         };
28394         
28395         return cfg;
28396         
28397     },
28398     
28399     initEvents: function() 
28400     {
28401         
28402     },
28403     
28404     onRender : function(ct, position) 
28405     {
28406         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28407         
28408         if(this.bullets.length){
28409             Roo.each(this.bullets, function(b){
28410                this.addItem(b);
28411             }, this);
28412         }
28413         
28414         this.format();
28415         
28416     },
28417     
28418     addItem : function(cfg)
28419     {
28420         var item = new Roo.bootstrap.NavProgressItem(cfg);
28421         
28422         item.parentId = this.id;
28423         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
28424         
28425         if(cfg.html){
28426             var top = new Roo.bootstrap.Element({
28427                 tag : 'div',
28428                 cls : 'roo-navigation-bar-text'
28429             });
28430             
28431             var bottom = new Roo.bootstrap.Element({
28432                 tag : 'div',
28433                 cls : 'roo-navigation-bar-text'
28434             });
28435             
28436             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
28437             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
28438             
28439             var topText = new Roo.bootstrap.Element({
28440                 tag : 'span',
28441                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
28442             });
28443             
28444             var bottomText = new Roo.bootstrap.Element({
28445                 tag : 'span',
28446                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
28447             });
28448             
28449             topText.onRender(top.el, null);
28450             bottomText.onRender(bottom.el, null);
28451             
28452             item.topEl = top;
28453             item.bottomEl = bottom;
28454         }
28455         
28456         this.barItems.push(item);
28457         
28458         return item;
28459     },
28460     
28461     getActive : function()
28462     {
28463         var active = false;
28464         
28465         Roo.each(this.barItems, function(v){
28466             
28467             if (!v.isActive()) {
28468                 return;
28469             }
28470             
28471             active = v;
28472             return false;
28473             
28474         });
28475         
28476         return active;
28477     },
28478     
28479     setActiveItem : function(item)
28480     {
28481         var prev = false;
28482         
28483         Roo.each(this.barItems, function(v){
28484             if (v.rid == item.rid) {
28485                 return ;
28486             }
28487             
28488             if (v.isActive()) {
28489                 v.setActive(false);
28490                 prev = v;
28491             }
28492         });
28493
28494         item.setActive(true);
28495         
28496         this.fireEvent('changed', this, item, prev);
28497     },
28498     
28499     getBarItem: function(rid)
28500     {
28501         var ret = false;
28502         
28503         Roo.each(this.barItems, function(e) {
28504             if (e.rid != rid) {
28505                 return;
28506             }
28507             
28508             ret =  e;
28509             return false;
28510         });
28511         
28512         return ret;
28513     },
28514     
28515     indexOfItem : function(item)
28516     {
28517         var index = false;
28518         
28519         Roo.each(this.barItems, function(v, i){
28520             
28521             if (v.rid != item.rid) {
28522                 return;
28523             }
28524             
28525             index = i;
28526             return false
28527         });
28528         
28529         return index;
28530     },
28531     
28532     setActiveNext : function()
28533     {
28534         var i = this.indexOfItem(this.getActive());
28535         
28536         if (i > this.barItems.length) {
28537             return;
28538         }
28539         
28540         this.setActiveItem(this.barItems[i+1]);
28541     },
28542     
28543     setActivePrev : function()
28544     {
28545         var i = this.indexOfItem(this.getActive());
28546         
28547         if (i  < 1) {
28548             return;
28549         }
28550         
28551         this.setActiveItem(this.barItems[i-1]);
28552     },
28553     
28554     format : function()
28555     {
28556         if(!this.barItems.length){
28557             return;
28558         }
28559      
28560         var width = 100 / this.barItems.length;
28561         
28562         Roo.each(this.barItems, function(i){
28563             i.el.setStyle('width', width + '%');
28564             i.topEl.el.setStyle('width', width + '%');
28565             i.bottomEl.el.setStyle('width', width + '%');
28566         }, this);
28567         
28568     }
28569     
28570 });
28571 /*
28572  * - LGPL
28573  *
28574  * Nav Progress Item
28575  * 
28576  */
28577
28578 /**
28579  * @class Roo.bootstrap.NavProgressItem
28580  * @extends Roo.bootstrap.Component
28581  * Bootstrap NavProgressItem class
28582  * @cfg {String} rid the reference id
28583  * @cfg {Boolean} active (true|false) Is item active default false
28584  * @cfg {Boolean} disabled (true|false) Is item active default false
28585  * @cfg {String} html
28586  * @cfg {String} position (top|bottom) text position default bottom
28587  * @cfg {String} icon show icon instead of number
28588  * 
28589  * @constructor
28590  * Create a new NavProgressItem
28591  * @param {Object} config The config object
28592  */
28593 Roo.bootstrap.NavProgressItem = function(config){
28594     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
28595     this.addEvents({
28596         // raw events
28597         /**
28598          * @event click
28599          * The raw click event for the entire grid.
28600          * @param {Roo.bootstrap.NavProgressItem} this
28601          * @param {Roo.EventObject} e
28602          */
28603         "click" : true
28604     });
28605    
28606 };
28607
28608 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
28609     
28610     rid : '',
28611     active : false,
28612     disabled : false,
28613     html : '',
28614     position : 'bottom',
28615     icon : false,
28616     
28617     getAutoCreate : function()
28618     {
28619         var iconCls = 'roo-navigation-bar-item-icon';
28620         
28621         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
28622         
28623         var cfg = {
28624             tag: 'li',
28625             cls: 'roo-navigation-bar-item',
28626             cn : [
28627                 {
28628                     tag : 'i',
28629                     cls : iconCls
28630                 }
28631             ]
28632         };
28633         
28634         if(this.active){
28635             cfg.cls += ' active';
28636         }
28637         if(this.disabled){
28638             cfg.cls += ' disabled';
28639         }
28640         
28641         return cfg;
28642     },
28643     
28644     disable : function()
28645     {
28646         this.setDisabled(true);
28647     },
28648     
28649     enable : function()
28650     {
28651         this.setDisabled(false);
28652     },
28653     
28654     initEvents: function() 
28655     {
28656         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
28657         
28658         this.iconEl.on('click', this.onClick, this);
28659     },
28660     
28661     onClick : function(e)
28662     {
28663         e.preventDefault();
28664         
28665         if(this.disabled){
28666             return;
28667         }
28668         
28669         if(this.fireEvent('click', this, e) === false){
28670             return;
28671         };
28672         
28673         this.parent().setActiveItem(this);
28674     },
28675     
28676     isActive: function () 
28677     {
28678         return this.active;
28679     },
28680     
28681     setActive : function(state)
28682     {
28683         if(this.active == state){
28684             return;
28685         }
28686         
28687         this.active = state;
28688         
28689         if (state) {
28690             this.el.addClass('active');
28691             return;
28692         }
28693         
28694         this.el.removeClass('active');
28695         
28696         return;
28697     },
28698     
28699     setDisabled : function(state)
28700     {
28701         if(this.disabled == state){
28702             return;
28703         }
28704         
28705         this.disabled = state;
28706         
28707         if (state) {
28708             this.el.addClass('disabled');
28709             return;
28710         }
28711         
28712         this.el.removeClass('disabled');
28713     },
28714     
28715     tooltipEl : function()
28716     {
28717         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
28718     }
28719 });
28720  
28721
28722  /*
28723  * - LGPL
28724  *
28725  * FieldLabel
28726  * 
28727  */
28728
28729 /**
28730  * @class Roo.bootstrap.FieldLabel
28731  * @extends Roo.bootstrap.Component
28732  * Bootstrap FieldLabel class
28733  * @cfg {String} html contents of the element
28734  * @cfg {String} tag tag of the element default label
28735  * @cfg {String} cls class of the element
28736  * @cfg {String} target label target 
28737  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
28738  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
28739  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
28740  * @cfg {String} iconTooltip default "This field is required"
28741  * 
28742  * @constructor
28743  * Create a new FieldLabel
28744  * @param {Object} config The config object
28745  */
28746
28747 Roo.bootstrap.FieldLabel = function(config){
28748     Roo.bootstrap.Element.superclass.constructor.call(this, config);
28749     
28750     this.addEvents({
28751             /**
28752              * @event invalid
28753              * Fires after the field has been marked as invalid.
28754              * @param {Roo.form.FieldLabel} this
28755              * @param {String} msg The validation message
28756              */
28757             invalid : true,
28758             /**
28759              * @event valid
28760              * Fires after the field has been validated with no errors.
28761              * @param {Roo.form.FieldLabel} this
28762              */
28763             valid : true
28764         });
28765 };
28766
28767 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
28768     
28769     tag: 'label',
28770     cls: '',
28771     html: '',
28772     target: '',
28773     allowBlank : true,
28774     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
28775     validClass : 'text-success fa fa-lg fa-check',
28776     iconTooltip : 'This field is required',
28777     
28778     getAutoCreate : function(){
28779         
28780         var cfg = {
28781             tag : this.tag,
28782             cls : 'roo-bootstrap-field-label ' + this.cls,
28783             for : this.target,
28784             cn : [
28785                 {
28786                     tag : 'i',
28787                     cls : '',
28788                     tooltip : this.iconTooltip
28789                 },
28790                 {
28791                     tag : 'span',
28792                     html : this.html
28793                 }
28794             ] 
28795         };
28796         
28797         return cfg;
28798     },
28799     
28800     initEvents: function() 
28801     {
28802         Roo.bootstrap.Element.superclass.initEvents.call(this);
28803         
28804         this.iconEl = this.el.select('i', true).first();
28805         
28806         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
28807         
28808         Roo.bootstrap.FieldLabel.register(this);
28809     },
28810     
28811     /**
28812      * Mark this field as valid
28813      */
28814     markValid : function()
28815     {
28816         this.iconEl.show();
28817         
28818         this.iconEl.removeClass(this.invalidClass);
28819         
28820         this.iconEl.addClass(this.validClass);
28821         
28822         this.fireEvent('valid', this);
28823     },
28824     
28825     /**
28826      * Mark this field as invalid
28827      * @param {String} msg The validation message
28828      */
28829     markInvalid : function(msg)
28830     {
28831         this.iconEl.show();
28832         
28833         this.iconEl.removeClass(this.validClass);
28834         
28835         this.iconEl.addClass(this.invalidClass);
28836         
28837         this.fireEvent('invalid', this, msg);
28838     }
28839     
28840    
28841 });
28842
28843 Roo.apply(Roo.bootstrap.FieldLabel, {
28844     
28845     groups: {},
28846     
28847      /**
28848     * register a FieldLabel Group
28849     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28850     */
28851     register : function(label)
28852     {
28853         if(this.groups.hasOwnProperty(label.target)){
28854             return;
28855         }
28856      
28857         this.groups[label.target] = label;
28858         
28859     },
28860     /**
28861     * fetch a FieldLabel Group based on the target
28862     * @param {string} target
28863     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28864     */
28865     get: function(target) {
28866         if (typeof(this.groups[target]) == 'undefined') {
28867             return false;
28868         }
28869         
28870         return this.groups[target] ;
28871     }
28872 });
28873
28874  
28875
28876  /*
28877  * - LGPL
28878  *
28879  * page DateSplitField.
28880  * 
28881  */
28882
28883
28884 /**
28885  * @class Roo.bootstrap.DateSplitField
28886  * @extends Roo.bootstrap.Component
28887  * Bootstrap DateSplitField class
28888  * @cfg {string} fieldLabel - the label associated
28889  * @cfg {Number} labelWidth set the width of label (0-12)
28890  * @cfg {String} labelAlign (top|left)
28891  * @cfg {Boolean} dayAllowBlank (true|false) default false
28892  * @cfg {Boolean} monthAllowBlank (true|false) default false
28893  * @cfg {Boolean} yearAllowBlank (true|false) default false
28894  * @cfg {string} dayPlaceholder 
28895  * @cfg {string} monthPlaceholder
28896  * @cfg {string} yearPlaceholder
28897  * @cfg {string} dayFormat default 'd'
28898  * @cfg {string} monthFormat default 'm'
28899  * @cfg {string} yearFormat default 'Y'
28900
28901  *     
28902  * @constructor
28903  * Create a new DateSplitField
28904  * @param {Object} config The config object
28905  */
28906
28907 Roo.bootstrap.DateSplitField = function(config){
28908     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28909     
28910     this.addEvents({
28911         // raw events
28912          /**
28913          * @event years
28914          * getting the data of years
28915          * @param {Roo.bootstrap.DateSplitField} this
28916          * @param {Object} years
28917          */
28918         "years" : true,
28919         /**
28920          * @event days
28921          * getting the data of days
28922          * @param {Roo.bootstrap.DateSplitField} this
28923          * @param {Object} days
28924          */
28925         "days" : true,
28926         /**
28927          * @event invalid
28928          * Fires after the field has been marked as invalid.
28929          * @param {Roo.form.Field} this
28930          * @param {String} msg The validation message
28931          */
28932         invalid : true,
28933        /**
28934          * @event valid
28935          * Fires after the field has been validated with no errors.
28936          * @param {Roo.form.Field} this
28937          */
28938         valid : true
28939     });
28940 };
28941
28942 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
28943     
28944     fieldLabel : '',
28945     labelAlign : 'top',
28946     labelWidth : 3,
28947     dayAllowBlank : false,
28948     monthAllowBlank : false,
28949     yearAllowBlank : false,
28950     dayPlaceholder : '',
28951     monthPlaceholder : '',
28952     yearPlaceholder : '',
28953     dayFormat : 'd',
28954     monthFormat : 'm',
28955     yearFormat : 'Y',
28956     isFormField : true,
28957     
28958     getAutoCreate : function()
28959     {
28960         var cfg = {
28961             tag : 'div',
28962             cls : 'row roo-date-split-field-group',
28963             cn : [
28964                 {
28965                     tag : 'input',
28966                     type : 'hidden',
28967                     cls : 'form-hidden-field roo-date-split-field-group-value',
28968                     name : this.name
28969                 }
28970             ]
28971         };
28972         
28973         if(this.fieldLabel){
28974             cfg.cn.push({
28975                 tag : 'div',
28976                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
28977                 cn : [
28978                     {
28979                         tag : 'label',
28980                         html : this.fieldLabel
28981                     }
28982                 ]
28983             });
28984         }
28985         
28986         Roo.each(['day', 'month', 'year'], function(t){
28987             cfg.cn.push({
28988                 tag : 'div',
28989                 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
28990             });
28991         }, this);
28992         
28993         return cfg;
28994     },
28995     
28996     inputEl: function ()
28997     {
28998         return this.el.select('.roo-date-split-field-group-value', true).first();
28999     },
29000     
29001     onRender : function(ct, position) 
29002     {
29003         var _this = this;
29004         
29005         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29006         
29007         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
29008         
29009         this.dayField = new Roo.bootstrap.ComboBox({
29010             allowBlank : this.dayAllowBlank,
29011             alwaysQuery : true,
29012             displayField : 'value',
29013             editable : false,
29014             fieldLabel : '',
29015             forceSelection : true,
29016             mode : 'local',
29017             placeholder : this.dayPlaceholder,
29018             selectOnFocus : true,
29019             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29020             triggerAction : 'all',
29021             typeAhead : true,
29022             valueField : 'value',
29023             store : new Roo.data.SimpleStore({
29024                 data : (function() {    
29025                     var days = [];
29026                     _this.fireEvent('days', _this, days);
29027                     return days;
29028                 })(),
29029                 fields : [ 'value' ]
29030             }),
29031             listeners : {
29032                 select : function (_self, record, index)
29033                 {
29034                     _this.setValue(_this.getValue());
29035                 }
29036             }
29037         });
29038
29039         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
29040         
29041         this.monthField = new Roo.bootstrap.MonthField({
29042             after : '<i class=\"fa fa-calendar\"></i>',
29043             allowBlank : this.monthAllowBlank,
29044             placeholder : this.monthPlaceholder,
29045             readOnly : true,
29046             listeners : {
29047                 render : function (_self)
29048                 {
29049                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
29050                         e.preventDefault();
29051                         _self.focus();
29052                     });
29053                 },
29054                 select : function (_self, oldvalue, newvalue)
29055                 {
29056                     _this.setValue(_this.getValue());
29057                 }
29058             }
29059         });
29060         
29061         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
29062         
29063         this.yearField = new Roo.bootstrap.ComboBox({
29064             allowBlank : this.yearAllowBlank,
29065             alwaysQuery : true,
29066             displayField : 'value',
29067             editable : false,
29068             fieldLabel : '',
29069             forceSelection : true,
29070             mode : 'local',
29071             placeholder : this.yearPlaceholder,
29072             selectOnFocus : true,
29073             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29074             triggerAction : 'all',
29075             typeAhead : true,
29076             valueField : 'value',
29077             store : new Roo.data.SimpleStore({
29078                 data : (function() {
29079                     var years = [];
29080                     _this.fireEvent('years', _this, years);
29081                     return years;
29082                 })(),
29083                 fields : [ 'value' ]
29084             }),
29085             listeners : {
29086                 select : function (_self, record, index)
29087                 {
29088                     _this.setValue(_this.getValue());
29089                 }
29090             }
29091         });
29092
29093         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
29094     },
29095     
29096     setValue : function(v, format)
29097     {
29098         this.inputEl.dom.value = v;
29099         
29100         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
29101         
29102         var d = Date.parseDate(v, f);
29103         
29104         if(!d){
29105             this.validate();
29106             return;
29107         }
29108         
29109         this.setDay(d.format(this.dayFormat));
29110         this.setMonth(d.format(this.monthFormat));
29111         this.setYear(d.format(this.yearFormat));
29112         
29113         this.validate();
29114         
29115         return;
29116     },
29117     
29118     setDay : function(v)
29119     {
29120         this.dayField.setValue(v);
29121         this.inputEl.dom.value = this.getValue();
29122         this.validate();
29123         return;
29124     },
29125     
29126     setMonth : function(v)
29127     {
29128         this.monthField.setValue(v, true);
29129         this.inputEl.dom.value = this.getValue();
29130         this.validate();
29131         return;
29132     },
29133     
29134     setYear : function(v)
29135     {
29136         this.yearField.setValue(v);
29137         this.inputEl.dom.value = this.getValue();
29138         this.validate();
29139         return;
29140     },
29141     
29142     getDay : function()
29143     {
29144         return this.dayField.getValue();
29145     },
29146     
29147     getMonth : function()
29148     {
29149         return this.monthField.getValue();
29150     },
29151     
29152     getYear : function()
29153     {
29154         return this.yearField.getValue();
29155     },
29156     
29157     getValue : function()
29158     {
29159         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
29160         
29161         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
29162         
29163         return date;
29164     },
29165     
29166     reset : function()
29167     {
29168         this.setDay('');
29169         this.setMonth('');
29170         this.setYear('');
29171         this.inputEl.dom.value = '';
29172         this.validate();
29173         return;
29174     },
29175     
29176     validate : function()
29177     {
29178         var d = this.dayField.validate();
29179         var m = this.monthField.validate();
29180         var y = this.yearField.validate();
29181         
29182         var valid = true;
29183         
29184         if(
29185                 (!this.dayAllowBlank && !d) ||
29186                 (!this.monthAllowBlank && !m) ||
29187                 (!this.yearAllowBlank && !y)
29188         ){
29189             valid = false;
29190         }
29191         
29192         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
29193             return valid;
29194         }
29195         
29196         if(valid){
29197             this.markValid();
29198             return valid;
29199         }
29200         
29201         this.markInvalid();
29202         
29203         return valid;
29204     },
29205     
29206     markValid : function()
29207     {
29208         
29209         var label = this.el.select('label', true).first();
29210         var icon = this.el.select('i.fa-star', true).first();
29211
29212         if(label && icon){
29213             icon.remove();
29214         }
29215         
29216         this.fireEvent('valid', this);
29217     },
29218     
29219      /**
29220      * Mark this field as invalid
29221      * @param {String} msg The validation message
29222      */
29223     markInvalid : function(msg)
29224     {
29225         
29226         var label = this.el.select('label', true).first();
29227         var icon = this.el.select('i.fa-star', true).first();
29228
29229         if(label && !icon){
29230             this.el.select('.roo-date-split-field-label', true).createChild({
29231                 tag : 'i',
29232                 cls : 'text-danger fa fa-lg fa-star',
29233                 tooltip : 'This field is required',
29234                 style : 'margin-right:5px;'
29235             }, label, true);
29236         }
29237         
29238         this.fireEvent('invalid', this, msg);
29239     },
29240     
29241     clearInvalid : function()
29242     {
29243         var label = this.el.select('label', true).first();
29244         var icon = this.el.select('i.fa-star', true).first();
29245
29246         if(label && icon){
29247             icon.remove();
29248         }
29249         
29250         this.fireEvent('valid', this);
29251     },
29252     
29253     getName: function()
29254     {
29255         return this.name;
29256     }
29257     
29258 });
29259
29260  /**
29261  *
29262  * This is based on 
29263  * http://masonry.desandro.com
29264  *
29265  * The idea is to render all the bricks based on vertical width...
29266  *
29267  * The original code extends 'outlayer' - we might need to use that....
29268  * 
29269  */
29270
29271
29272 /**
29273  * @class Roo.bootstrap.LayoutMasonry
29274  * @extends Roo.bootstrap.Component
29275  * Bootstrap Layout Masonry class
29276  * 
29277  * @constructor
29278  * Create a new Element
29279  * @param {Object} config The config object
29280  */
29281
29282 Roo.bootstrap.LayoutMasonry = function(config){
29283     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
29284     
29285     this.bricks = [];
29286     
29287 };
29288
29289 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
29290     
29291     /**
29292      * @cfg {Boolean} isLayoutInstant = no animation?
29293      */   
29294     isLayoutInstant : false, // needed?
29295    
29296     /**
29297      * @cfg {Number} boxWidth  width of the columns
29298      */   
29299     boxWidth : 450,
29300     
29301       /**
29302      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
29303      */   
29304     boxHeight : 0,
29305     
29306     /**
29307      * @cfg {Number} padWidth padding below box..
29308      */   
29309     padWidth : 10, 
29310     
29311     /**
29312      * @cfg {Number} gutter gutter width..
29313      */   
29314     gutter : 10,
29315     
29316      /**
29317      * @cfg {Number} maxCols maximum number of columns
29318      */   
29319     
29320     maxCols: 0,
29321     
29322     /**
29323      * @cfg {Boolean} isAutoInitial defalut true
29324      */   
29325     isAutoInitial : true, 
29326     
29327     containerWidth: 0,
29328     
29329     /**
29330      * @cfg {Boolean} isHorizontal defalut false
29331      */   
29332     isHorizontal : false, 
29333
29334     currentSize : null,
29335     
29336     tag: 'div',
29337     
29338     cls: '',
29339     
29340     bricks: null, //CompositeElement
29341     
29342     cols : 1,
29343     
29344     _isLayoutInited : false,
29345     
29346 //    isAlternative : false, // only use for vertical layout...
29347     
29348     /**
29349      * @cfg {Number} alternativePadWidth padding below box..
29350      */   
29351     alternativePadWidth : 50, 
29352     
29353     getAutoCreate : function(){
29354         
29355         var cfg = {
29356             tag: this.tag,
29357             cls: 'blog-masonary-wrapper ' + this.cls,
29358             cn : {
29359                 cls : 'mas-boxes masonary'
29360             }
29361         };
29362         
29363         return cfg;
29364     },
29365     
29366     getChildContainer: function( )
29367     {
29368         if (this.boxesEl) {
29369             return this.boxesEl;
29370         }
29371         
29372         this.boxesEl = this.el.select('.mas-boxes').first();
29373         
29374         return this.boxesEl;
29375     },
29376     
29377     
29378     initEvents : function()
29379     {
29380         var _this = this;
29381         
29382         if(this.isAutoInitial){
29383             Roo.log('hook children rendered');
29384             this.on('childrenrendered', function() {
29385                 Roo.log('children rendered');
29386                 _this.initial();
29387             } ,this);
29388         }
29389     },
29390     
29391     initial : function()
29392     {
29393         this.currentSize = this.el.getBox(true);
29394         
29395         Roo.EventManager.onWindowResize(this.resize, this); 
29396
29397         if(!this.isAutoInitial){
29398             this.layout();
29399             return;
29400         }
29401         
29402         this.layout();
29403         
29404         return;
29405         //this.layout.defer(500,this);
29406         
29407     },
29408     
29409     resize : function()
29410     {
29411         Roo.log('resize');
29412         
29413         var cs = this.el.getBox(true);
29414         
29415         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29416             Roo.log("no change in with or X");
29417             return;
29418         }
29419         
29420         this.currentSize = cs;
29421         
29422         this.layout();
29423         
29424     },
29425     
29426     layout : function()
29427     {   
29428         this._resetLayout();
29429         
29430         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29431         
29432         this.layoutItems( isInstant );
29433       
29434         this._isLayoutInited = true;
29435         
29436     },
29437     
29438     _resetLayout : function()
29439     {
29440         if(this.isHorizontal){
29441             this.horizontalMeasureColumns();
29442             return;
29443         }
29444         
29445         this.verticalMeasureColumns();
29446         
29447     },
29448     
29449     verticalMeasureColumns : function()
29450     {
29451         this.getContainerWidth();
29452         
29453 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29454 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
29455 //            return;
29456 //        }
29457         
29458         var boxWidth = this.boxWidth + this.padWidth;
29459         
29460         if(this.containerWidth < this.boxWidth){
29461             boxWidth = this.containerWidth
29462         }
29463         
29464         var containerWidth = this.containerWidth;
29465         
29466         var cols = Math.floor(containerWidth / boxWidth);
29467         
29468         this.cols = Math.max( cols, 1 );
29469         
29470         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
29471         
29472         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
29473         
29474         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
29475         
29476         this.colWidth = boxWidth + avail - this.padWidth;
29477         
29478         this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
29479         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
29480     },
29481     
29482     horizontalMeasureColumns : function()
29483     {
29484         this.getContainerWidth();
29485         
29486         var boxWidth = this.boxWidth;
29487         
29488         if(this.containerWidth < boxWidth){
29489             boxWidth = this.containerWidth;
29490         }
29491         
29492         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
29493         
29494         this.el.setHeight(boxWidth);
29495         
29496     },
29497     
29498     getContainerWidth : function()
29499     {
29500         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
29501     },
29502     
29503     layoutItems : function( isInstant )
29504     {
29505         var items = Roo.apply([], this.bricks);
29506         
29507         if(this.isHorizontal){
29508             this._horizontalLayoutItems( items , isInstant );
29509             return;
29510         }
29511         
29512 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29513 //            this._verticalAlternativeLayoutItems( items , isInstant );
29514 //            return;
29515 //        }
29516         
29517         this._verticalLayoutItems( items , isInstant );
29518         
29519     },
29520     
29521     _verticalLayoutItems : function ( items , isInstant)
29522     {
29523         if ( !items || !items.length ) {
29524             return;
29525         }
29526         
29527         var standard = [
29528             ['xs', 'xs', 'xs', 'tall'],
29529             ['xs', 'xs', 'tall'],
29530             ['xs', 'xs', 'sm'],
29531             ['xs', 'xs', 'xs'],
29532             ['xs', 'tall'],
29533             ['xs', 'sm'],
29534             ['xs', 'xs'],
29535             ['xs'],
29536             
29537             ['sm', 'xs', 'xs'],
29538             ['sm', 'xs'],
29539             ['sm'],
29540             
29541             ['tall', 'xs', 'xs', 'xs'],
29542             ['tall', 'xs', 'xs'],
29543             ['tall', 'xs'],
29544             ['tall']
29545             
29546         ];
29547         
29548         var queue = [];
29549         
29550         var boxes = [];
29551         
29552         var box = [];
29553         
29554         Roo.each(items, function(item, k){
29555             
29556             switch (item.size) {
29557                 // these layouts take up a full box,
29558                 case 'md' :
29559                 case 'md-left' :
29560                 case 'md-right' :
29561                 case 'wide' :
29562                     
29563                     if(box.length){
29564                         boxes.push(box);
29565                         box = [];
29566                     }
29567                     
29568                     boxes.push([item]);
29569                     
29570                     break;
29571                     
29572                 case 'xs' :
29573                 case 'sm' :
29574                 case 'tall' :
29575                     
29576                     box.push(item);
29577                     
29578                     break;
29579                 default :
29580                     break;
29581                     
29582             }
29583             
29584         }, this);
29585         
29586         if(box.length){
29587             boxes.push(box);
29588             box = [];
29589         }
29590         
29591         var filterPattern = function(box, length)
29592         {
29593             if(!box.length){
29594                 return;
29595             }
29596             
29597             var match = false;
29598             
29599             var pattern = box.slice(0, length);
29600             
29601             var format = [];
29602             
29603             Roo.each(pattern, function(i){
29604                 format.push(i.size);
29605             }, this);
29606             
29607             Roo.each(standard, function(s){
29608                 
29609                 if(String(s) != String(format)){
29610                     return;
29611                 }
29612                 
29613                 match = true;
29614                 return false;
29615                 
29616             }, this);
29617             
29618             if(!match && length == 1){
29619                 return;
29620             }
29621             
29622             if(!match){
29623                 filterPattern(box, length - 1);
29624                 return;
29625             }
29626                 
29627             queue.push(pattern);
29628
29629             box = box.slice(length, box.length);
29630
29631             filterPattern(box, 4);
29632
29633             return;
29634             
29635         }
29636         
29637         Roo.each(boxes, function(box, k){
29638             
29639             if(!box.length){
29640                 return;
29641             }
29642             
29643             if(box.length == 1){
29644                 queue.push(box);
29645                 return;
29646             }
29647             
29648             filterPattern(box, 4);
29649             
29650         }, this);
29651         
29652         this._processVerticalLayoutQueue( queue, isInstant );
29653         
29654     },
29655     
29656 //    _verticalAlternativeLayoutItems : function( items , isInstant )
29657 //    {
29658 //        if ( !items || !items.length ) {
29659 //            return;
29660 //        }
29661 //
29662 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
29663 //        
29664 //    },
29665     
29666     _horizontalLayoutItems : function ( items , isInstant)
29667     {
29668         if ( !items || !items.length || items.length < 3) {
29669             return;
29670         }
29671         
29672         items.reverse();
29673         
29674         var eItems = items.slice(0, 3);
29675         
29676         items = items.slice(3, items.length);
29677         
29678         var standard = [
29679             ['xs', 'xs', 'xs', 'wide'],
29680             ['xs', 'xs', 'wide'],
29681             ['xs', 'xs', 'sm'],
29682             ['xs', 'xs', 'xs'],
29683             ['xs', 'wide'],
29684             ['xs', 'sm'],
29685             ['xs', 'xs'],
29686             ['xs'],
29687             
29688             ['sm', 'xs', 'xs'],
29689             ['sm', 'xs'],
29690             ['sm'],
29691             
29692             ['wide', 'xs', 'xs', 'xs'],
29693             ['wide', 'xs', 'xs'],
29694             ['wide', 'xs'],
29695             ['wide'],
29696             
29697             ['wide-thin']
29698         ];
29699         
29700         var queue = [];
29701         
29702         var boxes = [];
29703         
29704         var box = [];
29705         
29706         Roo.each(items, function(item, k){
29707             
29708             switch (item.size) {
29709                 case 'md' :
29710                 case 'md-left' :
29711                 case 'md-right' :
29712                 case 'tall' :
29713                     
29714                     if(box.length){
29715                         boxes.push(box);
29716                         box = [];
29717                     }
29718                     
29719                     boxes.push([item]);
29720                     
29721                     break;
29722                     
29723                 case 'xs' :
29724                 case 'sm' :
29725                 case 'wide' :
29726                 case 'wide-thin' :
29727                     
29728                     box.push(item);
29729                     
29730                     break;
29731                 default :
29732                     break;
29733                     
29734             }
29735             
29736         }, this);
29737         
29738         if(box.length){
29739             boxes.push(box);
29740             box = [];
29741         }
29742         
29743         var filterPattern = function(box, length)
29744         {
29745             if(!box.length){
29746                 return;
29747             }
29748             
29749             var match = false;
29750             
29751             var pattern = box.slice(0, length);
29752             
29753             var format = [];
29754             
29755             Roo.each(pattern, function(i){
29756                 format.push(i.size);
29757             }, this);
29758             
29759             Roo.each(standard, function(s){
29760                 
29761                 if(String(s) != String(format)){
29762                     return;
29763                 }
29764                 
29765                 match = true;
29766                 return false;
29767                 
29768             }, this);
29769             
29770             if(!match && length == 1){
29771                 return;
29772             }
29773             
29774             if(!match){
29775                 filterPattern(box, length - 1);
29776                 return;
29777             }
29778                 
29779             queue.push(pattern);
29780
29781             box = box.slice(length, box.length);
29782
29783             filterPattern(box, 4);
29784
29785             return;
29786             
29787         }
29788         
29789         Roo.each(boxes, function(box, k){
29790             
29791             if(!box.length){
29792                 return;
29793             }
29794             
29795             if(box.length == 1){
29796                 queue.push(box);
29797                 return;
29798             }
29799             
29800             filterPattern(box, 4);
29801             
29802         }, this);
29803         
29804         
29805         var prune = [];
29806         
29807         var pos = this.el.getBox(true);
29808         
29809         var minX = pos.x;
29810         
29811         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29812         
29813         var hit_end = false;
29814         
29815         Roo.each(queue, function(box){
29816             
29817             if(hit_end){
29818                 
29819                 Roo.each(box, function(b){
29820                 
29821                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29822                     b.el.hide();
29823
29824                 }, this);
29825
29826                 return;
29827             }
29828             
29829             var mx = 0;
29830             
29831             Roo.each(box, function(b){
29832                 
29833                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29834                 b.el.show();
29835
29836                 mx = Math.max(mx, b.x);
29837                 
29838             }, this);
29839             
29840             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29841             
29842             if(maxX < minX){
29843                 
29844                 Roo.each(box, function(b){
29845                 
29846                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29847                     b.el.hide();
29848                     
29849                 }, this);
29850                 
29851                 hit_end = true;
29852                 
29853                 return;
29854             }
29855             
29856             prune.push(box);
29857             
29858         }, this);
29859         
29860         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29861     },
29862     
29863     /** Sets position of item in DOM
29864     * @param {Element} item
29865     * @param {Number} x - horizontal position
29866     * @param {Number} y - vertical position
29867     * @param {Boolean} isInstant - disables transitions
29868     */
29869     _processVerticalLayoutQueue : function( queue, isInstant )
29870     {
29871         var pos = this.el.getBox(true);
29872         var x = pos.x;
29873         var y = pos.y;
29874         var maxY = [];
29875         
29876         for (var i = 0; i < this.cols; i++){
29877             maxY[i] = pos.y;
29878         }
29879         
29880         Roo.each(queue, function(box, k){
29881             
29882             var col = k % this.cols;
29883             
29884             Roo.each(box, function(b,kk){
29885                 
29886                 b.el.position('absolute');
29887                 
29888                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29889                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29890                 
29891                 if(b.size == 'md-left' || b.size == 'md-right'){
29892                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29893                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29894                 }
29895                 
29896                 b.el.setWidth(width);
29897                 b.el.setHeight(height);
29898                 // iframe?
29899                 b.el.select('iframe',true).setSize(width,height);
29900                 
29901             }, this);
29902             
29903             for (var i = 0; i < this.cols; i++){
29904                 
29905                 if(maxY[i] < maxY[col]){
29906                     col = i;
29907                     continue;
29908                 }
29909                 
29910                 col = Math.min(col, i);
29911                 
29912             }
29913             
29914             x = pos.x + col * (this.colWidth + this.padWidth);
29915             
29916             y = maxY[col];
29917             
29918             var positions = [];
29919             
29920             switch (box.length){
29921                 case 1 :
29922                     positions = this.getVerticalOneBoxColPositions(x, y, box);
29923                     break;
29924                 case 2 :
29925                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
29926                     break;
29927                 case 3 :
29928                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
29929                     break;
29930                 case 4 :
29931                     positions = this.getVerticalFourBoxColPositions(x, y, box);
29932                     break;
29933                 default :
29934                     break;
29935             }
29936             
29937             Roo.each(box, function(b,kk){
29938                 
29939                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29940                 
29941                 var sz = b.el.getSize();
29942                 
29943                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29944                 
29945             }, this);
29946             
29947         }, this);
29948         
29949         var mY = 0;
29950         
29951         for (var i = 0; i < this.cols; i++){
29952             mY = Math.max(mY, maxY[i]);
29953         }
29954         
29955         this.el.setHeight(mY - pos.y);
29956         
29957     },
29958     
29959 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29960 //    {
29961 //        var pos = this.el.getBox(true);
29962 //        var x = pos.x;
29963 //        var y = pos.y;
29964 //        var maxX = pos.right;
29965 //        
29966 //        var maxHeight = 0;
29967 //        
29968 //        Roo.each(items, function(item, k){
29969 //            
29970 //            var c = k % 2;
29971 //            
29972 //            item.el.position('absolute');
29973 //                
29974 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29975 //
29976 //            item.el.setWidth(width);
29977 //
29978 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
29979 //
29980 //            item.el.setHeight(height);
29981 //            
29982 //            if(c == 0){
29983 //                item.el.setXY([x, y], isInstant ? false : true);
29984 //            } else {
29985 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
29986 //            }
29987 //            
29988 //            y = y + height + this.alternativePadWidth;
29989 //            
29990 //            maxHeight = maxHeight + height + this.alternativePadWidth;
29991 //            
29992 //        }, this);
29993 //        
29994 //        this.el.setHeight(maxHeight);
29995 //        
29996 //    },
29997     
29998     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
29999     {
30000         var pos = this.el.getBox(true);
30001         
30002         var minX = pos.x;
30003         var minY = pos.y;
30004         
30005         var maxX = pos.right;
30006         
30007         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
30008         
30009         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30010         
30011         Roo.each(queue, function(box, k){
30012             
30013             Roo.each(box, function(b, kk){
30014                 
30015                 b.el.position('absolute');
30016                 
30017                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30018                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30019                 
30020                 if(b.size == 'md-left' || b.size == 'md-right'){
30021                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30022                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30023                 }
30024                 
30025                 b.el.setWidth(width);
30026                 b.el.setHeight(height);
30027                 
30028             }, this);
30029             
30030             if(!box.length){
30031                 return;
30032             }
30033             
30034             var positions = [];
30035             
30036             switch (box.length){
30037                 case 1 :
30038                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
30039                     break;
30040                 case 2 :
30041                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
30042                     break;
30043                 case 3 :
30044                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
30045                     break;
30046                 case 4 :
30047                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
30048                     break;
30049                 default :
30050                     break;
30051             }
30052             
30053             Roo.each(box, function(b,kk){
30054                 
30055                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30056                 
30057                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
30058                 
30059             }, this);
30060             
30061         }, this);
30062         
30063     },
30064     
30065     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
30066     {
30067         Roo.each(eItems, function(b,k){
30068             
30069             b.size = (k == 0) ? 'sm' : 'xs';
30070             b.x = (k == 0) ? 2 : 1;
30071             b.y = (k == 0) ? 2 : 1;
30072             
30073             b.el.position('absolute');
30074             
30075             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30076                 
30077             b.el.setWidth(width);
30078             
30079             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30080             
30081             b.el.setHeight(height);
30082             
30083         }, this);
30084
30085         var positions = [];
30086         
30087         positions.push({
30088             x : maxX - this.unitWidth * 2 - this.gutter,
30089             y : minY
30090         });
30091         
30092         positions.push({
30093             x : maxX - this.unitWidth,
30094             y : minY + (this.unitWidth + this.gutter) * 2
30095         });
30096         
30097         positions.push({
30098             x : maxX - this.unitWidth * 3 - this.gutter * 2,
30099             y : minY
30100         });
30101         
30102         Roo.each(eItems, function(b,k){
30103             
30104             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
30105
30106         }, this);
30107         
30108     },
30109     
30110     getVerticalOneBoxColPositions : function(x, y, box)
30111     {
30112         var pos = [];
30113         
30114         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
30115         
30116         if(box[0].size == 'md-left'){
30117             rand = 0;
30118         }
30119         
30120         if(box[0].size == 'md-right'){
30121             rand = 1;
30122         }
30123         
30124         pos.push({
30125             x : x + (this.unitWidth + this.gutter) * rand,
30126             y : y
30127         });
30128         
30129         return pos;
30130     },
30131     
30132     getVerticalTwoBoxColPositions : function(x, y, box)
30133     {
30134         var pos = [];
30135         
30136         if(box[0].size == 'xs'){
30137             
30138             pos.push({
30139                 x : x,
30140                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
30141             });
30142
30143             pos.push({
30144                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
30145                 y : y
30146             });
30147             
30148             return pos;
30149             
30150         }
30151         
30152         pos.push({
30153             x : x,
30154             y : y
30155         });
30156
30157         pos.push({
30158             x : x + (this.unitWidth + this.gutter) * 2,
30159             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
30160         });
30161         
30162         return pos;
30163         
30164     },
30165     
30166     getVerticalThreeBoxColPositions : function(x, y, box)
30167     {
30168         var pos = [];
30169         
30170         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30171             
30172             pos.push({
30173                 x : x,
30174                 y : y
30175             });
30176
30177             pos.push({
30178                 x : x + (this.unitWidth + this.gutter) * 1,
30179                 y : y
30180             });
30181             
30182             pos.push({
30183                 x : x + (this.unitWidth + this.gutter) * 2,
30184                 y : y
30185             });
30186             
30187             return pos;
30188             
30189         }
30190         
30191         if(box[0].size == 'xs' && box[1].size == 'xs'){
30192             
30193             pos.push({
30194                 x : x,
30195                 y : y
30196             });
30197
30198             pos.push({
30199                 x : x,
30200                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
30201             });
30202             
30203             pos.push({
30204                 x : x + (this.unitWidth + this.gutter) * 1,
30205                 y : y
30206             });
30207             
30208             return pos;
30209             
30210         }
30211         
30212         pos.push({
30213             x : x,
30214             y : y
30215         });
30216
30217         pos.push({
30218             x : x + (this.unitWidth + this.gutter) * 2,
30219             y : y
30220         });
30221
30222         pos.push({
30223             x : x + (this.unitWidth + this.gutter) * 2,
30224             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
30225         });
30226             
30227         return pos;
30228         
30229     },
30230     
30231     getVerticalFourBoxColPositions : function(x, y, box)
30232     {
30233         var pos = [];
30234         
30235         if(box[0].size == 'xs'){
30236             
30237             pos.push({
30238                 x : x,
30239                 y : y
30240             });
30241
30242             pos.push({
30243                 x : x,
30244                 y : y + (this.unitHeight + this.gutter) * 1
30245             });
30246             
30247             pos.push({
30248                 x : x,
30249                 y : y + (this.unitHeight + this.gutter) * 2
30250             });
30251             
30252             pos.push({
30253                 x : x + (this.unitWidth + this.gutter) * 1,
30254                 y : y
30255             });
30256             
30257             return pos;
30258             
30259         }
30260         
30261         pos.push({
30262             x : x,
30263             y : y
30264         });
30265
30266         pos.push({
30267             x : x + (this.unitWidth + this.gutter) * 2,
30268             y : y
30269         });
30270
30271         pos.push({
30272             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
30273             y : y + (this.unitHeight + this.gutter) * 1
30274         });
30275
30276         pos.push({
30277             x : x + (this.unitWidth + this.gutter) * 2,
30278             y : y + (this.unitWidth + this.gutter) * 2
30279         });
30280
30281         return pos;
30282         
30283     },
30284     
30285     getHorizontalOneBoxColPositions : function(maxX, minY, box)
30286     {
30287         var pos = [];
30288         
30289         if(box[0].size == 'md-left'){
30290             pos.push({
30291                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30292                 y : minY
30293             });
30294             
30295             return pos;
30296         }
30297         
30298         if(box[0].size == 'md-right'){
30299             pos.push({
30300                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30301                 y : minY + (this.unitWidth + this.gutter) * 1
30302             });
30303             
30304             return pos;
30305         }
30306         
30307         var rand = Math.floor(Math.random() * (4 - box[0].y));
30308         
30309         pos.push({
30310             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30311             y : minY + (this.unitWidth + this.gutter) * rand
30312         });
30313         
30314         return pos;
30315         
30316     },
30317     
30318     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
30319     {
30320         var pos = [];
30321         
30322         if(box[0].size == 'xs'){
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) * (3 - box[1].y)
30332             });
30333             
30334             return pos;
30335             
30336         }
30337         
30338         pos.push({
30339             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30340             y : minY
30341         });
30342
30343         pos.push({
30344             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30345             y : minY + (this.unitWidth + this.gutter) * 2
30346         });
30347         
30348         return pos;
30349         
30350     },
30351     
30352     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
30353     {
30354         var pos = [];
30355         
30356         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30357             
30358             pos.push({
30359                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30360                 y : minY
30361             });
30362
30363             pos.push({
30364                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30365                 y : minY + (this.unitWidth + this.gutter) * 1
30366             });
30367             
30368             pos.push({
30369                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30370                 y : minY + (this.unitWidth + this.gutter) * 2
30371             });
30372             
30373             return pos;
30374             
30375         }
30376         
30377         if(box[0].size == 'xs' && box[1].size == 'xs'){
30378             
30379             pos.push({
30380                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30381                 y : minY
30382             });
30383
30384             pos.push({
30385                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30386                 y : minY
30387             });
30388             
30389             pos.push({
30390                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30391                 y : minY + (this.unitWidth + this.gutter) * 1
30392             });
30393             
30394             return pos;
30395             
30396         }
30397         
30398         pos.push({
30399             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30400             y : minY
30401         });
30402
30403         pos.push({
30404             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30405             y : minY + (this.unitWidth + this.gutter) * 2
30406         });
30407
30408         pos.push({
30409             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30410             y : minY + (this.unitWidth + this.gutter) * 2
30411         });
30412             
30413         return pos;
30414         
30415     },
30416     
30417     getHorizontalFourBoxColPositions : function(maxX, minY, box)
30418     {
30419         var pos = [];
30420         
30421         if(box[0].size == 'xs'){
30422             
30423             pos.push({
30424                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30425                 y : minY
30426             });
30427
30428             pos.push({
30429                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30430                 y : minY
30431             });
30432             
30433             pos.push({
30434                 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),
30435                 y : minY
30436             });
30437             
30438             pos.push({
30439                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
30440                 y : minY + (this.unitWidth + this.gutter) * 1
30441             });
30442             
30443             return pos;
30444             
30445         }
30446         
30447         pos.push({
30448             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30449             y : minY
30450         });
30451         
30452         pos.push({
30453             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30454             y : minY + (this.unitWidth + this.gutter) * 2
30455         });
30456         
30457         pos.push({
30458             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30459             y : minY + (this.unitWidth + this.gutter) * 2
30460         });
30461         
30462         pos.push({
30463             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),
30464             y : minY + (this.unitWidth + this.gutter) * 2
30465         });
30466
30467         return pos;
30468         
30469     }
30470     
30471 });
30472
30473  
30474
30475  /**
30476  *
30477  * This is based on 
30478  * http://masonry.desandro.com
30479  *
30480  * The idea is to render all the bricks based on vertical width...
30481  *
30482  * The original code extends 'outlayer' - we might need to use that....
30483  * 
30484  */
30485
30486
30487 /**
30488  * @class Roo.bootstrap.LayoutMasonryAuto
30489  * @extends Roo.bootstrap.Component
30490  * Bootstrap Layout Masonry class
30491  * 
30492  * @constructor
30493  * Create a new Element
30494  * @param {Object} config The config object
30495  */
30496
30497 Roo.bootstrap.LayoutMasonryAuto = function(config){
30498     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
30499 };
30500
30501 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
30502     
30503       /**
30504      * @cfg {Boolean} isFitWidth  - resize the width..
30505      */   
30506     isFitWidth : false,  // options..
30507     /**
30508      * @cfg {Boolean} isOriginLeft = left align?
30509      */   
30510     isOriginLeft : true,
30511     /**
30512      * @cfg {Boolean} isOriginTop = top align?
30513      */   
30514     isOriginTop : false,
30515     /**
30516      * @cfg {Boolean} isLayoutInstant = no animation?
30517      */   
30518     isLayoutInstant : false, // needed?
30519     /**
30520      * @cfg {Boolean} isResizingContainer = not sure if this is used..
30521      */   
30522     isResizingContainer : true,
30523     /**
30524      * @cfg {Number} columnWidth  width of the columns 
30525      */   
30526     
30527     columnWidth : 0,
30528     
30529     /**
30530      * @cfg {Number} maxCols maximum number of columns
30531      */   
30532     
30533     maxCols: 0,
30534     /**
30535      * @cfg {Number} padHeight padding below box..
30536      */   
30537     
30538     padHeight : 10, 
30539     
30540     /**
30541      * @cfg {Boolean} isAutoInitial defalut true
30542      */   
30543     
30544     isAutoInitial : true, 
30545     
30546     // private?
30547     gutter : 0,
30548     
30549     containerWidth: 0,
30550     initialColumnWidth : 0,
30551     currentSize : null,
30552     
30553     colYs : null, // array.
30554     maxY : 0,
30555     padWidth: 10,
30556     
30557     
30558     tag: 'div',
30559     cls: '',
30560     bricks: null, //CompositeElement
30561     cols : 0, // array?
30562     // element : null, // wrapped now this.el
30563     _isLayoutInited : null, 
30564     
30565     
30566     getAutoCreate : function(){
30567         
30568         var cfg = {
30569             tag: this.tag,
30570             cls: 'blog-masonary-wrapper ' + this.cls,
30571             cn : {
30572                 cls : 'mas-boxes masonary'
30573             }
30574         };
30575         
30576         return cfg;
30577     },
30578     
30579     getChildContainer: function( )
30580     {
30581         if (this.boxesEl) {
30582             return this.boxesEl;
30583         }
30584         
30585         this.boxesEl = this.el.select('.mas-boxes').first();
30586         
30587         return this.boxesEl;
30588     },
30589     
30590     
30591     initEvents : function()
30592     {
30593         var _this = this;
30594         
30595         if(this.isAutoInitial){
30596             Roo.log('hook children rendered');
30597             this.on('childrenrendered', function() {
30598                 Roo.log('children rendered');
30599                 _this.initial();
30600             } ,this);
30601         }
30602         
30603     },
30604     
30605     initial : function()
30606     {
30607         this.reloadItems();
30608
30609         this.currentSize = this.el.getBox(true);
30610
30611         /// was window resize... - let's see if this works..
30612         Roo.EventManager.onWindowResize(this.resize, this); 
30613
30614         if(!this.isAutoInitial){
30615             this.layout();
30616             return;
30617         }
30618         
30619         this.layout.defer(500,this);
30620     },
30621     
30622     reloadItems: function()
30623     {
30624         this.bricks = this.el.select('.masonry-brick', true);
30625         
30626         this.bricks.each(function(b) {
30627             //Roo.log(b.getSize());
30628             if (!b.attr('originalwidth')) {
30629                 b.attr('originalwidth',  b.getSize().width);
30630             }
30631             
30632         });
30633         
30634         Roo.log(this.bricks.elements.length);
30635     },
30636     
30637     resize : function()
30638     {
30639         Roo.log('resize');
30640         var cs = this.el.getBox(true);
30641         
30642         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
30643             Roo.log("no change in with or X");
30644             return;
30645         }
30646         this.currentSize = cs;
30647         this.layout();
30648     },
30649     
30650     layout : function()
30651     {
30652          Roo.log('layout');
30653         this._resetLayout();
30654         //this._manageStamps();
30655       
30656         // don't animate first layout
30657         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30658         this.layoutItems( isInstant );
30659       
30660         // flag for initalized
30661         this._isLayoutInited = true;
30662     },
30663     
30664     layoutItems : function( isInstant )
30665     {
30666         //var items = this._getItemsForLayout( this.items );
30667         // original code supports filtering layout items.. we just ignore it..
30668         
30669         this._layoutItems( this.bricks , isInstant );
30670       
30671         this._postLayout();
30672     },
30673     _layoutItems : function ( items , isInstant)
30674     {
30675        //this.fireEvent( 'layout', this, items );
30676     
30677
30678         if ( !items || !items.elements.length ) {
30679           // no items, emit event with empty array
30680             return;
30681         }
30682
30683         var queue = [];
30684         items.each(function(item) {
30685             Roo.log("layout item");
30686             Roo.log(item);
30687             // get x/y object from method
30688             var position = this._getItemLayoutPosition( item );
30689             // enqueue
30690             position.item = item;
30691             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
30692             queue.push( position );
30693         }, this);
30694       
30695         this._processLayoutQueue( queue );
30696     },
30697     /** Sets position of item in DOM
30698     * @param {Element} item
30699     * @param {Number} x - horizontal position
30700     * @param {Number} y - vertical position
30701     * @param {Boolean} isInstant - disables transitions
30702     */
30703     _processLayoutQueue : function( queue )
30704     {
30705         for ( var i=0, len = queue.length; i < len; i++ ) {
30706             var obj = queue[i];
30707             obj.item.position('absolute');
30708             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
30709         }
30710     },
30711       
30712     
30713     /**
30714     * Any logic you want to do after each layout,
30715     * i.e. size the container
30716     */
30717     _postLayout : function()
30718     {
30719         this.resizeContainer();
30720     },
30721     
30722     resizeContainer : function()
30723     {
30724         if ( !this.isResizingContainer ) {
30725             return;
30726         }
30727         var size = this._getContainerSize();
30728         if ( size ) {
30729             this.el.setSize(size.width,size.height);
30730             this.boxesEl.setSize(size.width,size.height);
30731         }
30732     },
30733     
30734     
30735     
30736     _resetLayout : function()
30737     {
30738         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
30739         this.colWidth = this.el.getWidth();
30740         //this.gutter = this.el.getWidth(); 
30741         
30742         this.measureColumns();
30743
30744         // reset column Y
30745         var i = this.cols;
30746         this.colYs = [];
30747         while (i--) {
30748             this.colYs.push( 0 );
30749         }
30750     
30751         this.maxY = 0;
30752     },
30753
30754     measureColumns : function()
30755     {
30756         this.getContainerWidth();
30757       // if columnWidth is 0, default to outerWidth of first item
30758         if ( !this.columnWidth ) {
30759             var firstItem = this.bricks.first();
30760             Roo.log(firstItem);
30761             this.columnWidth  = this.containerWidth;
30762             if (firstItem && firstItem.attr('originalwidth') ) {
30763                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
30764             }
30765             // columnWidth fall back to item of first element
30766             Roo.log("set column width?");
30767                         this.initialColumnWidth = this.columnWidth  ;
30768
30769             // if first elem has no width, default to size of container
30770             
30771         }
30772         
30773         
30774         if (this.initialColumnWidth) {
30775             this.columnWidth = this.initialColumnWidth;
30776         }
30777         
30778         
30779             
30780         // column width is fixed at the top - however if container width get's smaller we should
30781         // reduce it...
30782         
30783         // this bit calcs how man columns..
30784             
30785         var columnWidth = this.columnWidth += this.gutter;
30786       
30787         // calculate columns
30788         var containerWidth = this.containerWidth + this.gutter;
30789         
30790         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
30791         // fix rounding errors, typically with gutters
30792         var excess = columnWidth - containerWidth % columnWidth;
30793         
30794         
30795         // if overshoot is less than a pixel, round up, otherwise floor it
30796         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
30797         cols = Math[ mathMethod ]( cols );
30798         this.cols = Math.max( cols, 1 );
30799         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30800         
30801          // padding positioning..
30802         var totalColWidth = this.cols * this.columnWidth;
30803         var padavail = this.containerWidth - totalColWidth;
30804         // so for 2 columns - we need 3 'pads'
30805         
30806         var padNeeded = (1+this.cols) * this.padWidth;
30807         
30808         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
30809         
30810         this.columnWidth += padExtra
30811         //this.padWidth = Math.floor(padavail /  ( this.cols));
30812         
30813         // adjust colum width so that padding is fixed??
30814         
30815         // we have 3 columns ... total = width * 3
30816         // we have X left over... that should be used by 
30817         
30818         //if (this.expandC) {
30819             
30820         //}
30821         
30822         
30823         
30824     },
30825     
30826     getContainerWidth : function()
30827     {
30828        /* // container is parent if fit width
30829         var container = this.isFitWidth ? this.element.parentNode : this.element;
30830         // check that this.size and size are there
30831         // IE8 triggers resize on body size change, so they might not be
30832         
30833         var size = getSize( container );  //FIXME
30834         this.containerWidth = size && size.innerWidth; //FIXME
30835         */
30836          
30837         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30838         
30839     },
30840     
30841     _getItemLayoutPosition : function( item )  // what is item?
30842     {
30843         // we resize the item to our columnWidth..
30844       
30845         item.setWidth(this.columnWidth);
30846         item.autoBoxAdjust  = false;
30847         
30848         var sz = item.getSize();
30849  
30850         // how many columns does this brick span
30851         var remainder = this.containerWidth % this.columnWidth;
30852         
30853         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30854         // round if off by 1 pixel, otherwise use ceil
30855         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
30856         colSpan = Math.min( colSpan, this.cols );
30857         
30858         // normally this should be '1' as we dont' currently allow multi width columns..
30859         
30860         var colGroup = this._getColGroup( colSpan );
30861         // get the minimum Y value from the columns
30862         var minimumY = Math.min.apply( Math, colGroup );
30863         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30864         
30865         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
30866          
30867         // position the brick
30868         var position = {
30869             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30870             y: this.currentSize.y + minimumY + this.padHeight
30871         };
30872         
30873         Roo.log(position);
30874         // apply setHeight to necessary columns
30875         var setHeight = minimumY + sz.height + this.padHeight;
30876         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30877         
30878         var setSpan = this.cols + 1 - colGroup.length;
30879         for ( var i = 0; i < setSpan; i++ ) {
30880           this.colYs[ shortColIndex + i ] = setHeight ;
30881         }
30882       
30883         return position;
30884     },
30885     
30886     /**
30887      * @param {Number} colSpan - number of columns the element spans
30888      * @returns {Array} colGroup
30889      */
30890     _getColGroup : function( colSpan )
30891     {
30892         if ( colSpan < 2 ) {
30893           // if brick spans only one column, use all the column Ys
30894           return this.colYs;
30895         }
30896       
30897         var colGroup = [];
30898         // how many different places could this brick fit horizontally
30899         var groupCount = this.cols + 1 - colSpan;
30900         // for each group potential horizontal position
30901         for ( var i = 0; i < groupCount; i++ ) {
30902           // make an array of colY values for that one group
30903           var groupColYs = this.colYs.slice( i, i + colSpan );
30904           // and get the max value of the array
30905           colGroup[i] = Math.max.apply( Math, groupColYs );
30906         }
30907         return colGroup;
30908     },
30909     /*
30910     _manageStamp : function( stamp )
30911     {
30912         var stampSize =  stamp.getSize();
30913         var offset = stamp.getBox();
30914         // get the columns that this stamp affects
30915         var firstX = this.isOriginLeft ? offset.x : offset.right;
30916         var lastX = firstX + stampSize.width;
30917         var firstCol = Math.floor( firstX / this.columnWidth );
30918         firstCol = Math.max( 0, firstCol );
30919         
30920         var lastCol = Math.floor( lastX / this.columnWidth );
30921         // lastCol should not go over if multiple of columnWidth #425
30922         lastCol -= lastX % this.columnWidth ? 0 : 1;
30923         lastCol = Math.min( this.cols - 1, lastCol );
30924         
30925         // set colYs to bottom of the stamp
30926         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30927             stampSize.height;
30928             
30929         for ( var i = firstCol; i <= lastCol; i++ ) {
30930           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30931         }
30932     },
30933     */
30934     
30935     _getContainerSize : function()
30936     {
30937         this.maxY = Math.max.apply( Math, this.colYs );
30938         var size = {
30939             height: this.maxY
30940         };
30941       
30942         if ( this.isFitWidth ) {
30943             size.width = this._getContainerFitWidth();
30944         }
30945       
30946         return size;
30947     },
30948     
30949     _getContainerFitWidth : function()
30950     {
30951         var unusedCols = 0;
30952         // count unused columns
30953         var i = this.cols;
30954         while ( --i ) {
30955           if ( this.colYs[i] !== 0 ) {
30956             break;
30957           }
30958           unusedCols++;
30959         }
30960         // fit container to columns that have been used
30961         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30962     },
30963     
30964     needsResizeLayout : function()
30965     {
30966         var previousWidth = this.containerWidth;
30967         this.getContainerWidth();
30968         return previousWidth !== this.containerWidth;
30969     }
30970  
30971 });
30972
30973  
30974
30975  /*
30976  * - LGPL
30977  *
30978  * element
30979  * 
30980  */
30981
30982 /**
30983  * @class Roo.bootstrap.MasonryBrick
30984  * @extends Roo.bootstrap.Component
30985  * Bootstrap MasonryBrick class
30986  * 
30987  * @constructor
30988  * Create a new MasonryBrick
30989  * @param {Object} config The config object
30990  */
30991
30992 Roo.bootstrap.MasonryBrick = function(config){
30993     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
30994     
30995     this.addEvents({
30996         // raw events
30997         /**
30998          * @event click
30999          * When a MasonryBrick is clcik
31000          * @param {Roo.bootstrap.MasonryBrick} this
31001          * @param {Roo.EventObject} e
31002          */
31003         "click" : true
31004     });
31005 };
31006
31007 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
31008     
31009     /**
31010      * @cfg {String} title
31011      */   
31012     title : '',
31013     /**
31014      * @cfg {String} html
31015      */   
31016     html : '',
31017     /**
31018      * @cfg {String} bgimage
31019      */   
31020     bgimage : '',
31021     /**
31022      * @cfg {String} videourl
31023      */   
31024     videourl : '',
31025     /**
31026      * @cfg {String} cls
31027      */   
31028     cls : '',
31029     /**
31030      * @cfg {String} href
31031      */   
31032     href : '',
31033     /**
31034      * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
31035      */   
31036     size : 'xs',
31037     
31038     /**
31039      * @cfg {String} (center|bottom) placetitle
31040      */   
31041     placetitle : '',
31042     
31043     /**
31044      * @cfg {Boolean} isFitContainer defalut true
31045      */   
31046     isFitContainer : true, 
31047     
31048     /**
31049      * @cfg {Boolean} preventDefault defalut false
31050      */   
31051     preventDefault : false, 
31052     
31053     getAutoCreate : function()
31054     {
31055         if(!this.isFitContainer){
31056             return this.getSplitAutoCreate();
31057         }
31058         
31059         var cls = 'masonry-brick masonry-brick-full';
31060         
31061         if(this.href.length){
31062             cls += ' masonry-brick-link';
31063         }
31064         
31065         if(this.bgimage.length){
31066             cls += ' masonry-brick-image';
31067         }
31068         
31069         if(!this.html.length){
31070             cls += ' enable-mask';
31071         }
31072         
31073         if(this.size){
31074             cls += ' masonry-' + this.size + '-brick';
31075         }
31076         
31077         if(this.placetitle.length){
31078             
31079             switch (this.placetitle) {
31080                 case 'center' :
31081                     cls += ' masonry-center-title';
31082                     break;
31083                 case 'bottom' :
31084                     cls += ' masonry-bottom-title';
31085                     break;
31086                 default:
31087                     break;
31088             }
31089             
31090         } else {
31091             if(!this.html.length && !this.bgimage.length){
31092                 cls += ' masonry-center-title';
31093             }
31094
31095             if(!this.html.length && this.bgimage.length){
31096                 cls += ' masonry-bottom-title';
31097             }
31098         }
31099         
31100         if(this.cls){
31101             cls += ' ' + this.cls;
31102         }
31103         
31104         var cfg = {
31105             tag: (this.href.length) ? 'a' : 'div',
31106             cls: cls,
31107             cn: [
31108                 {
31109                     tag: 'div',
31110                     cls: 'masonry-brick-paragraph',
31111                     cn: []
31112                 }
31113             ]
31114         };
31115         
31116         if(this.href.length){
31117             cfg.href = this.href;
31118         }
31119         
31120         var cn = cfg.cn[0].cn;
31121         
31122         if(this.title.length){
31123             cn.push({
31124                 tag: 'h4',
31125                 cls: 'masonry-brick-title',
31126                 html: this.title
31127             });
31128         }
31129         
31130         if(this.html.length){
31131             cn.push({
31132                 tag: 'p',
31133                 cls: 'masonry-brick-text',
31134                 html: this.html
31135             });
31136         }  
31137         if (!this.title.length && !this.html.length) {
31138             cfg.cn[0].cls += ' hide';
31139         }
31140         
31141         if(this.bgimage.length){
31142             cfg.cn.push({
31143                 tag: 'img',
31144                 cls: 'masonry-brick-image-view',
31145                 src: this.bgimage
31146             });
31147         }
31148         
31149         if(this.videourl.length){
31150             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31151             // youtube support only?
31152             cfg.cn.push({
31153                 tag: 'iframe',
31154                 cls: 'masonry-brick-image-view',
31155                 src: vurl,
31156                 frameborder : 0,
31157                 allowfullscreen : true
31158             });
31159             
31160             
31161         }
31162         
31163         cfg.cn.push({
31164             tag: 'div',
31165             cls: 'masonry-brick-mask'
31166         });
31167         
31168         return cfg;
31169         
31170     },
31171     
31172     getSplitAutoCreate : function()
31173     {
31174         var cls = 'masonry-brick masonry-brick-split';
31175         
31176         if(this.href.length){
31177             cls += ' masonry-brick-link';
31178         }
31179         
31180         if(this.bgimage.length){
31181             cls += ' masonry-brick-image';
31182         }
31183         
31184         if(this.size){
31185             cls += ' masonry-' + this.size + '-brick';
31186         }
31187         
31188         switch (this.placetitle) {
31189             case 'center' :
31190                 cls += ' masonry-center-title';
31191                 break;
31192             case 'bottom' :
31193                 cls += ' masonry-bottom-title';
31194                 break;
31195             default:
31196                 if(!this.bgimage.length){
31197                     cls += ' masonry-center-title';
31198                 }
31199
31200                 if(this.bgimage.length){
31201                     cls += ' masonry-bottom-title';
31202                 }
31203                 break;
31204         }
31205         
31206         if(this.cls){
31207             cls += ' ' + this.cls;
31208         }
31209         
31210         var cfg = {
31211             tag: (this.href.length) ? 'a' : 'div',
31212             cls: cls,
31213             cn: [
31214                 {
31215                     tag: 'div',
31216                     cls: 'masonry-brick-split-head',
31217                     cn: [
31218                         {
31219                             tag: 'div',
31220                             cls: 'masonry-brick-paragraph',
31221                             cn: []
31222                         }
31223                     ]
31224                 },
31225                 {
31226                     tag: 'div',
31227                     cls: 'masonry-brick-split-body',
31228                     cn: []
31229                 }
31230             ]
31231         };
31232         
31233         if(this.href.length){
31234             cfg.href = this.href;
31235         }
31236         
31237         if(this.title.length){
31238             cfg.cn[0].cn[0].cn.push({
31239                 tag: 'h4',
31240                 cls: 'masonry-brick-title',
31241                 html: this.title
31242             });
31243         }
31244         
31245         if(this.html.length){
31246             cfg.cn[1].cn.push({
31247                 tag: 'p',
31248                 cls: 'masonry-brick-text',
31249                 html: this.html
31250             });
31251         }
31252
31253         if(this.bgimage.length){
31254             cfg.cn[0].cn.push({
31255                 tag: 'img',
31256                 cls: 'masonry-brick-image-view',
31257                 src: this.bgimage
31258             });
31259         }
31260         
31261         if(this.videourl.length){
31262             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31263             // youtube support only?
31264             cfg.cn[0].cn.cn.push({
31265                 tag: 'iframe',
31266                 cls: 'masonry-brick-image-view',
31267                 src: vurl,
31268                 frameborder : 0,
31269                 allowfullscreen : true
31270             });
31271         }
31272         
31273         return cfg;
31274     },
31275     
31276     initEvents: function() 
31277     {
31278         switch (this.size) {
31279             case 'xs' :
31280                 this.x = 1;
31281                 this.y = 1;
31282                 break;
31283             case 'sm' :
31284                 this.x = 2;
31285                 this.y = 2;
31286                 break;
31287             case 'md' :
31288             case 'md-left' :
31289             case 'md-right' :
31290                 this.x = 3;
31291                 this.y = 3;
31292                 break;
31293             case 'tall' :
31294                 this.x = 2;
31295                 this.y = 3;
31296                 break;
31297             case 'wide' :
31298                 this.x = 3;
31299                 this.y = 2;
31300                 break;
31301             case 'wide-thin' :
31302                 this.x = 3;
31303                 this.y = 1;
31304                 break;
31305                         
31306             default :
31307                 break;
31308         }
31309         
31310         if(Roo.isTouch){
31311             this.el.on('touchstart', this.onTouchStart, this);
31312             this.el.on('touchmove', this.onTouchMove, this);
31313             this.el.on('touchend', this.onTouchEnd, this);
31314             this.el.on('contextmenu', this.onContextMenu, this);
31315         } else {
31316             this.el.on('mouseenter'  ,this.enter, this);
31317             this.el.on('mouseleave', this.leave, this);
31318             this.el.on('click', this.onClick, this);
31319         }
31320         
31321         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
31322             this.parent().bricks.push(this);   
31323         }
31324         
31325     },
31326     
31327     onClick: function(e, el)
31328     {
31329         var time = this.endTimer - this.startTimer;
31330         
31331         if(Roo.isTouch){
31332             if(time > 1000){
31333                 e.preventDefault();
31334                 return;
31335             }
31336         }
31337         
31338         if(!this.preventDefault){
31339             return;
31340         }
31341         
31342         e.preventDefault();
31343         this.fireEvent('click', this);
31344     },
31345     
31346     enter: function(e, el)
31347     {
31348         e.preventDefault();
31349         
31350         if(!this.isFitContainer){
31351             return;
31352         }
31353         
31354         if(this.bgimage.length && this.html.length){
31355             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31356         }
31357     },
31358     
31359     leave: function(e, el)
31360     {
31361         e.preventDefault();
31362         
31363         if(!this.isFitContainer){
31364             return;
31365         }
31366         
31367         if(this.bgimage.length && this.html.length){
31368             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31369         }
31370     },
31371     
31372     onTouchStart: function(e, el)
31373     {
31374 //        e.preventDefault();
31375         
31376         this.touchmoved = false;
31377         
31378         if(!this.isFitContainer){
31379             return;
31380         }
31381         
31382         if(!this.bgimage.length || !this.html.length){
31383             return;
31384         }
31385         
31386         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31387         
31388         this.timer = new Date().getTime();
31389         
31390     },
31391     
31392     onTouchMove: function(e, el)
31393     {
31394         this.touchmoved = true;
31395     },
31396     
31397     onContextMenu : function(e,el)
31398     {
31399         e.preventDefault();
31400         e.stopPropagation();
31401         return false;
31402     },
31403     
31404     onTouchEnd: function(e, el)
31405     {
31406 //        e.preventDefault();
31407         
31408         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
31409         
31410             this.leave(e,el);
31411             
31412             return;
31413         }
31414         
31415         if(!this.bgimage.length || !this.html.length){
31416             
31417             if(this.href.length){
31418                 window.location.href = this.href;
31419             }
31420             
31421             return;
31422         }
31423         
31424         if(!this.isFitContainer){
31425             return;
31426         }
31427         
31428         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31429         
31430         window.location.href = this.href;
31431     }
31432     
31433 });
31434
31435  
31436
31437  /*
31438  * - LGPL
31439  *
31440  * element
31441  * 
31442  */
31443
31444 /**
31445  * @class Roo.bootstrap.Brick
31446  * @extends Roo.bootstrap.Component
31447  * Bootstrap Brick class
31448  * 
31449  * @constructor
31450  * Create a new Brick
31451  * @param {Object} config The config object
31452  */
31453
31454 Roo.bootstrap.Brick = function(config){
31455     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
31456     
31457     this.addEvents({
31458         // raw events
31459         /**
31460          * @event click
31461          * When a Brick is click
31462          * @param {Roo.bootstrap.Brick} this
31463          * @param {Roo.EventObject} e
31464          */
31465         "click" : true
31466     });
31467 };
31468
31469 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
31470     
31471     /**
31472      * @cfg {String} title
31473      */   
31474     title : '',
31475     /**
31476      * @cfg {String} html
31477      */   
31478     html : '',
31479     /**
31480      * @cfg {String} bgimage
31481      */   
31482     bgimage : '',
31483     /**
31484      * @cfg {String} cls
31485      */   
31486     cls : '',
31487     /**
31488      * @cfg {String} href
31489      */   
31490     href : '',
31491     /**
31492      * @cfg {String} video
31493      */   
31494     video : '',
31495     /**
31496      * @cfg {Boolean} square
31497      */   
31498     square : true,
31499     
31500     getAutoCreate : function()
31501     {
31502         var cls = 'roo-brick';
31503         
31504         if(this.href.length){
31505             cls += ' roo-brick-link';
31506         }
31507         
31508         if(this.bgimage.length){
31509             cls += ' roo-brick-image';
31510         }
31511         
31512         if(!this.html.length && !this.bgimage.length){
31513             cls += ' roo-brick-center-title';
31514         }
31515         
31516         if(!this.html.length && this.bgimage.length){
31517             cls += ' roo-brick-bottom-title';
31518         }
31519         
31520         if(this.cls){
31521             cls += ' ' + this.cls;
31522         }
31523         
31524         var cfg = {
31525             tag: (this.href.length) ? 'a' : 'div',
31526             cls: cls,
31527             cn: [
31528                 {
31529                     tag: 'div',
31530                     cls: 'roo-brick-paragraph',
31531                     cn: []
31532                 }
31533             ]
31534         };
31535         
31536         if(this.href.length){
31537             cfg.href = this.href;
31538         }
31539         
31540         var cn = cfg.cn[0].cn;
31541         
31542         if(this.title.length){
31543             cn.push({
31544                 tag: 'h4',
31545                 cls: 'roo-brick-title',
31546                 html: this.title
31547             });
31548         }
31549         
31550         if(this.html.length){
31551             cn.push({
31552                 tag: 'p',
31553                 cls: 'roo-brick-text',
31554                 html: this.html
31555             });
31556         } else {
31557             cn.cls += ' hide';
31558         }
31559         
31560         if(this.bgimage.length){
31561             cfg.cn.push({
31562                 tag: 'img',
31563                 cls: 'roo-brick-image-view',
31564                 src: this.bgimage
31565             });
31566         }
31567         
31568         return cfg;
31569     },
31570     
31571     initEvents: function() 
31572     {
31573         if(this.title.length || this.html.length){
31574             this.el.on('mouseenter'  ,this.enter, this);
31575             this.el.on('mouseleave', this.leave, this);
31576         }
31577         
31578         
31579         Roo.EventManager.onWindowResize(this.resize, this); 
31580         
31581         this.resize();
31582     },
31583     
31584     resize : function()
31585     {
31586         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
31587         
31588         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
31589         
31590         if(this.bgimage.length){
31591             var image = this.el.select('.roo-brick-image-view', true).first();
31592             image.setWidth(paragraph.getWidth());
31593             image.setHeight(paragraph.getWidth());
31594             
31595             this.el.setHeight(paragraph.getWidth());
31596             
31597         }
31598         
31599     },
31600     
31601     enter: function(e, el)
31602     {
31603         e.preventDefault();
31604         
31605         if(this.bgimage.length){
31606             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
31607             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
31608         }
31609     },
31610     
31611     leave: function(e, el)
31612     {
31613         e.preventDefault();
31614         
31615         if(this.bgimage.length){
31616             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
31617             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
31618         }
31619     }
31620     
31621 });
31622
31623  
31624
31625  /*
31626  * Based on:
31627  * Ext JS Library 1.1.1
31628  * Copyright(c) 2006-2007, Ext JS, LLC.
31629  *
31630  * Originally Released Under LGPL - original licence link has changed is not relivant.
31631  *
31632  * Fork - LGPL
31633  * <script type="text/javascript">
31634  */
31635
31636
31637 /**
31638  * @class Roo.bootstrap.SplitBar
31639  * @extends Roo.util.Observable
31640  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
31641  * <br><br>
31642  * Usage:
31643  * <pre><code>
31644 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
31645                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
31646 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
31647 split.minSize = 100;
31648 split.maxSize = 600;
31649 split.animate = true;
31650 split.on('moved', splitterMoved);
31651 </code></pre>
31652  * @constructor
31653  * Create a new SplitBar
31654  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
31655  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
31656  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31657  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
31658                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
31659                         position of the SplitBar).
31660  */
31661 Roo.bootstrap.SplitBar = function(cfg){
31662     
31663     /** @private */
31664     
31665     //{
31666     //  dragElement : elm
31667     //  resizingElement: el,
31668         // optional..
31669     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
31670     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
31671         // existingProxy ???
31672     //}
31673     
31674     this.el = Roo.get(cfg.dragElement, true);
31675     this.el.dom.unselectable = "on";
31676     /** @private */
31677     this.resizingEl = Roo.get(cfg.resizingElement, true);
31678
31679     /**
31680      * @private
31681      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31682      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
31683      * @type Number
31684      */
31685     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
31686     
31687     /**
31688      * The minimum size of the resizing element. (Defaults to 0)
31689      * @type Number
31690      */
31691     this.minSize = 0;
31692     
31693     /**
31694      * The maximum size of the resizing element. (Defaults to 2000)
31695      * @type Number
31696      */
31697     this.maxSize = 2000;
31698     
31699     /**
31700      * Whether to animate the transition to the new size
31701      * @type Boolean
31702      */
31703     this.animate = false;
31704     
31705     /**
31706      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
31707      * @type Boolean
31708      */
31709     this.useShim = false;
31710     
31711     /** @private */
31712     this.shim = null;
31713     
31714     if(!cfg.existingProxy){
31715         /** @private */
31716         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
31717     }else{
31718         this.proxy = Roo.get(cfg.existingProxy).dom;
31719     }
31720     /** @private */
31721     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
31722     
31723     /** @private */
31724     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
31725     
31726     /** @private */
31727     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
31728     
31729     /** @private */
31730     this.dragSpecs = {};
31731     
31732     /**
31733      * @private The adapter to use to positon and resize elements
31734      */
31735     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31736     this.adapter.init(this);
31737     
31738     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31739         /** @private */
31740         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
31741         this.el.addClass("roo-splitbar-h");
31742     }else{
31743         /** @private */
31744         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
31745         this.el.addClass("roo-splitbar-v");
31746     }
31747     
31748     this.addEvents({
31749         /**
31750          * @event resize
31751          * Fires when the splitter is moved (alias for {@link #event-moved})
31752          * @param {Roo.bootstrap.SplitBar} this
31753          * @param {Number} newSize the new width or height
31754          */
31755         "resize" : true,
31756         /**
31757          * @event moved
31758          * Fires when the splitter is moved
31759          * @param {Roo.bootstrap.SplitBar} this
31760          * @param {Number} newSize the new width or height
31761          */
31762         "moved" : true,
31763         /**
31764          * @event beforeresize
31765          * Fires before the splitter is dragged
31766          * @param {Roo.bootstrap.SplitBar} this
31767          */
31768         "beforeresize" : true,
31769
31770         "beforeapply" : true
31771     });
31772
31773     Roo.util.Observable.call(this);
31774 };
31775
31776 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
31777     onStartProxyDrag : function(x, y){
31778         this.fireEvent("beforeresize", this);
31779         if(!this.overlay){
31780             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
31781             o.unselectable();
31782             o.enableDisplayMode("block");
31783             // all splitbars share the same overlay
31784             Roo.bootstrap.SplitBar.prototype.overlay = o;
31785         }
31786         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
31787         this.overlay.show();
31788         Roo.get(this.proxy).setDisplayed("block");
31789         var size = this.adapter.getElementSize(this);
31790         this.activeMinSize = this.getMinimumSize();;
31791         this.activeMaxSize = this.getMaximumSize();;
31792         var c1 = size - this.activeMinSize;
31793         var c2 = Math.max(this.activeMaxSize - size, 0);
31794         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31795             this.dd.resetConstraints();
31796             this.dd.setXConstraint(
31797                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
31798                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
31799             );
31800             this.dd.setYConstraint(0, 0);
31801         }else{
31802             this.dd.resetConstraints();
31803             this.dd.setXConstraint(0, 0);
31804             this.dd.setYConstraint(
31805                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
31806                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
31807             );
31808          }
31809         this.dragSpecs.startSize = size;
31810         this.dragSpecs.startPoint = [x, y];
31811         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
31812     },
31813     
31814     /** 
31815      * @private Called after the drag operation by the DDProxy
31816      */
31817     onEndProxyDrag : function(e){
31818         Roo.get(this.proxy).setDisplayed(false);
31819         var endPoint = Roo.lib.Event.getXY(e);
31820         if(this.overlay){
31821             this.overlay.hide();
31822         }
31823         var newSize;
31824         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31825             newSize = this.dragSpecs.startSize + 
31826                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
31827                     endPoint[0] - this.dragSpecs.startPoint[0] :
31828                     this.dragSpecs.startPoint[0] - endPoint[0]
31829                 );
31830         }else{
31831             newSize = this.dragSpecs.startSize + 
31832                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
31833                     endPoint[1] - this.dragSpecs.startPoint[1] :
31834                     this.dragSpecs.startPoint[1] - endPoint[1]
31835                 );
31836         }
31837         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
31838         if(newSize != this.dragSpecs.startSize){
31839             if(this.fireEvent('beforeapply', this, newSize) !== false){
31840                 this.adapter.setElementSize(this, newSize);
31841                 this.fireEvent("moved", this, newSize);
31842                 this.fireEvent("resize", this, newSize);
31843             }
31844         }
31845     },
31846     
31847     /**
31848      * Get the adapter this SplitBar uses
31849      * @return The adapter object
31850      */
31851     getAdapter : function(){
31852         return this.adapter;
31853     },
31854     
31855     /**
31856      * Set the adapter this SplitBar uses
31857      * @param {Object} adapter A SplitBar adapter object
31858      */
31859     setAdapter : function(adapter){
31860         this.adapter = adapter;
31861         this.adapter.init(this);
31862     },
31863     
31864     /**
31865      * Gets the minimum size for the resizing element
31866      * @return {Number} The minimum size
31867      */
31868     getMinimumSize : function(){
31869         return this.minSize;
31870     },
31871     
31872     /**
31873      * Sets the minimum size for the resizing element
31874      * @param {Number} minSize The minimum size
31875      */
31876     setMinimumSize : function(minSize){
31877         this.minSize = minSize;
31878     },
31879     
31880     /**
31881      * Gets the maximum size for the resizing element
31882      * @return {Number} The maximum size
31883      */
31884     getMaximumSize : function(){
31885         return this.maxSize;
31886     },
31887     
31888     /**
31889      * Sets the maximum size for the resizing element
31890      * @param {Number} maxSize The maximum size
31891      */
31892     setMaximumSize : function(maxSize){
31893         this.maxSize = maxSize;
31894     },
31895     
31896     /**
31897      * Sets the initialize size for the resizing element
31898      * @param {Number} size The initial size
31899      */
31900     setCurrentSize : function(size){
31901         var oldAnimate = this.animate;
31902         this.animate = false;
31903         this.adapter.setElementSize(this, size);
31904         this.animate = oldAnimate;
31905     },
31906     
31907     /**
31908      * Destroy this splitbar. 
31909      * @param {Boolean} removeEl True to remove the element
31910      */
31911     destroy : function(removeEl){
31912         if(this.shim){
31913             this.shim.remove();
31914         }
31915         this.dd.unreg();
31916         this.proxy.parentNode.removeChild(this.proxy);
31917         if(removeEl){
31918             this.el.remove();
31919         }
31920     }
31921 });
31922
31923 /**
31924  * @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.
31925  */
31926 Roo.bootstrap.SplitBar.createProxy = function(dir){
31927     var proxy = new Roo.Element(document.createElement("div"));
31928     proxy.unselectable();
31929     var cls = 'roo-splitbar-proxy';
31930     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
31931     document.body.appendChild(proxy.dom);
31932     return proxy.dom;
31933 };
31934
31935 /** 
31936  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
31937  * Default Adapter. It assumes the splitter and resizing element are not positioned
31938  * elements and only gets/sets the width of the element. Generally used for table based layouts.
31939  */
31940 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
31941 };
31942
31943 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
31944     // do nothing for now
31945     init : function(s){
31946     
31947     },
31948     /**
31949      * Called before drag operations to get the current size of the resizing element. 
31950      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31951      */
31952      getElementSize : function(s){
31953         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31954             return s.resizingEl.getWidth();
31955         }else{
31956             return s.resizingEl.getHeight();
31957         }
31958     },
31959     
31960     /**
31961      * Called after drag operations to set the size of the resizing element.
31962      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31963      * @param {Number} newSize The new size to set
31964      * @param {Function} onComplete A function to be invoked when resizing is complete
31965      */
31966     setElementSize : function(s, newSize, onComplete){
31967         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31968             if(!s.animate){
31969                 s.resizingEl.setWidth(newSize);
31970                 if(onComplete){
31971                     onComplete(s, newSize);
31972                 }
31973             }else{
31974                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
31975             }
31976         }else{
31977             
31978             if(!s.animate){
31979                 s.resizingEl.setHeight(newSize);
31980                 if(onComplete){
31981                     onComplete(s, newSize);
31982                 }
31983             }else{
31984                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
31985             }
31986         }
31987     }
31988 };
31989
31990 /** 
31991  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
31992  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
31993  * Adapter that  moves the splitter element to align with the resized sizing element. 
31994  * Used with an absolute positioned SplitBar.
31995  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
31996  * document.body, make sure you assign an id to the body element.
31997  */
31998 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
31999     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
32000     this.container = Roo.get(container);
32001 };
32002
32003 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
32004     init : function(s){
32005         this.basic.init(s);
32006     },
32007     
32008     getElementSize : function(s){
32009         return this.basic.getElementSize(s);
32010     },
32011     
32012     setElementSize : function(s, newSize, onComplete){
32013         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
32014     },
32015     
32016     moveSplitter : function(s){
32017         var yes = Roo.bootstrap.SplitBar;
32018         switch(s.placement){
32019             case yes.LEFT:
32020                 s.el.setX(s.resizingEl.getRight());
32021                 break;
32022             case yes.RIGHT:
32023                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
32024                 break;
32025             case yes.TOP:
32026                 s.el.setY(s.resizingEl.getBottom());
32027                 break;
32028             case yes.BOTTOM:
32029                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
32030                 break;
32031         }
32032     }
32033 };
32034
32035 /**
32036  * Orientation constant - Create a vertical SplitBar
32037  * @static
32038  * @type Number
32039  */
32040 Roo.bootstrap.SplitBar.VERTICAL = 1;
32041
32042 /**
32043  * Orientation constant - Create a horizontal SplitBar
32044  * @static
32045  * @type Number
32046  */
32047 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
32048
32049 /**
32050  * Placement constant - The resizing element is to the left of the splitter element
32051  * @static
32052  * @type Number
32053  */
32054 Roo.bootstrap.SplitBar.LEFT = 1;
32055
32056 /**
32057  * Placement constant - The resizing element is to the right of the splitter element
32058  * @static
32059  * @type Number
32060  */
32061 Roo.bootstrap.SplitBar.RIGHT = 2;
32062
32063 /**
32064  * Placement constant - The resizing element is positioned above the splitter element
32065  * @static
32066  * @type Number
32067  */
32068 Roo.bootstrap.SplitBar.TOP = 3;
32069
32070 /**
32071  * Placement constant - The resizing element is positioned under splitter element
32072  * @static
32073  * @type Number
32074  */
32075 Roo.bootstrap.SplitBar.BOTTOM = 4;
32076 Roo.namespace("Roo.bootstrap.layout");/*
32077  * Based on:
32078  * Ext JS Library 1.1.1
32079  * Copyright(c) 2006-2007, Ext JS, LLC.
32080  *
32081  * Originally Released Under LGPL - original licence link has changed is not relivant.
32082  *
32083  * Fork - LGPL
32084  * <script type="text/javascript">
32085  */
32086
32087 /**
32088  * @class Roo.bootstrap.layout.Manager
32089  * @extends Roo.bootstrap.Component
32090  * Base class for layout managers.
32091  */
32092 Roo.bootstrap.layout.Manager = function(config)
32093 {
32094     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
32095
32096
32097
32098
32099
32100     /** false to disable window resize monitoring @type Boolean */
32101     this.monitorWindowResize = true;
32102     this.regions = {};
32103     this.addEvents({
32104         /**
32105          * @event layout
32106          * Fires when a layout is performed.
32107          * @param {Roo.LayoutManager} this
32108          */
32109         "layout" : true,
32110         /**
32111          * @event regionresized
32112          * Fires when the user resizes a region.
32113          * @param {Roo.LayoutRegion} region The resized region
32114          * @param {Number} newSize The new size (width for east/west, height for north/south)
32115          */
32116         "regionresized" : true,
32117         /**
32118          * @event regioncollapsed
32119          * Fires when a region is collapsed.
32120          * @param {Roo.LayoutRegion} region The collapsed region
32121          */
32122         "regioncollapsed" : true,
32123         /**
32124          * @event regionexpanded
32125          * Fires when a region is expanded.
32126          * @param {Roo.LayoutRegion} region The expanded region
32127          */
32128         "regionexpanded" : true
32129     });
32130     this.updating = false;
32131
32132     if (config.el) {
32133         this.el = Roo.get(config.el);
32134         this.initEvents();
32135     }
32136
32137 };
32138
32139 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
32140
32141
32142     regions : null,
32143
32144     monitorWindowResize : true,
32145
32146
32147     updating : false,
32148
32149
32150     onRender : function(ct, position)
32151     {
32152         if(!this.el){
32153             this.el = Roo.get(ct);
32154             this.initEvents();
32155         }
32156         //this.fireEvent('render',this);
32157     },
32158
32159
32160     initEvents: function()
32161     {
32162
32163
32164         // ie scrollbar fix
32165         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32166             document.body.scroll = "no";
32167         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32168             this.el.position('relative');
32169         }
32170         this.id = this.el.id;
32171         this.el.addClass("roo-layout-container");
32172         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32173         if(this.el.dom != document.body ) {
32174             this.el.on('resize', this.layout,this);
32175             this.el.on('show', this.layout,this);
32176         }
32177
32178     },
32179
32180     /**
32181      * Returns true if this layout is currently being updated
32182      * @return {Boolean}
32183      */
32184     isUpdating : function(){
32185         return this.updating;
32186     },
32187
32188     /**
32189      * Suspend the LayoutManager from doing auto-layouts while
32190      * making multiple add or remove calls
32191      */
32192     beginUpdate : function(){
32193         this.updating = true;
32194     },
32195
32196     /**
32197      * Restore auto-layouts and optionally disable the manager from performing a layout
32198      * @param {Boolean} noLayout true to disable a layout update
32199      */
32200     endUpdate : function(noLayout){
32201         this.updating = false;
32202         if(!noLayout){
32203             this.layout();
32204         }
32205     },
32206
32207     layout: function(){
32208         // abstract...
32209     },
32210
32211     onRegionResized : function(region, newSize){
32212         this.fireEvent("regionresized", region, newSize);
32213         this.layout();
32214     },
32215
32216     onRegionCollapsed : function(region){
32217         this.fireEvent("regioncollapsed", region);
32218     },
32219
32220     onRegionExpanded : function(region){
32221         this.fireEvent("regionexpanded", region);
32222     },
32223
32224     /**
32225      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32226      * performs box-model adjustments.
32227      * @return {Object} The size as an object {width: (the width), height: (the height)}
32228      */
32229     getViewSize : function()
32230     {
32231         var size;
32232         if(this.el.dom != document.body){
32233             size = this.el.getSize();
32234         }else{
32235             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32236         }
32237         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32238         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32239         return size;
32240     },
32241
32242     /**
32243      * Returns the Element this layout is bound to.
32244      * @return {Roo.Element}
32245      */
32246     getEl : function(){
32247         return this.el;
32248     },
32249
32250     /**
32251      * Returns the specified region.
32252      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32253      * @return {Roo.LayoutRegion}
32254      */
32255     getRegion : function(target){
32256         return this.regions[target.toLowerCase()];
32257     },
32258
32259     onWindowResize : function(){
32260         if(this.monitorWindowResize){
32261             this.layout();
32262         }
32263     }
32264 });
32265 /*
32266  * Based on:
32267  * Ext JS Library 1.1.1
32268  * Copyright(c) 2006-2007, Ext JS, LLC.
32269  *
32270  * Originally Released Under LGPL - original licence link has changed is not relivant.
32271  *
32272  * Fork - LGPL
32273  * <script type="text/javascript">
32274  */
32275 /**
32276  * @class Roo.bootstrap.layout.Border
32277  * @extends Roo.bootstrap.layout.Manager
32278  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32279  * please see: examples/bootstrap/nested.html<br><br>
32280  
32281 <b>The container the layout is rendered into can be either the body element or any other element.
32282 If it is not the body element, the container needs to either be an absolute positioned element,
32283 or you will need to add "position:relative" to the css of the container.  You will also need to specify
32284 the container size if it is not the body element.</b>
32285
32286 * @constructor
32287 * Create a new Border
32288 * @param {Object} config Configuration options
32289  */
32290 Roo.bootstrap.layout.Border = function(config){
32291     config = config || {};
32292     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
32293     
32294     
32295     
32296     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
32297         if(config[region]){
32298             config[region].region = region;
32299             this.addRegion(config[region]);
32300         }
32301     },this);
32302     
32303 };
32304
32305 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
32306
32307 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
32308     /**
32309      * Creates and adds a new region if it doesn't already exist.
32310      * @param {String} target The target region key (north, south, east, west or center).
32311      * @param {Object} config The regions config object
32312      * @return {BorderLayoutRegion} The new region
32313      */
32314     addRegion : function(config)
32315     {
32316         if(!this.regions[config.region]){
32317             var r = this.factory(config);
32318             this.bindRegion(r);
32319         }
32320         return this.regions[config.region];
32321     },
32322
32323     // private (kinda)
32324     bindRegion : function(r){
32325         this.regions[r.config.region] = r;
32326         
32327         r.on("visibilitychange",    this.layout, this);
32328         r.on("paneladded",          this.layout, this);
32329         r.on("panelremoved",        this.layout, this);
32330         r.on("invalidated",         this.layout, this);
32331         r.on("resized",             this.onRegionResized, this);
32332         r.on("collapsed",           this.onRegionCollapsed, this);
32333         r.on("expanded",            this.onRegionExpanded, this);
32334     },
32335
32336     /**
32337      * Performs a layout update.
32338      */
32339     layout : function()
32340     {
32341         if(this.updating) {
32342             return;
32343         }
32344         
32345         // render all the rebions if they have not been done alreayd?
32346         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
32347             if(this.regions[region] && !this.regions[region].bodyEl){
32348                 this.regions[region].onRender(this.el)
32349             }
32350         },this);
32351         
32352         var size = this.getViewSize();
32353         var w = size.width;
32354         var h = size.height;
32355         var centerW = w;
32356         var centerH = h;
32357         var centerY = 0;
32358         var centerX = 0;
32359         //var x = 0, y = 0;
32360
32361         var rs = this.regions;
32362         var north = rs["north"];
32363         var south = rs["south"]; 
32364         var west = rs["west"];
32365         var east = rs["east"];
32366         var center = rs["center"];
32367         //if(this.hideOnLayout){ // not supported anymore
32368             //c.el.setStyle("display", "none");
32369         //}
32370         if(north && north.isVisible()){
32371             var b = north.getBox();
32372             var m = north.getMargins();
32373             b.width = w - (m.left+m.right);
32374             b.x = m.left;
32375             b.y = m.top;
32376             centerY = b.height + b.y + m.bottom;
32377             centerH -= centerY;
32378             north.updateBox(this.safeBox(b));
32379         }
32380         if(south && south.isVisible()){
32381             var b = south.getBox();
32382             var m = south.getMargins();
32383             b.width = w - (m.left+m.right);
32384             b.x = m.left;
32385             var totalHeight = (b.height + m.top + m.bottom);
32386             b.y = h - totalHeight + m.top;
32387             centerH -= totalHeight;
32388             south.updateBox(this.safeBox(b));
32389         }
32390         if(west && west.isVisible()){
32391             var b = west.getBox();
32392             var m = west.getMargins();
32393             b.height = centerH - (m.top+m.bottom);
32394             b.x = m.left;
32395             b.y = centerY + m.top;
32396             var totalWidth = (b.width + m.left + m.right);
32397             centerX += totalWidth;
32398             centerW -= totalWidth;
32399             west.updateBox(this.safeBox(b));
32400         }
32401         if(east && east.isVisible()){
32402             var b = east.getBox();
32403             var m = east.getMargins();
32404             b.height = centerH - (m.top+m.bottom);
32405             var totalWidth = (b.width + m.left + m.right);
32406             b.x = w - totalWidth + m.left;
32407             b.y = centerY + m.top;
32408             centerW -= totalWidth;
32409             east.updateBox(this.safeBox(b));
32410         }
32411         if(center){
32412             var m = center.getMargins();
32413             var centerBox = {
32414                 x: centerX + m.left,
32415                 y: centerY + m.top,
32416                 width: centerW - (m.left+m.right),
32417                 height: centerH - (m.top+m.bottom)
32418             };
32419             //if(this.hideOnLayout){
32420                 //center.el.setStyle("display", "block");
32421             //}
32422             center.updateBox(this.safeBox(centerBox));
32423         }
32424         this.el.repaint();
32425         this.fireEvent("layout", this);
32426     },
32427
32428     // private
32429     safeBox : function(box){
32430         box.width = Math.max(0, box.width);
32431         box.height = Math.max(0, box.height);
32432         return box;
32433     },
32434
32435     /**
32436      * Adds a ContentPanel (or subclass) to this layout.
32437      * @param {String} target The target region key (north, south, east, west or center).
32438      * @param {Roo.ContentPanel} panel The panel to add
32439      * @return {Roo.ContentPanel} The added panel
32440      */
32441     add : function(target, panel){
32442          
32443         target = target.toLowerCase();
32444         return this.regions[target].add(panel);
32445     },
32446
32447     /**
32448      * Remove a ContentPanel (or subclass) to this layout.
32449      * @param {String} target The target region key (north, south, east, west or center).
32450      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
32451      * @return {Roo.ContentPanel} The removed panel
32452      */
32453     remove : function(target, panel){
32454         target = target.toLowerCase();
32455         return this.regions[target].remove(panel);
32456     },
32457
32458     /**
32459      * Searches all regions for a panel with the specified id
32460      * @param {String} panelId
32461      * @return {Roo.ContentPanel} The panel or null if it wasn't found
32462      */
32463     findPanel : function(panelId){
32464         var rs = this.regions;
32465         for(var target in rs){
32466             if(typeof rs[target] != "function"){
32467                 var p = rs[target].getPanel(panelId);
32468                 if(p){
32469                     return p;
32470                 }
32471             }
32472         }
32473         return null;
32474     },
32475
32476     /**
32477      * Searches all regions for a panel with the specified id and activates (shows) it.
32478      * @param {String/ContentPanel} panelId The panels id or the panel itself
32479      * @return {Roo.ContentPanel} The shown panel or null
32480      */
32481     showPanel : function(panelId) {
32482       var rs = this.regions;
32483       for(var target in rs){
32484          var r = rs[target];
32485          if(typeof r != "function"){
32486             if(r.hasPanel(panelId)){
32487                return r.showPanel(panelId);
32488             }
32489          }
32490       }
32491       return null;
32492    },
32493
32494    /**
32495      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
32496      * @param {Roo.state.Provider} provider (optional) An alternate state provider
32497      */
32498    /*
32499     restoreState : function(provider){
32500         if(!provider){
32501             provider = Roo.state.Manager;
32502         }
32503         var sm = new Roo.LayoutStateManager();
32504         sm.init(this, provider);
32505     },
32506 */
32507  
32508  
32509     /**
32510      * Adds a xtype elements to the layout.
32511      * <pre><code>
32512
32513 layout.addxtype({
32514        xtype : 'ContentPanel',
32515        region: 'west',
32516        items: [ .... ]
32517    }
32518 );
32519
32520 layout.addxtype({
32521         xtype : 'NestedLayoutPanel',
32522         region: 'west',
32523         layout: {
32524            center: { },
32525            west: { }   
32526         },
32527         items : [ ... list of content panels or nested layout panels.. ]
32528    }
32529 );
32530 </code></pre>
32531      * @param {Object} cfg Xtype definition of item to add.
32532      */
32533     addxtype : function(cfg)
32534     {
32535         // basically accepts a pannel...
32536         // can accept a layout region..!?!?
32537         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
32538         
32539         
32540         // theory?  children can only be panels??
32541         
32542         //if (!cfg.xtype.match(/Panel$/)) {
32543         //    return false;
32544         //}
32545         var ret = false;
32546         
32547         if (typeof(cfg.region) == 'undefined') {
32548             Roo.log("Failed to add Panel, region was not set");
32549             Roo.log(cfg);
32550             return false;
32551         }
32552         var region = cfg.region;
32553         delete cfg.region;
32554         
32555           
32556         var xitems = [];
32557         if (cfg.items) {
32558             xitems = cfg.items;
32559             delete cfg.items;
32560         }
32561         var nb = false;
32562         
32563         switch(cfg.xtype) 
32564         {
32565             case 'Content':  // ContentPanel (el, cfg)
32566             case 'Scroll':  // ContentPanel (el, cfg)
32567             case 'View': 
32568                 cfg.autoCreate = true;
32569                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32570                 //} else {
32571                 //    var el = this.el.createChild();
32572                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
32573                 //}
32574                 
32575                 this.add(region, ret);
32576                 break;
32577             
32578             /*
32579             case 'TreePanel': // our new panel!
32580                 cfg.el = this.el.createChild();
32581                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32582                 this.add(region, ret);
32583                 break;
32584             */
32585             
32586             case 'Nest': 
32587                 // create a new Layout (which is  a Border Layout...
32588                 
32589                 var clayout = cfg.layout;
32590                 clayout.el  = this.el.createChild();
32591                 clayout.items   = clayout.items  || [];
32592                 
32593                 delete cfg.layout;
32594                 
32595                 // replace this exitems with the clayout ones..
32596                 xitems = clayout.items;
32597                  
32598                 // force background off if it's in center...
32599                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
32600                     cfg.background = false;
32601                 }
32602                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
32603                 
32604                 
32605                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32606                 //console.log('adding nested layout panel '  + cfg.toSource());
32607                 this.add(region, ret);
32608                 nb = {}; /// find first...
32609                 break;
32610             
32611             case 'Grid':
32612                 
32613                 // needs grid and region
32614                 
32615                 //var el = this.getRegion(region).el.createChild();
32616                 /*
32617                  *var el = this.el.createChild();
32618                 // create the grid first...
32619                 cfg.grid.container = el;
32620                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
32621                 */
32622                 
32623                 if (region == 'center' && this.active ) {
32624                     cfg.background = false;
32625                 }
32626                 
32627                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32628                 
32629                 this.add(region, ret);
32630                 /*
32631                 if (cfg.background) {
32632                     // render grid on panel activation (if panel background)
32633                     ret.on('activate', function(gp) {
32634                         if (!gp.grid.rendered) {
32635                     //        gp.grid.render(el);
32636                         }
32637                     });
32638                 } else {
32639                   //  cfg.grid.render(el);
32640                 }
32641                 */
32642                 break;
32643            
32644            
32645             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
32646                 // it was the old xcomponent building that caused this before.
32647                 // espeically if border is the top element in the tree.
32648                 ret = this;
32649                 break; 
32650                 
32651                     
32652                 
32653                 
32654                 
32655             default:
32656                 /*
32657                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
32658                     
32659                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32660                     this.add(region, ret);
32661                 } else {
32662                 */
32663                     Roo.log(cfg);
32664                     throw "Can not add '" + cfg.xtype + "' to Border";
32665                     return null;
32666              
32667                                 
32668              
32669         }
32670         this.beginUpdate();
32671         // add children..
32672         var region = '';
32673         var abn = {};
32674         Roo.each(xitems, function(i)  {
32675             region = nb && i.region ? i.region : false;
32676             
32677             var add = ret.addxtype(i);
32678            
32679             if (region) {
32680                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
32681                 if (!i.background) {
32682                     abn[region] = nb[region] ;
32683                 }
32684             }
32685             
32686         });
32687         this.endUpdate();
32688
32689         // make the last non-background panel active..
32690         //if (nb) { Roo.log(abn); }
32691         if (nb) {
32692             
32693             for(var r in abn) {
32694                 region = this.getRegion(r);
32695                 if (region) {
32696                     // tried using nb[r], but it does not work..
32697                      
32698                     region.showPanel(abn[r]);
32699                    
32700                 }
32701             }
32702         }
32703         return ret;
32704         
32705     },
32706     
32707     
32708 // private
32709     factory : function(cfg)
32710     {
32711         
32712         var validRegions = Roo.bootstrap.layout.Border.regions;
32713
32714         var target = cfg.region;
32715         cfg.mgr = this;
32716         
32717         var r = Roo.bootstrap.layout;
32718         Roo.log(target);
32719         switch(target){
32720             case "north":
32721                 return new r.North(cfg);
32722             case "south":
32723                 return new r.South(cfg);
32724             case "east":
32725                 return new r.East(cfg);
32726             case "west":
32727                 return new r.West(cfg);
32728             case "center":
32729                 return new r.Center(cfg);
32730         }
32731         throw 'Layout region "'+target+'" not supported.';
32732     }
32733     
32734     
32735 });
32736  /*
32737  * Based on:
32738  * Ext JS Library 1.1.1
32739  * Copyright(c) 2006-2007, Ext JS, LLC.
32740  *
32741  * Originally Released Under LGPL - original licence link has changed is not relivant.
32742  *
32743  * Fork - LGPL
32744  * <script type="text/javascript">
32745  */
32746  
32747 /**
32748  * @class Roo.bootstrap.layout.Basic
32749  * @extends Roo.util.Observable
32750  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
32751  * and does not have a titlebar, tabs or any other features. All it does is size and position 
32752  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
32753  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
32754  * @cfg {string}   region  the region that it inhabits..
32755  * @cfg {bool}   skipConfig skip config?
32756  * 
32757
32758  */
32759 Roo.bootstrap.layout.Basic = function(config){
32760     
32761     this.mgr = config.mgr;
32762     
32763     this.position = config.region;
32764     
32765     var skipConfig = config.skipConfig;
32766     
32767     this.events = {
32768         /**
32769          * @scope Roo.BasicLayoutRegion
32770          */
32771         
32772         /**
32773          * @event beforeremove
32774          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
32775          * @param {Roo.LayoutRegion} this
32776          * @param {Roo.ContentPanel} panel The panel
32777          * @param {Object} e The cancel event object
32778          */
32779         "beforeremove" : true,
32780         /**
32781          * @event invalidated
32782          * Fires when the layout for this region is changed.
32783          * @param {Roo.LayoutRegion} this
32784          */
32785         "invalidated" : true,
32786         /**
32787          * @event visibilitychange
32788          * Fires when this region is shown or hidden 
32789          * @param {Roo.LayoutRegion} this
32790          * @param {Boolean} visibility true or false
32791          */
32792         "visibilitychange" : true,
32793         /**
32794          * @event paneladded
32795          * Fires when a panel is added. 
32796          * @param {Roo.LayoutRegion} this
32797          * @param {Roo.ContentPanel} panel The panel
32798          */
32799         "paneladded" : true,
32800         /**
32801          * @event panelremoved
32802          * Fires when a panel is removed. 
32803          * @param {Roo.LayoutRegion} this
32804          * @param {Roo.ContentPanel} panel The panel
32805          */
32806         "panelremoved" : true,
32807         /**
32808          * @event beforecollapse
32809          * Fires when this region before collapse.
32810          * @param {Roo.LayoutRegion} this
32811          */
32812         "beforecollapse" : true,
32813         /**
32814          * @event collapsed
32815          * Fires when this region is collapsed.
32816          * @param {Roo.LayoutRegion} this
32817          */
32818         "collapsed" : true,
32819         /**
32820          * @event expanded
32821          * Fires when this region is expanded.
32822          * @param {Roo.LayoutRegion} this
32823          */
32824         "expanded" : true,
32825         /**
32826          * @event slideshow
32827          * Fires when this region is slid into view.
32828          * @param {Roo.LayoutRegion} this
32829          */
32830         "slideshow" : true,
32831         /**
32832          * @event slidehide
32833          * Fires when this region slides out of view. 
32834          * @param {Roo.LayoutRegion} this
32835          */
32836         "slidehide" : true,
32837         /**
32838          * @event panelactivated
32839          * Fires when a panel is activated. 
32840          * @param {Roo.LayoutRegion} this
32841          * @param {Roo.ContentPanel} panel The activated panel
32842          */
32843         "panelactivated" : true,
32844         /**
32845          * @event resized
32846          * Fires when the user resizes this region. 
32847          * @param {Roo.LayoutRegion} this
32848          * @param {Number} newSize The new size (width for east/west, height for north/south)
32849          */
32850         "resized" : true
32851     };
32852     /** A collection of panels in this region. @type Roo.util.MixedCollection */
32853     this.panels = new Roo.util.MixedCollection();
32854     this.panels.getKey = this.getPanelId.createDelegate(this);
32855     this.box = null;
32856     this.activePanel = null;
32857     // ensure listeners are added...
32858     
32859     if (config.listeners || config.events) {
32860         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
32861             listeners : config.listeners || {},
32862             events : config.events || {}
32863         });
32864     }
32865     
32866     if(skipConfig !== true){
32867         this.applyConfig(config);
32868     }
32869 };
32870
32871 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
32872 {
32873     getPanelId : function(p){
32874         return p.getId();
32875     },
32876     
32877     applyConfig : function(config){
32878         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32879         this.config = config;
32880         
32881     },
32882     
32883     /**
32884      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
32885      * the width, for horizontal (north, south) the height.
32886      * @param {Number} newSize The new width or height
32887      */
32888     resizeTo : function(newSize){
32889         var el = this.el ? this.el :
32890                  (this.activePanel ? this.activePanel.getEl() : null);
32891         if(el){
32892             switch(this.position){
32893                 case "east":
32894                 case "west":
32895                     el.setWidth(newSize);
32896                     this.fireEvent("resized", this, newSize);
32897                 break;
32898                 case "north":
32899                 case "south":
32900                     el.setHeight(newSize);
32901                     this.fireEvent("resized", this, newSize);
32902                 break;                
32903             }
32904         }
32905     },
32906     
32907     getBox : function(){
32908         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
32909     },
32910     
32911     getMargins : function(){
32912         return this.margins;
32913     },
32914     
32915     updateBox : function(box){
32916         this.box = box;
32917         var el = this.activePanel.getEl();
32918         el.dom.style.left = box.x + "px";
32919         el.dom.style.top = box.y + "px";
32920         this.activePanel.setSize(box.width, box.height);
32921     },
32922     
32923     /**
32924      * Returns the container element for this region.
32925      * @return {Roo.Element}
32926      */
32927     getEl : function(){
32928         return this.activePanel;
32929     },
32930     
32931     /**
32932      * Returns true if this region is currently visible.
32933      * @return {Boolean}
32934      */
32935     isVisible : function(){
32936         return this.activePanel ? true : false;
32937     },
32938     
32939     setActivePanel : function(panel){
32940         panel = this.getPanel(panel);
32941         if(this.activePanel && this.activePanel != panel){
32942             this.activePanel.setActiveState(false);
32943             this.activePanel.getEl().setLeftTop(-10000,-10000);
32944         }
32945         this.activePanel = panel;
32946         panel.setActiveState(true);
32947         if(this.box){
32948             panel.setSize(this.box.width, this.box.height);
32949         }
32950         this.fireEvent("panelactivated", this, panel);
32951         this.fireEvent("invalidated");
32952     },
32953     
32954     /**
32955      * Show the specified panel.
32956      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
32957      * @return {Roo.ContentPanel} The shown panel or null
32958      */
32959     showPanel : function(panel){
32960         panel = this.getPanel(panel);
32961         if(panel){
32962             this.setActivePanel(panel);
32963         }
32964         return panel;
32965     },
32966     
32967     /**
32968      * Get the active panel for this region.
32969      * @return {Roo.ContentPanel} The active panel or null
32970      */
32971     getActivePanel : function(){
32972         return this.activePanel;
32973     },
32974     
32975     /**
32976      * Add the passed ContentPanel(s)
32977      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32978      * @return {Roo.ContentPanel} The panel added (if only one was added)
32979      */
32980     add : function(panel){
32981         if(arguments.length > 1){
32982             for(var i = 0, len = arguments.length; i < len; i++) {
32983                 this.add(arguments[i]);
32984             }
32985             return null;
32986         }
32987         if(this.hasPanel(panel)){
32988             this.showPanel(panel);
32989             return panel;
32990         }
32991         var el = panel.getEl();
32992         if(el.dom.parentNode != this.mgr.el.dom){
32993             this.mgr.el.dom.appendChild(el.dom);
32994         }
32995         if(panel.setRegion){
32996             panel.setRegion(this);
32997         }
32998         this.panels.add(panel);
32999         el.setStyle("position", "absolute");
33000         if(!panel.background){
33001             this.setActivePanel(panel);
33002             if(this.config.initialSize && this.panels.getCount()==1){
33003                 this.resizeTo(this.config.initialSize);
33004             }
33005         }
33006         this.fireEvent("paneladded", this, panel);
33007         return panel;
33008     },
33009     
33010     /**
33011      * Returns true if the panel is in this region.
33012      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33013      * @return {Boolean}
33014      */
33015     hasPanel : function(panel){
33016         if(typeof panel == "object"){ // must be panel obj
33017             panel = panel.getId();
33018         }
33019         return this.getPanel(panel) ? true : false;
33020     },
33021     
33022     /**
33023      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33024      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33025      * @param {Boolean} preservePanel Overrides the config preservePanel option
33026      * @return {Roo.ContentPanel} The panel that was removed
33027      */
33028     remove : function(panel, preservePanel){
33029         panel = this.getPanel(panel);
33030         if(!panel){
33031             return null;
33032         }
33033         var e = {};
33034         this.fireEvent("beforeremove", this, panel, e);
33035         if(e.cancel === true){
33036             return null;
33037         }
33038         var panelId = panel.getId();
33039         this.panels.removeKey(panelId);
33040         return panel;
33041     },
33042     
33043     /**
33044      * Returns the panel specified or null if it's not in this region.
33045      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33046      * @return {Roo.ContentPanel}
33047      */
33048     getPanel : function(id){
33049         if(typeof id == "object"){ // must be panel obj
33050             return id;
33051         }
33052         return this.panels.get(id);
33053     },
33054     
33055     /**
33056      * Returns this regions position (north/south/east/west/center).
33057      * @return {String} 
33058      */
33059     getPosition: function(){
33060         return this.position;    
33061     }
33062 });/*
33063  * Based on:
33064  * Ext JS Library 1.1.1
33065  * Copyright(c) 2006-2007, Ext JS, LLC.
33066  *
33067  * Originally Released Under LGPL - original licence link has changed is not relivant.
33068  *
33069  * Fork - LGPL
33070  * <script type="text/javascript">
33071  */
33072  
33073 /**
33074  * @class Roo.bootstrap.layout.Region
33075  * @extends Roo.bootstrap.layout.Basic
33076  * This class represents a region in a layout manager.
33077  
33078  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33079  * @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})
33080  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
33081  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
33082  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
33083  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
33084  * @cfg {String}    title           The title for the region (overrides panel titles)
33085  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
33086  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33087  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
33088  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33089  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
33090  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33091  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
33092  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
33093  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
33094  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
33095
33096  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
33097  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
33098  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
33099  * @cfg {Number}    width           For East/West panels
33100  * @cfg {Number}    height          For North/South panels
33101  * @cfg {Boolean}   split           To show the splitter
33102  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
33103  * 
33104  * @cfg {string}   cls             Extra CSS classes to add to region
33105  * 
33106  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
33107  * @cfg {string}   region  the region that it inhabits..
33108  *
33109
33110  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
33111  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
33112
33113  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
33114  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
33115  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
33116  */
33117 Roo.bootstrap.layout.Region = function(config)
33118 {
33119     this.applyConfig(config);
33120
33121     var mgr = config.mgr;
33122     var pos = config.region;
33123     config.skipConfig = true;
33124     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
33125     
33126     if (mgr.el) {
33127         this.onRender(mgr.el);   
33128     }
33129      
33130     this.visible = true;
33131     this.collapsed = false;
33132     this.unrendered_panels = [];
33133 };
33134
33135 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
33136
33137     position: '', // set by wrapper (eg. north/south etc..)
33138     unrendered_panels : null,  // unrendered panels.
33139     createBody : function(){
33140         /** This region's body element 
33141         * @type Roo.Element */
33142         this.bodyEl = this.el.createChild({
33143                 tag: "div",
33144                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
33145         });
33146     },
33147
33148     onRender: function(ctr, pos)
33149     {
33150         var dh = Roo.DomHelper;
33151         /** This region's container element 
33152         * @type Roo.Element */
33153         this.el = dh.append(ctr.dom, {
33154                 tag: "div",
33155                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
33156             }, true);
33157         /** This region's title element 
33158         * @type Roo.Element */
33159     
33160         this.titleEl = dh.append(this.el.dom,
33161             {
33162                     tag: "div",
33163                     unselectable: "on",
33164                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
33165                     children:[
33166                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
33167                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
33168                     ]}, true);
33169         
33170         this.titleEl.enableDisplayMode();
33171         /** This region's title text element 
33172         * @type HTMLElement */
33173         this.titleTextEl = this.titleEl.dom.firstChild;
33174         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33175         /*
33176         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
33177         this.closeBtn.enableDisplayMode();
33178         this.closeBtn.on("click", this.closeClicked, this);
33179         this.closeBtn.hide();
33180     */
33181         this.createBody(this.config);
33182         if(this.config.hideWhenEmpty){
33183             this.hide();
33184             this.on("paneladded", this.validateVisibility, this);
33185             this.on("panelremoved", this.validateVisibility, this);
33186         }
33187         if(this.autoScroll){
33188             this.bodyEl.setStyle("overflow", "auto");
33189         }else{
33190             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
33191         }
33192         //if(c.titlebar !== false){
33193             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
33194                 this.titleEl.hide();
33195             }else{
33196                 this.titleEl.show();
33197                 if(this.config.title){
33198                     this.titleTextEl.innerHTML = this.config.title;
33199                 }
33200             }
33201         //}
33202         if(this.config.collapsed){
33203             this.collapse(true);
33204         }
33205         if(this.config.hidden){
33206             this.hide();
33207         }
33208         
33209         if (this.unrendered_panels && this.unrendered_panels.length) {
33210             for (var i =0;i< this.unrendered_panels.length; i++) {
33211                 this.add(this.unrendered_panels[i]);
33212             }
33213             this.unrendered_panels = null;
33214             
33215         }
33216         
33217     },
33218     
33219     applyConfig : function(c)
33220     {
33221         /*
33222          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
33223             var dh = Roo.DomHelper;
33224             if(c.titlebar !== false){
33225                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
33226                 this.collapseBtn.on("click", this.collapse, this);
33227                 this.collapseBtn.enableDisplayMode();
33228                 /*
33229                 if(c.showPin === true || this.showPin){
33230                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
33231                     this.stickBtn.enableDisplayMode();
33232                     this.stickBtn.on("click", this.expand, this);
33233                     this.stickBtn.hide();
33234                 }
33235                 
33236             }
33237             */
33238             /** This region's collapsed element
33239             * @type Roo.Element */
33240             /*
33241              *
33242             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33243                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33244             ]}, true);
33245             
33246             if(c.floatable !== false){
33247                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33248                this.collapsedEl.on("click", this.collapseClick, this);
33249             }
33250
33251             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33252                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33253                    id: "message", unselectable: "on", style:{"float":"left"}});
33254                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33255              }
33256             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33257             this.expandBtn.on("click", this.expand, this);
33258             
33259         }
33260         
33261         if(this.collapseBtn){
33262             this.collapseBtn.setVisible(c.collapsible == true);
33263         }
33264         
33265         this.cmargins = c.cmargins || this.cmargins ||
33266                          (this.position == "west" || this.position == "east" ?
33267                              {top: 0, left: 2, right:2, bottom: 0} :
33268                              {top: 2, left: 0, right:0, bottom: 2});
33269         */
33270         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33271         
33272         
33273         this.bottomTabs = c.tabPosition != "top";
33274         
33275         this.autoScroll = c.autoScroll || false;
33276         
33277         
33278        
33279         
33280         this.duration = c.duration || .30;
33281         this.slideDuration = c.slideDuration || .45;
33282         this.config = c;
33283        
33284     },
33285     /**
33286      * Returns true if this region is currently visible.
33287      * @return {Boolean}
33288      */
33289     isVisible : function(){
33290         return this.visible;
33291     },
33292
33293     /**
33294      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33295      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
33296      */
33297     //setCollapsedTitle : function(title){
33298     //    title = title || "&#160;";
33299      //   if(this.collapsedTitleTextEl){
33300       //      this.collapsedTitleTextEl.innerHTML = title;
33301        // }
33302     //},
33303
33304     getBox : function(){
33305         var b;
33306       //  if(!this.collapsed){
33307             b = this.el.getBox(false, true);
33308        // }else{
33309           //  b = this.collapsedEl.getBox(false, true);
33310         //}
33311         return b;
33312     },
33313
33314     getMargins : function(){
33315         return this.margins;
33316         //return this.collapsed ? this.cmargins : this.margins;
33317     },
33318 /*
33319     highlight : function(){
33320         this.el.addClass("x-layout-panel-dragover");
33321     },
33322
33323     unhighlight : function(){
33324         this.el.removeClass("x-layout-panel-dragover");
33325     },
33326 */
33327     updateBox : function(box)
33328     {
33329         if (!this.bodyEl) {
33330             return; // not rendered yet..
33331         }
33332         
33333         this.box = box;
33334         if(!this.collapsed){
33335             this.el.dom.style.left = box.x + "px";
33336             this.el.dom.style.top = box.y + "px";
33337             this.updateBody(box.width, box.height);
33338         }else{
33339             this.collapsedEl.dom.style.left = box.x + "px";
33340             this.collapsedEl.dom.style.top = box.y + "px";
33341             this.collapsedEl.setSize(box.width, box.height);
33342         }
33343         if(this.tabs){
33344             this.tabs.autoSizeTabs();
33345         }
33346     },
33347
33348     updateBody : function(w, h)
33349     {
33350         if(w !== null){
33351             this.el.setWidth(w);
33352             w -= this.el.getBorderWidth("rl");
33353             if(this.config.adjustments){
33354                 w += this.config.adjustments[0];
33355             }
33356         }
33357         if(h !== null && h > 0){
33358             this.el.setHeight(h);
33359             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
33360             h -= this.el.getBorderWidth("tb");
33361             if(this.config.adjustments){
33362                 h += this.config.adjustments[1];
33363             }
33364             this.bodyEl.setHeight(h);
33365             if(this.tabs){
33366                 h = this.tabs.syncHeight(h);
33367             }
33368         }
33369         if(this.panelSize){
33370             w = w !== null ? w : this.panelSize.width;
33371             h = h !== null ? h : this.panelSize.height;
33372         }
33373         if(this.activePanel){
33374             var el = this.activePanel.getEl();
33375             w = w !== null ? w : el.getWidth();
33376             h = h !== null ? h : el.getHeight();
33377             this.panelSize = {width: w, height: h};
33378             this.activePanel.setSize(w, h);
33379         }
33380         if(Roo.isIE && this.tabs){
33381             this.tabs.el.repaint();
33382         }
33383     },
33384
33385     /**
33386      * Returns the container element for this region.
33387      * @return {Roo.Element}
33388      */
33389     getEl : function(){
33390         return this.el;
33391     },
33392
33393     /**
33394      * Hides this region.
33395      */
33396     hide : function(){
33397         //if(!this.collapsed){
33398             this.el.dom.style.left = "-2000px";
33399             this.el.hide();
33400         //}else{
33401          //   this.collapsedEl.dom.style.left = "-2000px";
33402          //   this.collapsedEl.hide();
33403        // }
33404         this.visible = false;
33405         this.fireEvent("visibilitychange", this, false);
33406     },
33407
33408     /**
33409      * Shows this region if it was previously hidden.
33410      */
33411     show : function(){
33412         //if(!this.collapsed){
33413             this.el.show();
33414         //}else{
33415         //    this.collapsedEl.show();
33416        // }
33417         this.visible = true;
33418         this.fireEvent("visibilitychange", this, true);
33419     },
33420 /*
33421     closeClicked : function(){
33422         if(this.activePanel){
33423             this.remove(this.activePanel);
33424         }
33425     },
33426
33427     collapseClick : function(e){
33428         if(this.isSlid){
33429            e.stopPropagation();
33430            this.slideIn();
33431         }else{
33432            e.stopPropagation();
33433            this.slideOut();
33434         }
33435     },
33436 */
33437     /**
33438      * Collapses this region.
33439      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
33440      */
33441     /*
33442     collapse : function(skipAnim, skipCheck = false){
33443         if(this.collapsed) {
33444             return;
33445         }
33446         
33447         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
33448             
33449             this.collapsed = true;
33450             if(this.split){
33451                 this.split.el.hide();
33452             }
33453             if(this.config.animate && skipAnim !== true){
33454                 this.fireEvent("invalidated", this);
33455                 this.animateCollapse();
33456             }else{
33457                 this.el.setLocation(-20000,-20000);
33458                 this.el.hide();
33459                 this.collapsedEl.show();
33460                 this.fireEvent("collapsed", this);
33461                 this.fireEvent("invalidated", this);
33462             }
33463         }
33464         
33465     },
33466 */
33467     animateCollapse : function(){
33468         // overridden
33469     },
33470
33471     /**
33472      * Expands this region if it was previously collapsed.
33473      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
33474      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
33475      */
33476     /*
33477     expand : function(e, skipAnim){
33478         if(e) {
33479             e.stopPropagation();
33480         }
33481         if(!this.collapsed || this.el.hasActiveFx()) {
33482             return;
33483         }
33484         if(this.isSlid){
33485             this.afterSlideIn();
33486             skipAnim = true;
33487         }
33488         this.collapsed = false;
33489         if(this.config.animate && skipAnim !== true){
33490             this.animateExpand();
33491         }else{
33492             this.el.show();
33493             if(this.split){
33494                 this.split.el.show();
33495             }
33496             this.collapsedEl.setLocation(-2000,-2000);
33497             this.collapsedEl.hide();
33498             this.fireEvent("invalidated", this);
33499             this.fireEvent("expanded", this);
33500         }
33501     },
33502 */
33503     animateExpand : function(){
33504         // overridden
33505     },
33506
33507     initTabs : function()
33508     {
33509         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
33510         
33511         var ts = new Roo.bootstrap.panel.Tabs({
33512                 el: this.bodyEl.dom,
33513                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
33514                 disableTooltips: this.config.disableTabTips,
33515                 toolbar : this.config.toolbar
33516             });
33517         
33518         if(this.config.hideTabs){
33519             ts.stripWrap.setDisplayed(false);
33520         }
33521         this.tabs = ts;
33522         ts.resizeTabs = this.config.resizeTabs === true;
33523         ts.minTabWidth = this.config.minTabWidth || 40;
33524         ts.maxTabWidth = this.config.maxTabWidth || 250;
33525         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
33526         ts.monitorResize = false;
33527         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
33528         ts.bodyEl.addClass('roo-layout-tabs-body');
33529         this.panels.each(this.initPanelAsTab, this);
33530     },
33531
33532     initPanelAsTab : function(panel){
33533         var ti = this.tabs.addTab(
33534             panel.getEl().id,
33535             panel.getTitle(),
33536             null,
33537             this.config.closeOnTab && panel.isClosable(),
33538             panel.tpl
33539         );
33540         if(panel.tabTip !== undefined){
33541             ti.setTooltip(panel.tabTip);
33542         }
33543         ti.on("activate", function(){
33544               this.setActivePanel(panel);
33545         }, this);
33546         
33547         if(this.config.closeOnTab){
33548             ti.on("beforeclose", function(t, e){
33549                 e.cancel = true;
33550                 this.remove(panel);
33551             }, this);
33552         }
33553         
33554         panel.tabItem = ti;
33555         
33556         return ti;
33557     },
33558
33559     updatePanelTitle : function(panel, title)
33560     {
33561         if(this.activePanel == panel){
33562             this.updateTitle(title);
33563         }
33564         if(this.tabs){
33565             var ti = this.tabs.getTab(panel.getEl().id);
33566             ti.setText(title);
33567             if(panel.tabTip !== undefined){
33568                 ti.setTooltip(panel.tabTip);
33569             }
33570         }
33571     },
33572
33573     updateTitle : function(title){
33574         if(this.titleTextEl && !this.config.title){
33575             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
33576         }
33577     },
33578
33579     setActivePanel : function(panel)
33580     {
33581         panel = this.getPanel(panel);
33582         if(this.activePanel && this.activePanel != panel){
33583             this.activePanel.setActiveState(false);
33584         }
33585         this.activePanel = panel;
33586         panel.setActiveState(true);
33587         if(this.panelSize){
33588             panel.setSize(this.panelSize.width, this.panelSize.height);
33589         }
33590         if(this.closeBtn){
33591             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
33592         }
33593         this.updateTitle(panel.getTitle());
33594         if(this.tabs){
33595             this.fireEvent("invalidated", this);
33596         }
33597         this.fireEvent("panelactivated", this, panel);
33598     },
33599
33600     /**
33601      * Shows the specified panel.
33602      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
33603      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
33604      */
33605     showPanel : function(panel)
33606     {
33607         panel = this.getPanel(panel);
33608         if(panel){
33609             if(this.tabs){
33610                 var tab = this.tabs.getTab(panel.getEl().id);
33611                 if(tab.isHidden()){
33612                     this.tabs.unhideTab(tab.id);
33613                 }
33614                 tab.activate();
33615             }else{
33616                 this.setActivePanel(panel);
33617             }
33618         }
33619         return panel;
33620     },
33621
33622     /**
33623      * Get the active panel for this region.
33624      * @return {Roo.ContentPanel} The active panel or null
33625      */
33626     getActivePanel : function(){
33627         return this.activePanel;
33628     },
33629
33630     validateVisibility : function(){
33631         if(this.panels.getCount() < 1){
33632             this.updateTitle("&#160;");
33633             this.closeBtn.hide();
33634             this.hide();
33635         }else{
33636             if(!this.isVisible()){
33637                 this.show();
33638             }
33639         }
33640     },
33641
33642     /**
33643      * Adds the passed ContentPanel(s) to this region.
33644      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33645      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
33646      */
33647     add : function(panel)
33648     {
33649         if(arguments.length > 1){
33650             for(var i = 0, len = arguments.length; i < len; i++) {
33651                 this.add(arguments[i]);
33652             }
33653             return null;
33654         }
33655         
33656         // if we have not been rendered yet, then we can not really do much of this..
33657         if (!this.bodyEl) {
33658             this.unrendered_panels.push(panel);
33659             return panel;
33660         }
33661         
33662         
33663         
33664         
33665         if(this.hasPanel(panel)){
33666             this.showPanel(panel);
33667             return panel;
33668         }
33669         panel.setRegion(this);
33670         this.panels.add(panel);
33671        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
33672             // sinle panel - no tab...?? would it not be better to render it with the tabs,
33673             // and hide them... ???
33674             this.bodyEl.dom.appendChild(panel.getEl().dom);
33675             if(panel.background !== true){
33676                 this.setActivePanel(panel);
33677             }
33678             this.fireEvent("paneladded", this, panel);
33679             return panel;
33680         }
33681         */
33682         if(!this.tabs){
33683             this.initTabs();
33684         }else{
33685             this.initPanelAsTab(panel);
33686         }
33687         
33688         
33689         if(panel.background !== true){
33690             this.tabs.activate(panel.getEl().id);
33691         }
33692         this.fireEvent("paneladded", this, panel);
33693         return panel;
33694     },
33695
33696     /**
33697      * Hides the tab for the specified panel.
33698      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33699      */
33700     hidePanel : function(panel){
33701         if(this.tabs && (panel = this.getPanel(panel))){
33702             this.tabs.hideTab(panel.getEl().id);
33703         }
33704     },
33705
33706     /**
33707      * Unhides the tab for a previously hidden panel.
33708      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33709      */
33710     unhidePanel : function(panel){
33711         if(this.tabs && (panel = this.getPanel(panel))){
33712             this.tabs.unhideTab(panel.getEl().id);
33713         }
33714     },
33715
33716     clearPanels : function(){
33717         while(this.panels.getCount() > 0){
33718              this.remove(this.panels.first());
33719         }
33720     },
33721
33722     /**
33723      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33724      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33725      * @param {Boolean} preservePanel Overrides the config preservePanel option
33726      * @return {Roo.ContentPanel} The panel that was removed
33727      */
33728     remove : function(panel, preservePanel)
33729     {
33730         panel = this.getPanel(panel);
33731         if(!panel){
33732             return null;
33733         }
33734         var e = {};
33735         this.fireEvent("beforeremove", this, panel, e);
33736         if(e.cancel === true){
33737             return null;
33738         }
33739         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
33740         var panelId = panel.getId();
33741         this.panels.removeKey(panelId);
33742         if(preservePanel){
33743             document.body.appendChild(panel.getEl().dom);
33744         }
33745         if(this.tabs){
33746             this.tabs.removeTab(panel.getEl().id);
33747         }else if (!preservePanel){
33748             this.bodyEl.dom.removeChild(panel.getEl().dom);
33749         }
33750         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
33751             var p = this.panels.first();
33752             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
33753             tempEl.appendChild(p.getEl().dom);
33754             this.bodyEl.update("");
33755             this.bodyEl.dom.appendChild(p.getEl().dom);
33756             tempEl = null;
33757             this.updateTitle(p.getTitle());
33758             this.tabs = null;
33759             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33760             this.setActivePanel(p);
33761         }
33762         panel.setRegion(null);
33763         if(this.activePanel == panel){
33764             this.activePanel = null;
33765         }
33766         if(this.config.autoDestroy !== false && preservePanel !== true){
33767             try{panel.destroy();}catch(e){}
33768         }
33769         this.fireEvent("panelremoved", this, panel);
33770         return panel;
33771     },
33772
33773     /**
33774      * Returns the TabPanel component used by this region
33775      * @return {Roo.TabPanel}
33776      */
33777     getTabs : function(){
33778         return this.tabs;
33779     },
33780
33781     createTool : function(parentEl, className){
33782         var btn = Roo.DomHelper.append(parentEl, {
33783             tag: "div",
33784             cls: "x-layout-tools-button",
33785             children: [ {
33786                 tag: "div",
33787                 cls: "roo-layout-tools-button-inner " + className,
33788                 html: "&#160;"
33789             }]
33790         }, true);
33791         btn.addClassOnOver("roo-layout-tools-button-over");
33792         return btn;
33793     }
33794 });/*
33795  * Based on:
33796  * Ext JS Library 1.1.1
33797  * Copyright(c) 2006-2007, Ext JS, LLC.
33798  *
33799  * Originally Released Under LGPL - original licence link has changed is not relivant.
33800  *
33801  * Fork - LGPL
33802  * <script type="text/javascript">
33803  */
33804  
33805
33806
33807 /**
33808  * @class Roo.SplitLayoutRegion
33809  * @extends Roo.LayoutRegion
33810  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
33811  */
33812 Roo.bootstrap.layout.Split = function(config){
33813     this.cursor = config.cursor;
33814     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
33815 };
33816
33817 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
33818 {
33819     splitTip : "Drag to resize.",
33820     collapsibleSplitTip : "Drag to resize. Double click to hide.",
33821     useSplitTips : false,
33822
33823     applyConfig : function(config){
33824         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
33825     },
33826     
33827     onRender : function(ctr,pos) {
33828         
33829         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
33830         if(!this.config.split){
33831             return;
33832         }
33833         if(!this.split){
33834             
33835             var splitEl = Roo.DomHelper.append(ctr.dom,  {
33836                             tag: "div",
33837                             id: this.el.id + "-split",
33838                             cls: "roo-layout-split roo-layout-split-"+this.position,
33839                             html: "&#160;"
33840             });
33841             /** The SplitBar for this region 
33842             * @type Roo.SplitBar */
33843             // does not exist yet...
33844             Roo.log([this.position, this.orientation]);
33845             
33846             this.split = new Roo.bootstrap.SplitBar({
33847                 dragElement : splitEl,
33848                 resizingElement: this.el,
33849                 orientation : this.orientation
33850             });
33851             
33852             this.split.on("moved", this.onSplitMove, this);
33853             this.split.useShim = this.config.useShim === true;
33854             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
33855             if(this.useSplitTips){
33856                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
33857             }
33858             //if(config.collapsible){
33859             //    this.split.el.on("dblclick", this.collapse,  this);
33860             //}
33861         }
33862         if(typeof this.config.minSize != "undefined"){
33863             this.split.minSize = this.config.minSize;
33864         }
33865         if(typeof this.config.maxSize != "undefined"){
33866             this.split.maxSize = this.config.maxSize;
33867         }
33868         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
33869             this.hideSplitter();
33870         }
33871         
33872     },
33873
33874     getHMaxSize : function(){
33875          var cmax = this.config.maxSize || 10000;
33876          var center = this.mgr.getRegion("center");
33877          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
33878     },
33879
33880     getVMaxSize : function(){
33881          var cmax = this.config.maxSize || 10000;
33882          var center = this.mgr.getRegion("center");
33883          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
33884     },
33885
33886     onSplitMove : function(split, newSize){
33887         this.fireEvent("resized", this, newSize);
33888     },
33889     
33890     /** 
33891      * Returns the {@link Roo.SplitBar} for this region.
33892      * @return {Roo.SplitBar}
33893      */
33894     getSplitBar : function(){
33895         return this.split;
33896     },
33897     
33898     hide : function(){
33899         this.hideSplitter();
33900         Roo.bootstrap.layout.Split.superclass.hide.call(this);
33901     },
33902
33903     hideSplitter : function(){
33904         if(this.split){
33905             this.split.el.setLocation(-2000,-2000);
33906             this.split.el.hide();
33907         }
33908     },
33909
33910     show : function(){
33911         if(this.split){
33912             this.split.el.show();
33913         }
33914         Roo.bootstrap.layout.Split.superclass.show.call(this);
33915     },
33916     
33917     beforeSlide: function(){
33918         if(Roo.isGecko){// firefox overflow auto bug workaround
33919             this.bodyEl.clip();
33920             if(this.tabs) {
33921                 this.tabs.bodyEl.clip();
33922             }
33923             if(this.activePanel){
33924                 this.activePanel.getEl().clip();
33925                 
33926                 if(this.activePanel.beforeSlide){
33927                     this.activePanel.beforeSlide();
33928                 }
33929             }
33930         }
33931     },
33932     
33933     afterSlide : function(){
33934         if(Roo.isGecko){// firefox overflow auto bug workaround
33935             this.bodyEl.unclip();
33936             if(this.tabs) {
33937                 this.tabs.bodyEl.unclip();
33938             }
33939             if(this.activePanel){
33940                 this.activePanel.getEl().unclip();
33941                 if(this.activePanel.afterSlide){
33942                     this.activePanel.afterSlide();
33943                 }
33944             }
33945         }
33946     },
33947
33948     initAutoHide : function(){
33949         if(this.autoHide !== false){
33950             if(!this.autoHideHd){
33951                 var st = new Roo.util.DelayedTask(this.slideIn, this);
33952                 this.autoHideHd = {
33953                     "mouseout": function(e){
33954                         if(!e.within(this.el, true)){
33955                             st.delay(500);
33956                         }
33957                     },
33958                     "mouseover" : function(e){
33959                         st.cancel();
33960                     },
33961                     scope : this
33962                 };
33963             }
33964             this.el.on(this.autoHideHd);
33965         }
33966     },
33967
33968     clearAutoHide : function(){
33969         if(this.autoHide !== false){
33970             this.el.un("mouseout", this.autoHideHd.mouseout);
33971             this.el.un("mouseover", this.autoHideHd.mouseover);
33972         }
33973     },
33974
33975     clearMonitor : function(){
33976         Roo.get(document).un("click", this.slideInIf, this);
33977     },
33978
33979     // these names are backwards but not changed for compat
33980     slideOut : function(){
33981         if(this.isSlid || this.el.hasActiveFx()){
33982             return;
33983         }
33984         this.isSlid = true;
33985         if(this.collapseBtn){
33986             this.collapseBtn.hide();
33987         }
33988         this.closeBtnState = this.closeBtn.getStyle('display');
33989         this.closeBtn.hide();
33990         if(this.stickBtn){
33991             this.stickBtn.show();
33992         }
33993         this.el.show();
33994         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
33995         this.beforeSlide();
33996         this.el.setStyle("z-index", 10001);
33997         this.el.slideIn(this.getSlideAnchor(), {
33998             callback: function(){
33999                 this.afterSlide();
34000                 this.initAutoHide();
34001                 Roo.get(document).on("click", this.slideInIf, this);
34002                 this.fireEvent("slideshow", this);
34003             },
34004             scope: this,
34005             block: true
34006         });
34007     },
34008
34009     afterSlideIn : function(){
34010         this.clearAutoHide();
34011         this.isSlid = false;
34012         this.clearMonitor();
34013         this.el.setStyle("z-index", "");
34014         if(this.collapseBtn){
34015             this.collapseBtn.show();
34016         }
34017         this.closeBtn.setStyle('display', this.closeBtnState);
34018         if(this.stickBtn){
34019             this.stickBtn.hide();
34020         }
34021         this.fireEvent("slidehide", this);
34022     },
34023
34024     slideIn : function(cb){
34025         if(!this.isSlid || this.el.hasActiveFx()){
34026             Roo.callback(cb);
34027             return;
34028         }
34029         this.isSlid = false;
34030         this.beforeSlide();
34031         this.el.slideOut(this.getSlideAnchor(), {
34032             callback: function(){
34033                 this.el.setLeftTop(-10000, -10000);
34034                 this.afterSlide();
34035                 this.afterSlideIn();
34036                 Roo.callback(cb);
34037             },
34038             scope: this,
34039             block: true
34040         });
34041     },
34042     
34043     slideInIf : function(e){
34044         if(!e.within(this.el)){
34045             this.slideIn();
34046         }
34047     },
34048
34049     animateCollapse : function(){
34050         this.beforeSlide();
34051         this.el.setStyle("z-index", 20000);
34052         var anchor = this.getSlideAnchor();
34053         this.el.slideOut(anchor, {
34054             callback : function(){
34055                 this.el.setStyle("z-index", "");
34056                 this.collapsedEl.slideIn(anchor, {duration:.3});
34057                 this.afterSlide();
34058                 this.el.setLocation(-10000,-10000);
34059                 this.el.hide();
34060                 this.fireEvent("collapsed", this);
34061             },
34062             scope: this,
34063             block: true
34064         });
34065     },
34066
34067     animateExpand : function(){
34068         this.beforeSlide();
34069         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34070         this.el.setStyle("z-index", 20000);
34071         this.collapsedEl.hide({
34072             duration:.1
34073         });
34074         this.el.slideIn(this.getSlideAnchor(), {
34075             callback : function(){
34076                 this.el.setStyle("z-index", "");
34077                 this.afterSlide();
34078                 if(this.split){
34079                     this.split.el.show();
34080                 }
34081                 this.fireEvent("invalidated", this);
34082                 this.fireEvent("expanded", this);
34083             },
34084             scope: this,
34085             block: true
34086         });
34087     },
34088
34089     anchors : {
34090         "west" : "left",
34091         "east" : "right",
34092         "north" : "top",
34093         "south" : "bottom"
34094     },
34095
34096     sanchors : {
34097         "west" : "l",
34098         "east" : "r",
34099         "north" : "t",
34100         "south" : "b"
34101     },
34102
34103     canchors : {
34104         "west" : "tl-tr",
34105         "east" : "tr-tl",
34106         "north" : "tl-bl",
34107         "south" : "bl-tl"
34108     },
34109
34110     getAnchor : function(){
34111         return this.anchors[this.position];
34112     },
34113
34114     getCollapseAnchor : function(){
34115         return this.canchors[this.position];
34116     },
34117
34118     getSlideAnchor : function(){
34119         return this.sanchors[this.position];
34120     },
34121
34122     getAlignAdj : function(){
34123         var cm = this.cmargins;
34124         switch(this.position){
34125             case "west":
34126                 return [0, 0];
34127             break;
34128             case "east":
34129                 return [0, 0];
34130             break;
34131             case "north":
34132                 return [0, 0];
34133             break;
34134             case "south":
34135                 return [0, 0];
34136             break;
34137         }
34138     },
34139
34140     getExpandAdj : function(){
34141         var c = this.collapsedEl, cm = this.cmargins;
34142         switch(this.position){
34143             case "west":
34144                 return [-(cm.right+c.getWidth()+cm.left), 0];
34145             break;
34146             case "east":
34147                 return [cm.right+c.getWidth()+cm.left, 0];
34148             break;
34149             case "north":
34150                 return [0, -(cm.top+cm.bottom+c.getHeight())];
34151             break;
34152             case "south":
34153                 return [0, cm.top+cm.bottom+c.getHeight()];
34154             break;
34155         }
34156     }
34157 });/*
34158  * Based on:
34159  * Ext JS Library 1.1.1
34160  * Copyright(c) 2006-2007, Ext JS, LLC.
34161  *
34162  * Originally Released Under LGPL - original licence link has changed is not relivant.
34163  *
34164  * Fork - LGPL
34165  * <script type="text/javascript">
34166  */
34167 /*
34168  * These classes are private internal classes
34169  */
34170 Roo.bootstrap.layout.Center = function(config){
34171     config.region = "center";
34172     Roo.bootstrap.layout.Region.call(this, config);
34173     this.visible = true;
34174     this.minWidth = config.minWidth || 20;
34175     this.minHeight = config.minHeight || 20;
34176 };
34177
34178 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
34179     hide : function(){
34180         // center panel can't be hidden
34181     },
34182     
34183     show : function(){
34184         // center panel can't be hidden
34185     },
34186     
34187     getMinWidth: function(){
34188         return this.minWidth;
34189     },
34190     
34191     getMinHeight: function(){
34192         return this.minHeight;
34193     }
34194 });
34195
34196
34197
34198
34199  
34200
34201
34202
34203
34204
34205 Roo.bootstrap.layout.North = function(config)
34206 {
34207     config.region = 'north';
34208     config.cursor = 'n-resize';
34209     
34210     Roo.bootstrap.layout.Split.call(this, config);
34211     
34212     
34213     if(this.split){
34214         this.split.placement = Roo.bootstrap.SplitBar.TOP;
34215         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
34216         this.split.el.addClass("roo-layout-split-v");
34217     }
34218     var size = config.initialSize || config.height;
34219     if(typeof size != "undefined"){
34220         this.el.setHeight(size);
34221     }
34222 };
34223 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
34224 {
34225     orientation: Roo.bootstrap.SplitBar.VERTICAL,
34226     
34227     
34228     
34229     getBox : function(){
34230         if(this.collapsed){
34231             return this.collapsedEl.getBox();
34232         }
34233         var box = this.el.getBox();
34234         if(this.split){
34235             box.height += this.split.el.getHeight();
34236         }
34237         return box;
34238     },
34239     
34240     updateBox : function(box){
34241         if(this.split && !this.collapsed){
34242             box.height -= this.split.el.getHeight();
34243             this.split.el.setLeft(box.x);
34244             this.split.el.setTop(box.y+box.height);
34245             this.split.el.setWidth(box.width);
34246         }
34247         if(this.collapsed){
34248             this.updateBody(box.width, null);
34249         }
34250         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34251     }
34252 });
34253
34254
34255
34256
34257
34258 Roo.bootstrap.layout.South = function(config){
34259     config.region = 'south';
34260     config.cursor = 's-resize';
34261     Roo.bootstrap.layout.Split.call(this, config);
34262     if(this.split){
34263         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
34264         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
34265         this.split.el.addClass("roo-layout-split-v");
34266     }
34267     var size = config.initialSize || config.height;
34268     if(typeof size != "undefined"){
34269         this.el.setHeight(size);
34270     }
34271 };
34272
34273 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
34274     orientation: Roo.bootstrap.SplitBar.VERTICAL,
34275     getBox : function(){
34276         if(this.collapsed){
34277             return this.collapsedEl.getBox();
34278         }
34279         var box = this.el.getBox();
34280         if(this.split){
34281             var sh = this.split.el.getHeight();
34282             box.height += sh;
34283             box.y -= sh;
34284         }
34285         return box;
34286     },
34287     
34288     updateBox : function(box){
34289         if(this.split && !this.collapsed){
34290             var sh = this.split.el.getHeight();
34291             box.height -= sh;
34292             box.y += sh;
34293             this.split.el.setLeft(box.x);
34294             this.split.el.setTop(box.y-sh);
34295             this.split.el.setWidth(box.width);
34296         }
34297         if(this.collapsed){
34298             this.updateBody(box.width, null);
34299         }
34300         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34301     }
34302 });
34303
34304 Roo.bootstrap.layout.East = function(config){
34305     config.region = "east";
34306     config.cursor = "e-resize";
34307     Roo.bootstrap.layout.Split.call(this, config);
34308     if(this.split){
34309         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
34310         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
34311         this.split.el.addClass("roo-layout-split-h");
34312     }
34313     var size = config.initialSize || config.width;
34314     if(typeof size != "undefined"){
34315         this.el.setWidth(size);
34316     }
34317 };
34318 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
34319     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
34320     getBox : function(){
34321         if(this.collapsed){
34322             return this.collapsedEl.getBox();
34323         }
34324         var box = this.el.getBox();
34325         if(this.split){
34326             var sw = this.split.el.getWidth();
34327             box.width += sw;
34328             box.x -= sw;
34329         }
34330         return box;
34331     },
34332
34333     updateBox : function(box){
34334         if(this.split && !this.collapsed){
34335             var sw = this.split.el.getWidth();
34336             box.width -= sw;
34337             this.split.el.setLeft(box.x);
34338             this.split.el.setTop(box.y);
34339             this.split.el.setHeight(box.height);
34340             box.x += sw;
34341         }
34342         if(this.collapsed){
34343             this.updateBody(null, box.height);
34344         }
34345         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34346     }
34347 });
34348
34349 Roo.bootstrap.layout.West = function(config){
34350     config.region = "west";
34351     config.cursor = "w-resize";
34352     
34353     Roo.bootstrap.layout.Split.call(this, config);
34354     if(this.split){
34355         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
34356         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
34357         this.split.el.addClass("roo-layout-split-h");
34358     }
34359     
34360 };
34361 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
34362     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
34363     
34364     onRender: function(ctr, pos)
34365     {
34366         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
34367         var size = this.config.initialSize || this.config.width;
34368         if(typeof size != "undefined"){
34369             this.el.setWidth(size);
34370         }
34371     },
34372     
34373     getBox : function(){
34374         if(this.collapsed){
34375             return this.collapsedEl.getBox();
34376         }
34377         var box = this.el.getBox();
34378         if(this.split){
34379             box.width += this.split.el.getWidth();
34380         }
34381         return box;
34382     },
34383     
34384     updateBox : function(box){
34385         if(this.split && !this.collapsed){
34386             var sw = this.split.el.getWidth();
34387             box.width -= sw;
34388             this.split.el.setLeft(box.x+box.width);
34389             this.split.el.setTop(box.y);
34390             this.split.el.setHeight(box.height);
34391         }
34392         if(this.collapsed){
34393             this.updateBody(null, box.height);
34394         }
34395         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34396     }
34397 });
34398 Roo.namespace("Roo.bootstrap.panel");/*
34399  * Based on:
34400  * Ext JS Library 1.1.1
34401  * Copyright(c) 2006-2007, Ext JS, LLC.
34402  *
34403  * Originally Released Under LGPL - original licence link has changed is not relivant.
34404  *
34405  * Fork - LGPL
34406  * <script type="text/javascript">
34407  */
34408 /**
34409  * @class Roo.ContentPanel
34410  * @extends Roo.util.Observable
34411  * A basic ContentPanel element.
34412  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
34413  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
34414  * @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
34415  * @cfg {Boolean}   closable      True if the panel can be closed/removed
34416  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
34417  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
34418  * @cfg {Toolbar}   toolbar       A toolbar for this panel
34419  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
34420  * @cfg {String} title          The title for this panel
34421  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
34422  * @cfg {String} url            Calls {@link #setUrl} with this value
34423  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
34424  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
34425  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
34426  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
34427  * @cfg {Boolean} badges render the badges
34428
34429  * @constructor
34430  * Create a new ContentPanel.
34431  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
34432  * @param {String/Object} config A string to set only the title or a config object
34433  * @param {String} content (optional) Set the HTML content for this panel
34434  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
34435  */
34436 Roo.bootstrap.panel.Content = function( config){
34437     
34438     this.tpl = config.tpl || false;
34439     
34440     var el = config.el;
34441     var content = config.content;
34442
34443     if(config.autoCreate){ // xtype is available if this is called from factory
34444         el = Roo.id();
34445     }
34446     this.el = Roo.get(el);
34447     if(!this.el && config && config.autoCreate){
34448         if(typeof config.autoCreate == "object"){
34449             if(!config.autoCreate.id){
34450                 config.autoCreate.id = config.id||el;
34451             }
34452             this.el = Roo.DomHelper.append(document.body,
34453                         config.autoCreate, true);
34454         }else{
34455             var elcfg =  {   tag: "div",
34456                             cls: "roo-layout-inactive-content",
34457                             id: config.id||el
34458                             };
34459             if (config.html) {
34460                 elcfg.html = config.html;
34461                 
34462             }
34463                         
34464             this.el = Roo.DomHelper.append(document.body, elcfg , true);
34465         }
34466     } 
34467     this.closable = false;
34468     this.loaded = false;
34469     this.active = false;
34470    
34471       
34472     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
34473         
34474         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
34475         
34476         this.wrapEl = this.el; //this.el.wrap();
34477         var ti = [];
34478         if (config.toolbar.items) {
34479             ti = config.toolbar.items ;
34480             delete config.toolbar.items ;
34481         }
34482         
34483         var nitems = [];
34484         this.toolbar.render(this.wrapEl, 'before');
34485         for(var i =0;i < ti.length;i++) {
34486           //  Roo.log(['add child', items[i]]);
34487             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34488         }
34489         this.toolbar.items = nitems;
34490         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
34491         delete config.toolbar;
34492         
34493     }
34494     /*
34495     // xtype created footer. - not sure if will work as we normally have to render first..
34496     if (this.footer && !this.footer.el && this.footer.xtype) {
34497         if (!this.wrapEl) {
34498             this.wrapEl = this.el.wrap();
34499         }
34500     
34501         this.footer.container = this.wrapEl.createChild();
34502          
34503         this.footer = Roo.factory(this.footer, Roo);
34504         
34505     }
34506     */
34507     
34508      if(typeof config == "string"){
34509         this.title = config;
34510     }else{
34511         Roo.apply(this, config);
34512     }
34513     
34514     if(this.resizeEl){
34515         this.resizeEl = Roo.get(this.resizeEl, true);
34516     }else{
34517         this.resizeEl = this.el;
34518     }
34519     // handle view.xtype
34520     
34521  
34522     
34523     
34524     this.addEvents({
34525         /**
34526          * @event activate
34527          * Fires when this panel is activated. 
34528          * @param {Roo.ContentPanel} this
34529          */
34530         "activate" : true,
34531         /**
34532          * @event deactivate
34533          * Fires when this panel is activated. 
34534          * @param {Roo.ContentPanel} this
34535          */
34536         "deactivate" : true,
34537
34538         /**
34539          * @event resize
34540          * Fires when this panel is resized if fitToFrame is true.
34541          * @param {Roo.ContentPanel} this
34542          * @param {Number} width The width after any component adjustments
34543          * @param {Number} height The height after any component adjustments
34544          */
34545         "resize" : true,
34546         
34547          /**
34548          * @event render
34549          * Fires when this tab is created
34550          * @param {Roo.ContentPanel} this
34551          */
34552         "render" : true
34553         
34554         
34555         
34556     });
34557     
34558
34559     
34560     
34561     if(this.autoScroll){
34562         this.resizeEl.setStyle("overflow", "auto");
34563     } else {
34564         // fix randome scrolling
34565         //this.el.on('scroll', function() {
34566         //    Roo.log('fix random scolling');
34567         //    this.scrollTo('top',0); 
34568         //});
34569     }
34570     content = content || this.content;
34571     if(content){
34572         this.setContent(content);
34573     }
34574     if(config && config.url){
34575         this.setUrl(this.url, this.params, this.loadOnce);
34576     }
34577     
34578     
34579     
34580     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
34581     
34582     if (this.view && typeof(this.view.xtype) != 'undefined') {
34583         this.view.el = this.el.appendChild(document.createElement("div"));
34584         this.view = Roo.factory(this.view); 
34585         this.view.render  &&  this.view.render(false, '');  
34586     }
34587     
34588     
34589     this.fireEvent('render', this);
34590 };
34591
34592 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
34593     
34594     tabTip : '',
34595     
34596     setRegion : function(region){
34597         this.region = region;
34598         this.setActiveClass(region && !this.background);
34599     },
34600     
34601     
34602     setActiveClass: function(state)
34603     {
34604         if(state){
34605            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
34606            this.el.setStyle('position','relative');
34607         }else{
34608            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
34609            this.el.setStyle('position', 'absolute');
34610         } 
34611     },
34612     
34613     /**
34614      * Returns the toolbar for this Panel if one was configured. 
34615      * @return {Roo.Toolbar} 
34616      */
34617     getToolbar : function(){
34618         return this.toolbar;
34619     },
34620     
34621     setActiveState : function(active)
34622     {
34623         this.active = active;
34624         this.setActiveClass(active);
34625         if(!active){
34626             this.fireEvent("deactivate", this);
34627         }else{
34628             this.fireEvent("activate", this);
34629         }
34630     },
34631     /**
34632      * Updates this panel's element
34633      * @param {String} content The new content
34634      * @param {Boolean} loadScripts (optional) true to look for and process scripts
34635     */
34636     setContent : function(content, loadScripts){
34637         this.el.update(content, loadScripts);
34638     },
34639
34640     ignoreResize : function(w, h){
34641         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
34642             return true;
34643         }else{
34644             this.lastSize = {width: w, height: h};
34645             return false;
34646         }
34647     },
34648     /**
34649      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
34650      * @return {Roo.UpdateManager} The UpdateManager
34651      */
34652     getUpdateManager : function(){
34653         return this.el.getUpdateManager();
34654     },
34655      /**
34656      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
34657      * @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:
34658 <pre><code>
34659 panel.load({
34660     url: "your-url.php",
34661     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
34662     callback: yourFunction,
34663     scope: yourObject, //(optional scope)
34664     discardUrl: false,
34665     nocache: false,
34666     text: "Loading...",
34667     timeout: 30,
34668     scripts: false
34669 });
34670 </code></pre>
34671      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
34672      * 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.
34673      * @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}
34674      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
34675      * @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.
34676      * @return {Roo.ContentPanel} this
34677      */
34678     load : function(){
34679         var um = this.el.getUpdateManager();
34680         um.update.apply(um, arguments);
34681         return this;
34682     },
34683
34684
34685     /**
34686      * 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.
34687      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
34688      * @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)
34689      * @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)
34690      * @return {Roo.UpdateManager} The UpdateManager
34691      */
34692     setUrl : function(url, params, loadOnce){
34693         if(this.refreshDelegate){
34694             this.removeListener("activate", this.refreshDelegate);
34695         }
34696         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34697         this.on("activate", this.refreshDelegate);
34698         return this.el.getUpdateManager();
34699     },
34700     
34701     _handleRefresh : function(url, params, loadOnce){
34702         if(!loadOnce || !this.loaded){
34703             var updater = this.el.getUpdateManager();
34704             updater.update(url, params, this._setLoaded.createDelegate(this));
34705         }
34706     },
34707     
34708     _setLoaded : function(){
34709         this.loaded = true;
34710     }, 
34711     
34712     /**
34713      * Returns this panel's id
34714      * @return {String} 
34715      */
34716     getId : function(){
34717         return this.el.id;
34718     },
34719     
34720     /** 
34721      * Returns this panel's element - used by regiosn to add.
34722      * @return {Roo.Element} 
34723      */
34724     getEl : function(){
34725         return this.wrapEl || this.el;
34726     },
34727     
34728    
34729     
34730     adjustForComponents : function(width, height)
34731     {
34732         //Roo.log('adjustForComponents ');
34733         if(this.resizeEl != this.el){
34734             width -= this.el.getFrameWidth('lr');
34735             height -= this.el.getFrameWidth('tb');
34736         }
34737         if(this.toolbar){
34738             var te = this.toolbar.getEl();
34739             height -= te.getHeight();
34740             te.setWidth(width);
34741         }
34742         if(this.footer){
34743             var te = this.footer.getEl();
34744             Roo.log("footer:" + te.getHeight());
34745             
34746             height -= te.getHeight();
34747             te.setWidth(width);
34748         }
34749         
34750         
34751         if(this.adjustments){
34752             width += this.adjustments[0];
34753             height += this.adjustments[1];
34754         }
34755         return {"width": width, "height": height};
34756     },
34757     
34758     setSize : function(width, height){
34759         if(this.fitToFrame && !this.ignoreResize(width, height)){
34760             if(this.fitContainer && this.resizeEl != this.el){
34761                 this.el.setSize(width, height);
34762             }
34763             var size = this.adjustForComponents(width, height);
34764             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
34765             this.fireEvent('resize', this, size.width, size.height);
34766         }
34767     },
34768     
34769     /**
34770      * Returns this panel's title
34771      * @return {String} 
34772      */
34773     getTitle : function(){
34774         return this.title;
34775     },
34776     
34777     /**
34778      * Set this panel's title
34779      * @param {String} title
34780      */
34781     setTitle : function(title){
34782         this.title = title;
34783         if(this.region){
34784             this.region.updatePanelTitle(this, title);
34785         }
34786     },
34787     
34788     /**
34789      * Returns true is this panel was configured to be closable
34790      * @return {Boolean} 
34791      */
34792     isClosable : function(){
34793         return this.closable;
34794     },
34795     
34796     beforeSlide : function(){
34797         this.el.clip();
34798         this.resizeEl.clip();
34799     },
34800     
34801     afterSlide : function(){
34802         this.el.unclip();
34803         this.resizeEl.unclip();
34804     },
34805     
34806     /**
34807      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
34808      *   Will fail silently if the {@link #setUrl} method has not been called.
34809      *   This does not activate the panel, just updates its content.
34810      */
34811     refresh : function(){
34812         if(this.refreshDelegate){
34813            this.loaded = false;
34814            this.refreshDelegate();
34815         }
34816     },
34817     
34818     /**
34819      * Destroys this panel
34820      */
34821     destroy : function(){
34822         this.el.removeAllListeners();
34823         var tempEl = document.createElement("span");
34824         tempEl.appendChild(this.el.dom);
34825         tempEl.innerHTML = "";
34826         this.el.remove();
34827         this.el = null;
34828     },
34829     
34830     /**
34831      * form - if the content panel contains a form - this is a reference to it.
34832      * @type {Roo.form.Form}
34833      */
34834     form : false,
34835     /**
34836      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
34837      *    This contains a reference to it.
34838      * @type {Roo.View}
34839      */
34840     view : false,
34841     
34842       /**
34843      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
34844      * <pre><code>
34845
34846 layout.addxtype({
34847        xtype : 'Form',
34848        items: [ .... ]
34849    }
34850 );
34851
34852 </code></pre>
34853      * @param {Object} cfg Xtype definition of item to add.
34854      */
34855     
34856     
34857     getChildContainer: function () {
34858         return this.getEl();
34859     }
34860     
34861     
34862     /*
34863         var  ret = new Roo.factory(cfg);
34864         return ret;
34865         
34866         
34867         // add form..
34868         if (cfg.xtype.match(/^Form$/)) {
34869             
34870             var el;
34871             //if (this.footer) {
34872             //    el = this.footer.container.insertSibling(false, 'before');
34873             //} else {
34874                 el = this.el.createChild();
34875             //}
34876
34877             this.form = new  Roo.form.Form(cfg);
34878             
34879             
34880             if ( this.form.allItems.length) {
34881                 this.form.render(el.dom);
34882             }
34883             return this.form;
34884         }
34885         // should only have one of theses..
34886         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
34887             // views.. should not be just added - used named prop 'view''
34888             
34889             cfg.el = this.el.appendChild(document.createElement("div"));
34890             // factory?
34891             
34892             var ret = new Roo.factory(cfg);
34893              
34894              ret.render && ret.render(false, ''); // render blank..
34895             this.view = ret;
34896             return ret;
34897         }
34898         return false;
34899     }
34900     \*/
34901 });
34902  
34903 /**
34904  * @class Roo.bootstrap.panel.Grid
34905  * @extends Roo.bootstrap.panel.Content
34906  * @constructor
34907  * Create a new GridPanel.
34908  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
34909  * @param {Object} config A the config object
34910   
34911  */
34912
34913
34914
34915 Roo.bootstrap.panel.Grid = function(config)
34916 {
34917     
34918       
34919     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
34920         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
34921
34922     config.el = this.wrapper;
34923     //this.el = this.wrapper;
34924     
34925       if (config.container) {
34926         // ctor'ed from a Border/panel.grid
34927         
34928         
34929         this.wrapper.setStyle("overflow", "hidden");
34930         this.wrapper.addClass('roo-grid-container');
34931
34932     }
34933     
34934     
34935     if(config.toolbar){
34936         var tool_el = this.wrapper.createChild();    
34937         this.toolbar = Roo.factory(config.toolbar);
34938         var ti = [];
34939         if (config.toolbar.items) {
34940             ti = config.toolbar.items ;
34941             delete config.toolbar.items ;
34942         }
34943         
34944         var nitems = [];
34945         this.toolbar.render(tool_el);
34946         for(var i =0;i < ti.length;i++) {
34947           //  Roo.log(['add child', items[i]]);
34948             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34949         }
34950         this.toolbar.items = nitems;
34951         
34952         delete config.toolbar;
34953     }
34954     
34955     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
34956     config.grid.scrollBody = true;;
34957     config.grid.monitorWindowResize = false; // turn off autosizing
34958     config.grid.autoHeight = false;
34959     config.grid.autoWidth = false;
34960     
34961     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
34962     
34963     if (config.background) {
34964         // render grid on panel activation (if panel background)
34965         this.on('activate', function(gp) {
34966             if (!gp.grid.rendered) {
34967                 gp.grid.render(this.wrapper);
34968                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
34969             }
34970         });
34971             
34972     } else {
34973         this.grid.render(this.wrapper);
34974         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
34975
34976     }
34977     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
34978     // ??? needed ??? config.el = this.wrapper;
34979     
34980     
34981     
34982   
34983     // xtype created footer. - not sure if will work as we normally have to render first..
34984     if (this.footer && !this.footer.el && this.footer.xtype) {
34985         
34986         var ctr = this.grid.getView().getFooterPanel(true);
34987         this.footer.dataSource = this.grid.dataSource;
34988         this.footer = Roo.factory(this.footer, Roo);
34989         this.footer.render(ctr);
34990         
34991     }
34992     
34993     
34994     
34995     
34996      
34997 };
34998
34999 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
35000     getId : function(){
35001         return this.grid.id;
35002     },
35003     
35004     /**
35005      * Returns the grid for this panel
35006      * @return {Roo.bootstrap.Table} 
35007      */
35008     getGrid : function(){
35009         return this.grid;    
35010     },
35011     
35012     setSize : function(width, height){
35013         if(!this.ignoreResize(width, height)){
35014             var grid = this.grid;
35015             var size = this.adjustForComponents(width, height);
35016             var gridel = grid.getGridEl();
35017             gridel.setSize(size.width, size.height);
35018             /*
35019             var thd = grid.getGridEl().select('thead',true).first();
35020             var tbd = grid.getGridEl().select('tbody', true).first();
35021             if (tbd) {
35022                 tbd.setSize(width, height - thd.getHeight());
35023             }
35024             */
35025             grid.autoSize();
35026         }
35027     },
35028      
35029     
35030     
35031     beforeSlide : function(){
35032         this.grid.getView().scroller.clip();
35033     },
35034     
35035     afterSlide : function(){
35036         this.grid.getView().scroller.unclip();
35037     },
35038     
35039     destroy : function(){
35040         this.grid.destroy();
35041         delete this.grid;
35042         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
35043     }
35044 });
35045
35046 /**
35047  * @class Roo.bootstrap.panel.Nest
35048  * @extends Roo.bootstrap.panel.Content
35049  * @constructor
35050  * Create a new Panel, that can contain a layout.Border.
35051  * 
35052  * 
35053  * @param {Roo.BorderLayout} layout The layout for this panel
35054  * @param {String/Object} config A string to set only the title or a config object
35055  */
35056 Roo.bootstrap.panel.Nest = function(config)
35057 {
35058     // construct with only one argument..
35059     /* FIXME - implement nicer consturctors
35060     if (layout.layout) {
35061         config = layout;
35062         layout = config.layout;
35063         delete config.layout;
35064     }
35065     if (layout.xtype && !layout.getEl) {
35066         // then layout needs constructing..
35067         layout = Roo.factory(layout, Roo);
35068     }
35069     */
35070     
35071     config.el =  config.layout.getEl();
35072     
35073     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
35074     
35075     config.layout.monitorWindowResize = false; // turn off autosizing
35076     this.layout = config.layout;
35077     this.layout.getEl().addClass("roo-layout-nested-layout");
35078     
35079     
35080     
35081     
35082 };
35083
35084 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
35085
35086     setSize : function(width, height){
35087         if(!this.ignoreResize(width, height)){
35088             var size = this.adjustForComponents(width, height);
35089             var el = this.layout.getEl();
35090             if (size.height < 1) {
35091                 el.setWidth(size.width);   
35092             } else {
35093                 el.setSize(size.width, size.height);
35094             }
35095             var touch = el.dom.offsetWidth;
35096             this.layout.layout();
35097             // ie requires a double layout on the first pass
35098             if(Roo.isIE && !this.initialized){
35099                 this.initialized = true;
35100                 this.layout.layout();
35101             }
35102         }
35103     },
35104     
35105     // activate all subpanels if not currently active..
35106     
35107     setActiveState : function(active){
35108         this.active = active;
35109         this.setActiveClass(active);
35110         
35111         if(!active){
35112             this.fireEvent("deactivate", this);
35113             return;
35114         }
35115         
35116         this.fireEvent("activate", this);
35117         // not sure if this should happen before or after..
35118         if (!this.layout) {
35119             return; // should not happen..
35120         }
35121         var reg = false;
35122         for (var r in this.layout.regions) {
35123             reg = this.layout.getRegion(r);
35124             if (reg.getActivePanel()) {
35125                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
35126                 reg.setActivePanel(reg.getActivePanel());
35127                 continue;
35128             }
35129             if (!reg.panels.length) {
35130                 continue;
35131             }
35132             reg.showPanel(reg.getPanel(0));
35133         }
35134         
35135         
35136         
35137         
35138     },
35139     
35140     /**
35141      * Returns the nested BorderLayout for this panel
35142      * @return {Roo.BorderLayout} 
35143      */
35144     getLayout : function(){
35145         return this.layout;
35146     },
35147     
35148      /**
35149      * Adds a xtype elements to the layout of the nested panel
35150      * <pre><code>
35151
35152 panel.addxtype({
35153        xtype : 'ContentPanel',
35154        region: 'west',
35155        items: [ .... ]
35156    }
35157 );
35158
35159 panel.addxtype({
35160         xtype : 'NestedLayoutPanel',
35161         region: 'west',
35162         layout: {
35163            center: { },
35164            west: { }   
35165         },
35166         items : [ ... list of content panels or nested layout panels.. ]
35167    }
35168 );
35169 </code></pre>
35170      * @param {Object} cfg Xtype definition of item to add.
35171      */
35172     addxtype : function(cfg) {
35173         return this.layout.addxtype(cfg);
35174     
35175     }
35176 });        /*
35177  * Based on:
35178  * Ext JS Library 1.1.1
35179  * Copyright(c) 2006-2007, Ext JS, LLC.
35180  *
35181  * Originally Released Under LGPL - original licence link has changed is not relivant.
35182  *
35183  * Fork - LGPL
35184  * <script type="text/javascript">
35185  */
35186 /**
35187  * @class Roo.TabPanel
35188  * @extends Roo.util.Observable
35189  * A lightweight tab container.
35190  * <br><br>
35191  * Usage:
35192  * <pre><code>
35193 // basic tabs 1, built from existing content
35194 var tabs = new Roo.TabPanel("tabs1");
35195 tabs.addTab("script", "View Script");
35196 tabs.addTab("markup", "View Markup");
35197 tabs.activate("script");
35198
35199 // more advanced tabs, built from javascript
35200 var jtabs = new Roo.TabPanel("jtabs");
35201 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
35202
35203 // set up the UpdateManager
35204 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
35205 var updater = tab2.getUpdateManager();
35206 updater.setDefaultUrl("ajax1.htm");
35207 tab2.on('activate', updater.refresh, updater, true);
35208
35209 // Use setUrl for Ajax loading
35210 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
35211 tab3.setUrl("ajax2.htm", null, true);
35212
35213 // Disabled tab
35214 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
35215 tab4.disable();
35216
35217 jtabs.activate("jtabs-1");
35218  * </code></pre>
35219  * @constructor
35220  * Create a new TabPanel.
35221  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
35222  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
35223  */
35224 Roo.bootstrap.panel.Tabs = function(config){
35225     /**
35226     * The container element for this TabPanel.
35227     * @type Roo.Element
35228     */
35229     this.el = Roo.get(config.el);
35230     delete config.el;
35231     if(config){
35232         if(typeof config == "boolean"){
35233             this.tabPosition = config ? "bottom" : "top";
35234         }else{
35235             Roo.apply(this, config);
35236         }
35237     }
35238     
35239     if(this.tabPosition == "bottom"){
35240         this.bodyEl = Roo.get(this.createBody(this.el.dom));
35241         this.el.addClass("roo-tabs-bottom");
35242     }
35243     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
35244     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
35245     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
35246     if(Roo.isIE){
35247         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
35248     }
35249     if(this.tabPosition != "bottom"){
35250         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
35251          * @type Roo.Element
35252          */
35253         this.bodyEl = Roo.get(this.createBody(this.el.dom));
35254         this.el.addClass("roo-tabs-top");
35255     }
35256     this.items = [];
35257
35258     this.bodyEl.setStyle("position", "relative");
35259
35260     this.active = null;
35261     this.activateDelegate = this.activate.createDelegate(this);
35262
35263     this.addEvents({
35264         /**
35265          * @event tabchange
35266          * Fires when the active tab changes
35267          * @param {Roo.TabPanel} this
35268          * @param {Roo.TabPanelItem} activePanel The new active tab
35269          */
35270         "tabchange": true,
35271         /**
35272          * @event beforetabchange
35273          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
35274          * @param {Roo.TabPanel} this
35275          * @param {Object} e Set cancel to true on this object to cancel the tab change
35276          * @param {Roo.TabPanelItem} tab The tab being changed to
35277          */
35278         "beforetabchange" : true
35279     });
35280
35281     Roo.EventManager.onWindowResize(this.onResize, this);
35282     this.cpad = this.el.getPadding("lr");
35283     this.hiddenCount = 0;
35284
35285
35286     // toolbar on the tabbar support...
35287     if (this.toolbar) {
35288         alert("no toolbar support yet");
35289         this.toolbar  = false;
35290         /*
35291         var tcfg = this.toolbar;
35292         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
35293         this.toolbar = new Roo.Toolbar(tcfg);
35294         if (Roo.isSafari) {
35295             var tbl = tcfg.container.child('table', true);
35296             tbl.setAttribute('width', '100%');
35297         }
35298         */
35299         
35300     }
35301    
35302
35303
35304     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
35305 };
35306
35307 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
35308     /*
35309      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
35310      */
35311     tabPosition : "top",
35312     /*
35313      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
35314      */
35315     currentTabWidth : 0,
35316     /*
35317      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
35318      */
35319     minTabWidth : 40,
35320     /*
35321      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
35322      */
35323     maxTabWidth : 250,
35324     /*
35325      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
35326      */
35327     preferredTabWidth : 175,
35328     /*
35329      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
35330      */
35331     resizeTabs : false,
35332     /*
35333      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
35334      */
35335     monitorResize : true,
35336     /*
35337      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
35338      */
35339     toolbar : false,
35340
35341     /**
35342      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
35343      * @param {String} id The id of the div to use <b>or create</b>
35344      * @param {String} text The text for the tab
35345      * @param {String} content (optional) Content to put in the TabPanelItem body
35346      * @param {Boolean} closable (optional) True to create a close icon on the tab
35347      * @return {Roo.TabPanelItem} The created TabPanelItem
35348      */
35349     addTab : function(id, text, content, closable, tpl)
35350     {
35351         var item = new Roo.bootstrap.panel.TabItem({
35352             panel: this,
35353             id : id,
35354             text : text,
35355             closable : closable,
35356             tpl : tpl
35357         });
35358         this.addTabItem(item);
35359         if(content){
35360             item.setContent(content);
35361         }
35362         return item;
35363     },
35364
35365     /**
35366      * Returns the {@link Roo.TabPanelItem} with the specified id/index
35367      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
35368      * @return {Roo.TabPanelItem}
35369      */
35370     getTab : function(id){
35371         return this.items[id];
35372     },
35373
35374     /**
35375      * Hides the {@link Roo.TabPanelItem} with the specified id/index
35376      * @param {String/Number} id The id or index of the TabPanelItem to hide.
35377      */
35378     hideTab : function(id){
35379         var t = this.items[id];
35380         if(!t.isHidden()){
35381            t.setHidden(true);
35382            this.hiddenCount++;
35383            this.autoSizeTabs();
35384         }
35385     },
35386
35387     /**
35388      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
35389      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
35390      */
35391     unhideTab : function(id){
35392         var t = this.items[id];
35393         if(t.isHidden()){
35394            t.setHidden(false);
35395            this.hiddenCount--;
35396            this.autoSizeTabs();
35397         }
35398     },
35399
35400     /**
35401      * Adds an existing {@link Roo.TabPanelItem}.
35402      * @param {Roo.TabPanelItem} item The TabPanelItem to add
35403      */
35404     addTabItem : function(item){
35405         this.items[item.id] = item;
35406         this.items.push(item);
35407       //  if(this.resizeTabs){
35408     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
35409   //         this.autoSizeTabs();
35410 //        }else{
35411 //            item.autoSize();
35412        // }
35413     },
35414
35415     /**
35416      * Removes a {@link Roo.TabPanelItem}.
35417      * @param {String/Number} id The id or index of the TabPanelItem to remove.
35418      */
35419     removeTab : function(id){
35420         var items = this.items;
35421         var tab = items[id];
35422         if(!tab) { return; }
35423         var index = items.indexOf(tab);
35424         if(this.active == tab && items.length > 1){
35425             var newTab = this.getNextAvailable(index);
35426             if(newTab) {
35427                 newTab.activate();
35428             }
35429         }
35430         this.stripEl.dom.removeChild(tab.pnode.dom);
35431         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
35432             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
35433         }
35434         items.splice(index, 1);
35435         delete this.items[tab.id];
35436         tab.fireEvent("close", tab);
35437         tab.purgeListeners();
35438         this.autoSizeTabs();
35439     },
35440
35441     getNextAvailable : function(start){
35442         var items = this.items;
35443         var index = start;
35444         // look for a next tab that will slide over to
35445         // replace the one being removed
35446         while(index < items.length){
35447             var item = items[++index];
35448             if(item && !item.isHidden()){
35449                 return item;
35450             }
35451         }
35452         // if one isn't found select the previous tab (on the left)
35453         index = start;
35454         while(index >= 0){
35455             var item = items[--index];
35456             if(item && !item.isHidden()){
35457                 return item;
35458             }
35459         }
35460         return null;
35461     },
35462
35463     /**
35464      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
35465      * @param {String/Number} id The id or index of the TabPanelItem to disable.
35466      */
35467     disableTab : function(id){
35468         var tab = this.items[id];
35469         if(tab && this.active != tab){
35470             tab.disable();
35471         }
35472     },
35473
35474     /**
35475      * Enables a {@link Roo.TabPanelItem} that is disabled.
35476      * @param {String/Number} id The id or index of the TabPanelItem to enable.
35477      */
35478     enableTab : function(id){
35479         var tab = this.items[id];
35480         tab.enable();
35481     },
35482
35483     /**
35484      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
35485      * @param {String/Number} id The id or index of the TabPanelItem to activate.
35486      * @return {Roo.TabPanelItem} The TabPanelItem.
35487      */
35488     activate : function(id){
35489         var tab = this.items[id];
35490         if(!tab){
35491             return null;
35492         }
35493         if(tab == this.active || tab.disabled){
35494             return tab;
35495         }
35496         var e = {};
35497         this.fireEvent("beforetabchange", this, e, tab);
35498         if(e.cancel !== true && !tab.disabled){
35499             if(this.active){
35500                 this.active.hide();
35501             }
35502             this.active = this.items[id];
35503             this.active.show();
35504             this.fireEvent("tabchange", this, this.active);
35505         }
35506         return tab;
35507     },
35508
35509     /**
35510      * Gets the active {@link Roo.TabPanelItem}.
35511      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
35512      */
35513     getActiveTab : function(){
35514         return this.active;
35515     },
35516
35517     /**
35518      * Updates the tab body element to fit the height of the container element
35519      * for overflow scrolling
35520      * @param {Number} targetHeight (optional) Override the starting height from the elements height
35521      */
35522     syncHeight : function(targetHeight){
35523         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35524         var bm = this.bodyEl.getMargins();
35525         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
35526         this.bodyEl.setHeight(newHeight);
35527         return newHeight;
35528     },
35529
35530     onResize : function(){
35531         if(this.monitorResize){
35532             this.autoSizeTabs();
35533         }
35534     },
35535
35536     /**
35537      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
35538      */
35539     beginUpdate : function(){
35540         this.updating = true;
35541     },
35542
35543     /**
35544      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
35545      */
35546     endUpdate : function(){
35547         this.updating = false;
35548         this.autoSizeTabs();
35549     },
35550
35551     /**
35552      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
35553      */
35554     autoSizeTabs : function(){
35555         var count = this.items.length;
35556         var vcount = count - this.hiddenCount;
35557         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
35558             return;
35559         }
35560         var w = Math.max(this.el.getWidth() - this.cpad, 10);
35561         var availWidth = Math.floor(w / vcount);
35562         var b = this.stripBody;
35563         if(b.getWidth() > w){
35564             var tabs = this.items;
35565             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
35566             if(availWidth < this.minTabWidth){
35567                 /*if(!this.sleft){    // incomplete scrolling code
35568                     this.createScrollButtons();
35569                 }
35570                 this.showScroll();
35571                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
35572             }
35573         }else{
35574             if(this.currentTabWidth < this.preferredTabWidth){
35575                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
35576             }
35577         }
35578     },
35579
35580     /**
35581      * Returns the number of tabs in this TabPanel.
35582      * @return {Number}
35583      */
35584      getCount : function(){
35585          return this.items.length;
35586      },
35587
35588     /**
35589      * Resizes all the tabs to the passed width
35590      * @param {Number} The new width
35591      */
35592     setTabWidth : function(width){
35593         this.currentTabWidth = width;
35594         for(var i = 0, len = this.items.length; i < len; i++) {
35595                 if(!this.items[i].isHidden()) {
35596                 this.items[i].setWidth(width);
35597             }
35598         }
35599     },
35600
35601     /**
35602      * Destroys this TabPanel
35603      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
35604      */
35605     destroy : function(removeEl){
35606         Roo.EventManager.removeResizeListener(this.onResize, this);
35607         for(var i = 0, len = this.items.length; i < len; i++){
35608             this.items[i].purgeListeners();
35609         }
35610         if(removeEl === true){
35611             this.el.update("");
35612             this.el.remove();
35613         }
35614     },
35615     
35616     createStrip : function(container)
35617     {
35618         var strip = document.createElement("nav");
35619         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
35620         container.appendChild(strip);
35621         return strip;
35622     },
35623     
35624     createStripList : function(strip)
35625     {
35626         // div wrapper for retard IE
35627         // returns the "tr" element.
35628         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
35629         //'<div class="x-tabs-strip-wrap">'+
35630           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
35631           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
35632         return strip.firstChild; //.firstChild.firstChild.firstChild;
35633     },
35634     createBody : function(container)
35635     {
35636         var body = document.createElement("div");
35637         Roo.id(body, "tab-body");
35638         //Roo.fly(body).addClass("x-tabs-body");
35639         Roo.fly(body).addClass("tab-content");
35640         container.appendChild(body);
35641         return body;
35642     },
35643     createItemBody :function(bodyEl, id){
35644         var body = Roo.getDom(id);
35645         if(!body){
35646             body = document.createElement("div");
35647             body.id = id;
35648         }
35649         //Roo.fly(body).addClass("x-tabs-item-body");
35650         Roo.fly(body).addClass("tab-pane");
35651          bodyEl.insertBefore(body, bodyEl.firstChild);
35652         return body;
35653     },
35654     /** @private */
35655     createStripElements :  function(stripEl, text, closable, tpl)
35656     {
35657         var td = document.createElement("li"); // was td..
35658         
35659         
35660         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
35661         
35662         
35663         stripEl.appendChild(td);
35664         /*if(closable){
35665             td.className = "x-tabs-closable";
35666             if(!this.closeTpl){
35667                 this.closeTpl = 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>' +
35670                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
35671                 );
35672             }
35673             var el = this.closeTpl.overwrite(td, {"text": text});
35674             var close = el.getElementsByTagName("div")[0];
35675             var inner = el.getElementsByTagName("em")[0];
35676             return {"el": el, "close": close, "inner": inner};
35677         } else {
35678         */
35679         // not sure what this is..
35680 //            if(!this.tabTpl){
35681                 //this.tabTpl = new Roo.Template(
35682                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
35683                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
35684                 //);
35685 //                this.tabTpl = new Roo.Template(
35686 //                   '<a href="#">' +
35687 //                   '<span unselectable="on"' +
35688 //                            (this.disableTooltips ? '' : ' title="{text}"') +
35689 //                            ' >{text}</span></a>'
35690 //                );
35691 //                
35692 //            }
35693
35694
35695             var template = tpl || this.tabTpl || false;
35696             
35697             if(!template){
35698                 
35699                 template = new Roo.Template(
35700                    '<a href="#">' +
35701                    '<span unselectable="on"' +
35702                             (this.disableTooltips ? '' : ' title="{text}"') +
35703                             ' >{text}</span></a>'
35704                 );
35705             }
35706             
35707             switch (typeof(template)) {
35708                 case 'object' :
35709                     break;
35710                 case 'string' :
35711                     template = new Roo.Template(template);
35712                     break;
35713                 default :
35714                     break;
35715             }
35716             
35717             var el = template.overwrite(td, {"text": text});
35718             
35719             var inner = el.getElementsByTagName("span")[0];
35720             
35721             return {"el": el, "inner": inner};
35722             
35723     }
35724         
35725     
35726 });
35727
35728 /**
35729  * @class Roo.TabPanelItem
35730  * @extends Roo.util.Observable
35731  * Represents an individual item (tab plus body) in a TabPanel.
35732  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
35733  * @param {String} id The id of this TabPanelItem
35734  * @param {String} text The text for the tab of this TabPanelItem
35735  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
35736  */
35737 Roo.bootstrap.panel.TabItem = function(config){
35738     /**
35739      * The {@link Roo.TabPanel} this TabPanelItem belongs to
35740      * @type Roo.TabPanel
35741      */
35742     this.tabPanel = config.panel;
35743     /**
35744      * The id for this TabPanelItem
35745      * @type String
35746      */
35747     this.id = config.id;
35748     /** @private */
35749     this.disabled = false;
35750     /** @private */
35751     this.text = config.text;
35752     /** @private */
35753     this.loaded = false;
35754     this.closable = config.closable;
35755
35756     /**
35757      * The body element for this TabPanelItem.
35758      * @type Roo.Element
35759      */
35760     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
35761     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
35762     this.bodyEl.setStyle("display", "block");
35763     this.bodyEl.setStyle("zoom", "1");
35764     //this.hideAction();
35765
35766     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
35767     /** @private */
35768     this.el = Roo.get(els.el);
35769     this.inner = Roo.get(els.inner, true);
35770     this.textEl = Roo.get(this.el.dom.firstChild, true);
35771     this.pnode = Roo.get(els.el.parentNode, true);
35772     this.el.on("mousedown", this.onTabMouseDown, this);
35773     this.el.on("click", this.onTabClick, this);
35774     /** @private */
35775     if(config.closable){
35776         var c = Roo.get(els.close, true);
35777         c.dom.title = this.closeText;
35778         c.addClassOnOver("close-over");
35779         c.on("click", this.closeClick, this);
35780      }
35781
35782     this.addEvents({
35783          /**
35784          * @event activate
35785          * Fires when this tab becomes the active tab.
35786          * @param {Roo.TabPanel} tabPanel The parent TabPanel
35787          * @param {Roo.TabPanelItem} this
35788          */
35789         "activate": true,
35790         /**
35791          * @event beforeclose
35792          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
35793          * @param {Roo.TabPanelItem} this
35794          * @param {Object} e Set cancel to true on this object to cancel the close.
35795          */
35796         "beforeclose": true,
35797         /**
35798          * @event close
35799          * Fires when this tab is closed.
35800          * @param {Roo.TabPanelItem} this
35801          */
35802          "close": true,
35803         /**
35804          * @event deactivate
35805          * Fires when this tab is no longer the active tab.
35806          * @param {Roo.TabPanel} tabPanel The parent TabPanel
35807          * @param {Roo.TabPanelItem} this
35808          */
35809          "deactivate" : true
35810     });
35811     this.hidden = false;
35812
35813     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
35814 };
35815
35816 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
35817            {
35818     purgeListeners : function(){
35819        Roo.util.Observable.prototype.purgeListeners.call(this);
35820        this.el.removeAllListeners();
35821     },
35822     /**
35823      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
35824      */
35825     show : function(){
35826         this.pnode.addClass("active");
35827         this.showAction();
35828         if(Roo.isOpera){
35829             this.tabPanel.stripWrap.repaint();
35830         }
35831         this.fireEvent("activate", this.tabPanel, this);
35832     },
35833
35834     /**
35835      * Returns true if this tab is the active tab.
35836      * @return {Boolean}
35837      */
35838     isActive : function(){
35839         return this.tabPanel.getActiveTab() == this;
35840     },
35841
35842     /**
35843      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
35844      */
35845     hide : function(){
35846         this.pnode.removeClass("active");
35847         this.hideAction();
35848         this.fireEvent("deactivate", this.tabPanel, this);
35849     },
35850
35851     hideAction : function(){
35852         this.bodyEl.hide();
35853         this.bodyEl.setStyle("position", "absolute");
35854         this.bodyEl.setLeft("-20000px");
35855         this.bodyEl.setTop("-20000px");
35856     },
35857
35858     showAction : function(){
35859         this.bodyEl.setStyle("position", "relative");
35860         this.bodyEl.setTop("");
35861         this.bodyEl.setLeft("");
35862         this.bodyEl.show();
35863     },
35864
35865     /**
35866      * Set the tooltip for the tab.
35867      * @param {String} tooltip The tab's tooltip
35868      */
35869     setTooltip : function(text){
35870         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
35871             this.textEl.dom.qtip = text;
35872             this.textEl.dom.removeAttribute('title');
35873         }else{
35874             this.textEl.dom.title = text;
35875         }
35876     },
35877
35878     onTabClick : function(e){
35879         e.preventDefault();
35880         this.tabPanel.activate(this.id);
35881     },
35882
35883     onTabMouseDown : function(e){
35884         e.preventDefault();
35885         this.tabPanel.activate(this.id);
35886     },
35887 /*
35888     getWidth : function(){
35889         return this.inner.getWidth();
35890     },
35891
35892     setWidth : function(width){
35893         var iwidth = width - this.pnode.getPadding("lr");
35894         this.inner.setWidth(iwidth);
35895         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
35896         this.pnode.setWidth(width);
35897     },
35898 */
35899     /**
35900      * Show or hide the tab
35901      * @param {Boolean} hidden True to hide or false to show.
35902      */
35903     setHidden : function(hidden){
35904         this.hidden = hidden;
35905         this.pnode.setStyle("display", hidden ? "none" : "");
35906     },
35907
35908     /**
35909      * Returns true if this tab is "hidden"
35910      * @return {Boolean}
35911      */
35912     isHidden : function(){
35913         return this.hidden;
35914     },
35915
35916     /**
35917      * Returns the text for this tab
35918      * @return {String}
35919      */
35920     getText : function(){
35921         return this.text;
35922     },
35923     /*
35924     autoSize : function(){
35925         //this.el.beginMeasure();
35926         this.textEl.setWidth(1);
35927         /*
35928          *  #2804 [new] Tabs in Roojs
35929          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
35930          */
35931         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
35932         //this.el.endMeasure();
35933     //},
35934
35935     /**
35936      * Sets the text for the tab (Note: this also sets the tooltip text)
35937      * @param {String} text The tab's text and tooltip
35938      */
35939     setText : function(text){
35940         this.text = text;
35941         this.textEl.update(text);
35942         this.setTooltip(text);
35943         //if(!this.tabPanel.resizeTabs){
35944         //    this.autoSize();
35945         //}
35946     },
35947     /**
35948      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
35949      */
35950     activate : function(){
35951         this.tabPanel.activate(this.id);
35952     },
35953
35954     /**
35955      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
35956      */
35957     disable : function(){
35958         if(this.tabPanel.active != this){
35959             this.disabled = true;
35960             this.pnode.addClass("disabled");
35961         }
35962     },
35963
35964     /**
35965      * Enables this TabPanelItem if it was previously disabled.
35966      */
35967     enable : function(){
35968         this.disabled = false;
35969         this.pnode.removeClass("disabled");
35970     },
35971
35972     /**
35973      * Sets the content for this TabPanelItem.
35974      * @param {String} content The content
35975      * @param {Boolean} loadScripts true to look for and load scripts
35976      */
35977     setContent : function(content, loadScripts){
35978         this.bodyEl.update(content, loadScripts);
35979     },
35980
35981     /**
35982      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
35983      * @return {Roo.UpdateManager} The UpdateManager
35984      */
35985     getUpdateManager : function(){
35986         return this.bodyEl.getUpdateManager();
35987     },
35988
35989     /**
35990      * Set a URL to be used to load the content for this TabPanelItem.
35991      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
35992      * @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)
35993      * @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)
35994      * @return {Roo.UpdateManager} The UpdateManager
35995      */
35996     setUrl : function(url, params, loadOnce){
35997         if(this.refreshDelegate){
35998             this.un('activate', this.refreshDelegate);
35999         }
36000         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36001         this.on("activate", this.refreshDelegate);
36002         return this.bodyEl.getUpdateManager();
36003     },
36004
36005     /** @private */
36006     _handleRefresh : function(url, params, loadOnce){
36007         if(!loadOnce || !this.loaded){
36008             var updater = this.bodyEl.getUpdateManager();
36009             updater.update(url, params, this._setLoaded.createDelegate(this));
36010         }
36011     },
36012
36013     /**
36014      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
36015      *   Will fail silently if the setUrl method has not been called.
36016      *   This does not activate the panel, just updates its content.
36017      */
36018     refresh : function(){
36019         if(this.refreshDelegate){
36020            this.loaded = false;
36021            this.refreshDelegate();
36022         }
36023     },
36024
36025     /** @private */
36026     _setLoaded : function(){
36027         this.loaded = true;
36028     },
36029
36030     /** @private */
36031     closeClick : function(e){
36032         var o = {};
36033         e.stopEvent();
36034         this.fireEvent("beforeclose", this, o);
36035         if(o.cancel !== true){
36036             this.tabPanel.removeTab(this.id);
36037         }
36038     },
36039     /**
36040      * The text displayed in the tooltip for the close icon.
36041      * @type String
36042      */
36043     closeText : "Close this tab"
36044 });