Roo/bootstrap/DocumentManager.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         
105         cfg.id = this.id || Roo.id();
106         
107         // fill in the extra attributes 
108         if (this.xattr && typeof(this.xattr) =='object') {
109             for (var i in this.xattr) {
110                 cfg[i] = this.xattr[i];
111             }
112         }
113         
114         if(this.dataId){
115             cfg.dataId = this.dataId;
116         }
117         
118         if (this.cls) {
119             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
120         }
121         
122         if (this.style) { // fixme needs to support more complex style data.
123             cfg.style = this.style;
124         }
125         
126         if(this.name){
127             cfg.name = this.name;
128         }
129         
130         this.el = ct.createChild(cfg, position);
131         
132         if (this.tooltip) {
133             this.tooltipEl().attr('tooltip', this.tooltip);
134         }
135         
136         if(this.tabIndex !== undefined){
137             this.el.dom.setAttribute('tabIndex', this.tabIndex);
138         }
139         
140         this.initEvents();
141         
142     },
143     /**
144      * Fetch the element to add children to
145      * @return {Roo.Element} defaults to this.el
146      */
147     getChildContainer : function()
148     {
149         return this.el;
150     },
151     /**
152      * Fetch the element to display the tooltip on.
153      * @return {Roo.Element} defaults to this.el
154      */
155     tooltipEl : function()
156     {
157         return this.el;
158     },
159         
160     addxtype  : function(tree,cntr)
161     {
162         var cn = this;
163         
164         cn = Roo.factory(tree);
165         //Roo.log(['addxtype', cn]);
166            
167         cn.parentType = this.xtype; //??
168         cn.parentId = this.id;
169         
170         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171         if (typeof(cn.container_method) == 'string') {
172             cntr = cn.container_method;
173         }
174         
175         
176         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
177         
178         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
179         
180         var build_from_html =  Roo.XComponent.build_from_html;
181           
182         var is_body  = (tree.xtype == 'Body') ;
183           
184         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
185           
186         var self_cntr_el = Roo.get(this[cntr](false));
187         
188         // do not try and build conditional elements 
189         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
190             return false;
191         }
192         
193         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195                 return this.addxtypeChild(tree,cntr, is_body);
196             }
197             
198             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
199                 
200             if(echild){
201                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
202             }
203             
204             Roo.log('skipping render');
205             return cn;
206             
207         }
208         
209         var ret = false;
210         if (!build_from_html) {
211             return false;
212         }
213         
214         // this i think handles overlaying multiple children of the same type
215         // with the sam eelement.. - which might be buggy..
216         while (true) {
217             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
218             
219             if (!echild) {
220                 break;
221             }
222             
223             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
224                 break;
225             }
226             
227             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
228         }
229        
230         return ret;
231     },
232     
233     
234     addxtypeChild : function (tree, cntr, is_body)
235     {
236         Roo.debug && Roo.log('addxtypeChild:' + cntr);
237         var cn = this;
238         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
239         
240         
241         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
242                     (typeof(tree['flexy:foreach']) != 'undefined');
243           
244         
245         
246          skip_children = false;
247         // render the element if it's not BODY.
248         if (!is_body) {
249            
250             cn = Roo.factory(tree);
251            
252             cn.parentType = this.xtype; //??
253             cn.parentId = this.id;
254             
255             var build_from_html =  Roo.XComponent.build_from_html;
256             
257             
258             // does the container contain child eleemnts with 'xtype' attributes.
259             // that match this xtype..
260             // note - when we render we create these as well..
261             // so we should check to see if body has xtype set.
262             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
263                
264                 var self_cntr_el = Roo.get(this[cntr](false));
265                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
266                 if (echild) { 
267                     //Roo.log(Roo.XComponent.build_from_html);
268                     //Roo.log("got echild:");
269                     //Roo.log(echild);
270                 }
271                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
272                 // and are not displayed -this causes this to use up the wrong element when matching.
273                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
274                 
275                 
276                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
277                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
278                   
279                   
280                   
281                     cn.el = echild;
282                   //  Roo.log("GOT");
283                     //echild.dom.removeAttribute('xtype');
284                 } else {
285                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
286                     Roo.debug && Roo.log(self_cntr_el);
287                     Roo.debug && Roo.log(echild);
288                     Roo.debug && Roo.log(cn);
289                 }
290             }
291            
292             
293            
294             // if object has flexy:if - then it may or may not be rendered.
295             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
296                 // skip a flexy if element.
297                 Roo.debug && Roo.log('skipping render');
298                 Roo.debug && Roo.log(tree);
299                 if (!cn.el) {
300                     Roo.debug && Roo.log('skipping all children');
301                     skip_children = true;
302                 }
303                 
304              } else {
305                  
306                 // actually if flexy:foreach is found, we really want to create 
307                 // multiple copies here...
308                 //Roo.log('render');
309                 //Roo.log(this[cntr]());
310                 // some elements do not have render methods.. like the layouts...
311                 cn.render && cn.render(this[cntr](true));
312              }
313             // then add the element..
314         }
315         
316         
317         // handle the kids..
318         
319         var nitems = [];
320         /*
321         if (typeof (tree.menu) != 'undefined') {
322             tree.menu.parentType = cn.xtype;
323             tree.menu.triggerEl = cn.el;
324             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
325             
326         }
327         */
328         if (!tree.items || !tree.items.length) {
329             cn.items = nitems;
330             //Roo.log(["no children", this]);
331             
332             return cn;
333         }
334          
335         var items = tree.items;
336         delete tree.items;
337         
338         //Roo.log(items.length);
339             // add the items..
340         if (!skip_children) {    
341             for(var i =0;i < items.length;i++) {
342               //  Roo.log(['add child', items[i]]);
343                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
344             }
345         }
346         
347         cn.items = nitems;
348         
349         //Roo.log("fire childrenrendered");
350         
351         cn.fireEvent('childrenrendered', this);
352         
353         return cn;
354     },
355     /**
356      * Show a component - removes 'hidden' class
357      */
358     show : function()
359     {
360         if (this.el) {
361             this.el.removeClass('hidden');
362         }
363     },
364     /**
365      * Hide a component - adds 'hidden' class
366      */
367     hide: function()
368     {
369         if (this.el && !this.el.hasClass('hidden')) {
370             this.el.addClass('hidden');
371         }
372         
373     }
374 });
375
376  /*
377  * - LGPL
378  *
379  * Body
380  *
381  */
382
383 /**
384  * @class Roo.bootstrap.Body
385  * @extends Roo.bootstrap.Component
386  * Bootstrap Body class
387  *
388  * @constructor
389  * Create a new body
390  * @param {Object} config The config object
391  */
392
393 Roo.bootstrap.Body = function(config){
394
395     config = config || {};
396
397     Roo.bootstrap.Body.superclass.constructor.call(this, config);
398     this.el = Roo.get(config.el ? config.el : document.body );
399     if (this.cls && this.cls.length) {
400         Roo.get(document.body).addClass(this.cls);
401     }
402 };
403
404 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
405
406     is_body : true,// just to make sure it's constructed?
407
408         autoCreate : {
409         cls: 'container'
410     },
411     onRender : function(ct, position)
412     {
413        /* Roo.log("Roo.bootstrap.Body - onRender");
414         if (this.cls && this.cls.length) {
415             Roo.get(document.body).addClass(this.cls);
416         }
417         // style??? xttr???
418         */
419     }
420
421
422
423
424 });
425 /*
426  * - LGPL
427  *
428  * button group
429  * 
430  */
431
432
433 /**
434  * @class Roo.bootstrap.ButtonGroup
435  * @extends Roo.bootstrap.Component
436  * Bootstrap ButtonGroup class
437  * @cfg {String} size lg | sm | xs (default empty normal)
438  * @cfg {String} align vertical | justified  (default none)
439  * @cfg {String} direction up | down (default down)
440  * @cfg {Boolean} toolbar false | true
441  * @cfg {Boolean} btn true | false
442  * 
443  * 
444  * @constructor
445  * Create a new Input
446  * @param {Object} config The config object
447  */
448
449 Roo.bootstrap.ButtonGroup = function(config){
450     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
451 };
452
453 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
454     
455     size: '',
456     align: '',
457     direction: '',
458     toolbar: false,
459     btn: true,
460
461     getAutoCreate : function(){
462         var cfg = {
463             cls: 'btn-group',
464             html : null
465         };
466         
467         cfg.html = this.html || cfg.html;
468         
469         if (this.toolbar) {
470             cfg = {
471                 cls: 'btn-toolbar',
472                 html: null
473             };
474             
475             return cfg;
476         }
477         
478         if (['vertical','justified'].indexOf(this.align)!==-1) {
479             cfg.cls = 'btn-group-' + this.align;
480             
481             if (this.align == 'justified') {
482                 console.log(this.items);
483             }
484         }
485         
486         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
487             cfg.cls += ' btn-group-' + this.size;
488         }
489         
490         if (this.direction == 'up') {
491             cfg.cls += ' dropup' ;
492         }
493         
494         return cfg;
495     }
496    
497 });
498
499  /*
500  * - LGPL
501  *
502  * button
503  * 
504  */
505
506 /**
507  * @class Roo.bootstrap.Button
508  * @extends Roo.bootstrap.Component
509  * Bootstrap Button class
510  * @cfg {String} html The button content
511  * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default 
512  * @cfg {String} size ( lg | sm | xs)
513  * @cfg {String} tag ( a | input | submit)
514  * @cfg {String} href empty or href
515  * @cfg {Boolean} disabled default false;
516  * @cfg {Boolean} isClose default false;
517  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
518  * @cfg {String} badge text for badge
519  * @cfg {String} theme default 
520  * @cfg {Boolean} inverse 
521  * @cfg {Boolean} toggle 
522  * @cfg {String} ontext text for on toggle state
523  * @cfg {String} offtext text for off toggle state
524  * @cfg {Boolean} defaulton 
525  * @cfg {Boolean} preventDefault  default true
526  * @cfg {Boolean} removeClass remove the standard class..
527  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
528  * 
529  * @constructor
530  * Create a new button
531  * @param {Object} config The config object
532  */
533
534
535 Roo.bootstrap.Button = function(config){
536     Roo.bootstrap.Button.superclass.constructor.call(this, config);
537     this.addEvents({
538         // raw events
539         /**
540          * @event click
541          * When a butotn is pressed
542          * @param {Roo.bootstrap.Button} this
543          * @param {Roo.EventObject} e
544          */
545         "click" : true,
546          /**
547          * @event toggle
548          * After the button has been toggles
549          * @param {Roo.EventObject} e
550          * @param {boolean} pressed (also available as button.pressed)
551          */
552         "toggle" : true
553     });
554 };
555
556 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
557     html: false,
558     active: false,
559     weight: '',
560     size: '',
561     tag: 'button',
562     href: '',
563     disabled: false,
564     isClose: false,
565     glyphicon: '',
566     badge: '',
567     theme: 'default',
568     inverse: false,
569     
570     toggle: false,
571     ontext: 'ON',
572     offtext: 'OFF',
573     defaulton: true,
574     preventDefault: true,
575     removeClass: false,
576     name: false,
577     target: false,
578     
579     
580     pressed : null,
581      
582     
583     getAutoCreate : function(){
584         
585         var cfg = {
586             tag : 'button',
587             cls : 'roo-button',
588             html: ''
589         };
590         
591         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
592             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
593             this.tag = 'button';
594         } else {
595             cfg.tag = this.tag;
596         }
597         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
598         
599         if (this.toggle == true) {
600             cfg={
601                 tag: 'div',
602                 cls: 'slider-frame roo-button',
603                 cn: [
604                     {
605                         tag: 'span',
606                         'data-on-text':'ON',
607                         'data-off-text':'OFF',
608                         cls: 'slider-button',
609                         html: this.offtext
610                     }
611                 ]
612             };
613             
614             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
615                 cfg.cls += ' '+this.weight;
616             }
617             
618             return cfg;
619         }
620         
621         if (this.isClose) {
622             cfg.cls += ' close';
623             
624             cfg["aria-hidden"] = true;
625             
626             cfg.html = "&times;";
627             
628             return cfg;
629         }
630         
631          
632         if (this.theme==='default') {
633             cfg.cls = 'btn roo-button';
634             
635             //if (this.parentType != 'Navbar') {
636             this.weight = this.weight.length ?  this.weight : 'default';
637             //}
638             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
639                 
640                 cfg.cls += ' btn-' + this.weight;
641             }
642         } else if (this.theme==='glow') {
643             
644             cfg.tag = 'a';
645             cfg.cls = 'btn-glow roo-button';
646             
647             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
648                 
649                 cfg.cls += ' ' + this.weight;
650             }
651         }
652    
653         
654         if (this.inverse) {
655             this.cls += ' inverse';
656         }
657         
658         
659         if (this.active) {
660             cfg.cls += ' active';
661         }
662         
663         if (this.disabled) {
664             cfg.disabled = 'disabled';
665         }
666         
667         if (this.items) {
668             Roo.log('changing to ul' );
669             cfg.tag = 'ul';
670             this.glyphicon = 'caret';
671         }
672         
673         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
674          
675         //gsRoo.log(this.parentType);
676         if (this.parentType === 'Navbar' && !this.parent().bar) {
677             Roo.log('changing to li?');
678             
679             cfg.tag = 'li';
680             
681             cfg.cls = '';
682             cfg.cn =  [{
683                 tag : 'a',
684                 cls : 'roo-button',
685                 html : this.html,
686                 href : this.href || '#'
687             }];
688             if (this.menu) {
689                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
690                 cfg.cls += ' dropdown';
691             }   
692             
693             delete cfg.html;
694             
695         }
696         
697        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
698         
699         if (this.glyphicon) {
700             cfg.html = ' ' + cfg.html;
701             
702             cfg.cn = [
703                 {
704                     tag: 'span',
705                     cls: 'glyphicon glyphicon-' + this.glyphicon
706                 }
707             ];
708         }
709         
710         if (this.badge) {
711             cfg.html += ' ';
712             
713             cfg.tag = 'a';
714             
715 //            cfg.cls='btn roo-button';
716             
717             cfg.href=this.href;
718             
719             var value = cfg.html;
720             
721             if(this.glyphicon){
722                 value = {
723                             tag: 'span',
724                             cls: 'glyphicon glyphicon-' + this.glyphicon,
725                             html: this.html
726                         };
727                 
728             }
729             
730             cfg.cn = [
731                 value,
732                 {
733                     tag: 'span',
734                     cls: 'badge',
735                     html: this.badge
736                 }
737             ];
738             
739             cfg.html='';
740         }
741         
742         if (this.menu) {
743             cfg.cls += ' dropdown';
744             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
745         }
746         
747         if (cfg.tag !== 'a' && this.href !== '') {
748             throw "Tag must be a to set href.";
749         } else if (this.href.length > 0) {
750             cfg.href = this.href;
751         }
752         
753         if(this.removeClass){
754             cfg.cls = '';
755         }
756         
757         if(this.target){
758             cfg.target = this.target;
759         }
760         
761         return cfg;
762     },
763     initEvents: function() {
764        // Roo.log('init events?');
765 //        Roo.log(this.el.dom);
766         // add the menu...
767         
768         if (typeof (this.menu) != 'undefined') {
769             this.menu.parentType = this.xtype;
770             this.menu.triggerEl = this.el;
771             this.addxtype(Roo.apply({}, this.menu));
772         }
773
774
775        if (this.el.hasClass('roo-button')) {
776             this.el.on('click', this.onClick, this);
777        } else {
778             this.el.select('.roo-button').on('click', this.onClick, this);
779        }
780        
781        if(this.removeClass){
782            this.el.on('click', this.onClick, this);
783        }
784        
785        this.el.enableDisplayMode();
786         
787     },
788     onClick : function(e)
789     {
790         if (this.disabled) {
791             return;
792         }
793         
794         
795         Roo.log('button on click ');
796         if(this.preventDefault){
797             e.preventDefault();
798         }
799         if (this.pressed === true || this.pressed === false) {
800             this.pressed = !this.pressed;
801             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
802             this.fireEvent('toggle', this, e, this.pressed);
803         }
804         
805         
806         this.fireEvent('click', this, e);
807     },
808     
809     /**
810      * Enables this button
811      */
812     enable : function()
813     {
814         this.disabled = false;
815         this.el.removeClass('disabled');
816     },
817     
818     /**
819      * Disable this button
820      */
821     disable : function()
822     {
823         this.disabled = true;
824         this.el.addClass('disabled');
825     },
826      /**
827      * sets the active state on/off, 
828      * @param {Boolean} state (optional) Force a particular state
829      */
830     setActive : function(v) {
831         
832         this.el[v ? 'addClass' : 'removeClass']('active');
833     },
834      /**
835      * toggles the current active state 
836      */
837     toggleActive : function()
838     {
839        var active = this.el.hasClass('active');
840        this.setActive(!active);
841        
842         
843     },
844     setText : function(str)
845     {
846         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
847     },
848     getText : function()
849     {
850         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
851     },
852     hide: function() {
853        
854      
855         this.el.hide();   
856     },
857     show: function() {
858        
859         this.el.show();   
860     }
861     
862     
863 });
864
865  /*
866  * - LGPL
867  *
868  * column
869  * 
870  */
871
872 /**
873  * @class Roo.bootstrap.Column
874  * @extends Roo.bootstrap.Component
875  * Bootstrap Column class
876  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
877  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
878  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
879  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
880  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
881  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
882  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
883  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
884  *
885  * 
886  * @cfg {Boolean} hidden (true|false) hide the element
887  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
888  * @cfg {String} fa (ban|check|...) font awesome icon
889  * @cfg {Number} fasize (1|2|....) font awsome size
890
891  * @cfg {String} icon (info-sign|check|...) glyphicon name
892
893  * @cfg {String} html content of column.
894  * 
895  * @constructor
896  * Create a new Column
897  * @param {Object} config The config object
898  */
899
900 Roo.bootstrap.Column = function(config){
901     Roo.bootstrap.Column.superclass.constructor.call(this, config);
902 };
903
904 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
905     
906     xs: false,
907     sm: false,
908     md: false,
909     lg: false,
910     xsoff: false,
911     smoff: false,
912     mdoff: false,
913     lgoff: false,
914     html: '',
915     offset: 0,
916     alert: false,
917     fa: false,
918     icon : false,
919     hidden : false,
920     fasize : 1,
921     
922     getAutoCreate : function(){
923         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
924         
925         cfg = {
926             tag: 'div',
927             cls: 'column'
928         };
929         
930         var settings=this;
931         ['xs','sm','md','lg'].map(function(size){
932             //Roo.log( size + ':' + settings[size]);
933             
934             if (settings[size+'off'] !== false) {
935                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
936             }
937             
938             if (settings[size] === false) {
939                 return;
940             }
941             
942             if (!settings[size]) { // 0 = hidden
943                 cfg.cls += ' hidden-' + size;
944                 return;
945             }
946             cfg.cls += ' col-' + size + '-' + settings[size];
947             
948         });
949         
950         if (this.hidden) {
951             cfg.cls += ' hidden';
952         }
953         
954         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
955             cfg.cls +=' alert alert-' + this.alert;
956         }
957         
958         
959         if (this.html.length) {
960             cfg.html = this.html;
961         }
962         if (this.fa) {
963             var fasize = '';
964             if (this.fasize > 1) {
965                 fasize = ' fa-' + this.fasize + 'x';
966             }
967             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
968             
969             
970         }
971         if (this.icon) {
972             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
973         }
974         
975         return cfg;
976     }
977    
978 });
979
980  
981
982  /*
983  * - LGPL
984  *
985  * page container.
986  * 
987  */
988
989
990 /**
991  * @class Roo.bootstrap.Container
992  * @extends Roo.bootstrap.Component
993  * Bootstrap Container class
994  * @cfg {Boolean} jumbotron is it a jumbotron element
995  * @cfg {String} html content of element
996  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
997  * @cfg {String} panel (primary|success|info|warning|danger) render as panel  - type - primary/success.....
998  * @cfg {String} header content of header (for panel)
999  * @cfg {String} footer content of footer (for panel)
1000  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1001  * @cfg {String} tag (header|aside|section) type of HTML tag.
1002  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1003  * @cfg {String} fa font awesome icon
1004  * @cfg {String} icon (info-sign|check|...) glyphicon name
1005  * @cfg {Boolean} hidden (true|false) hide the element
1006  * @cfg {Boolean} expandable (true|false) default false
1007  * @cfg {Boolean} expanded (true|false) default true
1008  * @cfg {String} rheader contet on the right of header
1009  * @cfg {Boolean} clickable (true|false) default false
1010
1011  *     
1012  * @constructor
1013  * Create a new Container
1014  * @param {Object} config The config object
1015  */
1016
1017 Roo.bootstrap.Container = function(config){
1018     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1019     
1020     this.addEvents({
1021         // raw events
1022          /**
1023          * @event expand
1024          * After the panel has been expand
1025          * 
1026          * @param {Roo.bootstrap.Container} this
1027          */
1028         "expand" : true,
1029         /**
1030          * @event collapse
1031          * After the panel has been collapsed
1032          * 
1033          * @param {Roo.bootstrap.Container} this
1034          */
1035         "collapse" : true,
1036         /**
1037          * @event click
1038          * When a element is chick
1039          * @param {Roo.bootstrap.Container} this
1040          * @param {Roo.EventObject} e
1041          */
1042         "click" : true
1043     });
1044 };
1045
1046 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1047     
1048     jumbotron : false,
1049     well: '',
1050     panel : '',
1051     header: '',
1052     footer : '',
1053     sticky: '',
1054     tag : false,
1055     alert : false,
1056     fa: false,
1057     icon : false,
1058     expandable : false,
1059     rheader : '',
1060     expanded : true,
1061     clickable: false,
1062   
1063      
1064     getChildContainer : function() {
1065         
1066         if(!this.el){
1067             return false;
1068         }
1069         
1070         if (this.panel.length) {
1071             return this.el.select('.panel-body',true).first();
1072         }
1073         
1074         return this.el;
1075     },
1076     
1077     
1078     getAutoCreate : function(){
1079         
1080         var cfg = {
1081             tag : this.tag || 'div',
1082             html : '',
1083             cls : ''
1084         };
1085         if (this.jumbotron) {
1086             cfg.cls = 'jumbotron';
1087         }
1088         
1089         
1090         
1091         // - this is applied by the parent..
1092         //if (this.cls) {
1093         //    cfg.cls = this.cls + '';
1094         //}
1095         
1096         if (this.sticky.length) {
1097             
1098             var bd = Roo.get(document.body);
1099             if (!bd.hasClass('bootstrap-sticky')) {
1100                 bd.addClass('bootstrap-sticky');
1101                 Roo.select('html',true).setStyle('height', '100%');
1102             }
1103              
1104             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1105         }
1106         
1107         
1108         if (this.well.length) {
1109             switch (this.well) {
1110                 case 'lg':
1111                 case 'sm':
1112                     cfg.cls +=' well well-' +this.well;
1113                     break;
1114                 default:
1115                     cfg.cls +=' well';
1116                     break;
1117             }
1118         }
1119         
1120         if (this.hidden) {
1121             cfg.cls += ' hidden';
1122         }
1123         
1124         
1125         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1126             cfg.cls +=' alert alert-' + this.alert;
1127         }
1128         
1129         var body = cfg;
1130         
1131         if (this.panel.length) {
1132             cfg.cls += ' panel panel-' + this.panel;
1133             cfg.cn = [];
1134             if (this.header.length) {
1135                 
1136                 var h = [];
1137                 
1138                 if(this.expandable){
1139                     
1140                     cfg.cls = cfg.cls + ' expandable';
1141                     
1142                     h.push({
1143                         tag: 'i',
1144                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1145                     });
1146                     
1147                 }
1148                 
1149                 h.push(
1150                     {
1151                         tag: 'span',
1152                         cls : 'panel-title',
1153                         html : (this.expandable ? '&nbsp;' : '') + this.header
1154                     },
1155                     {
1156                         tag: 'span',
1157                         cls: 'panel-header-right',
1158                         html: this.rheader
1159                     }
1160                 );
1161                 
1162                 cfg.cn.push({
1163                     cls : 'panel-heading',
1164                     style : this.expandable ? 'cursor: pointer' : '',
1165                     cn : h
1166                 });
1167                 
1168             }
1169             
1170             body = false;
1171             cfg.cn.push({
1172                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1173                 html : this.html
1174             });
1175             
1176             
1177             if (this.footer.length) {
1178                 cfg.cn.push({
1179                     cls : 'panel-footer',
1180                     html : this.footer
1181                     
1182                 });
1183             }
1184             
1185         }
1186         
1187         if (body) {
1188             body.html = this.html || cfg.html;
1189             // prefix with the icons..
1190             if (this.fa) {
1191                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1192             }
1193             if (this.icon) {
1194                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1195             }
1196             
1197             
1198         }
1199         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1200             cfg.cls =  'container';
1201         }
1202         
1203         return cfg;
1204     },
1205     
1206     initEvents: function() 
1207     {
1208         if(this.expandable){
1209             var headerEl = this.headerEl();
1210         
1211             if(headerEl){
1212                 headerEl.on('click', this.onToggleClick, this);
1213             }
1214         }
1215         
1216         if(this.clickable){
1217             this.el.on('click', this.onClick, this);
1218         }
1219         
1220     },
1221     
1222     onToggleClick : function()
1223     {
1224         var headerEl = this.headerEl();
1225         
1226         if(!headerEl){
1227             return;
1228         }
1229         
1230         if(this.expanded){
1231             this.collapse();
1232             return;
1233         }
1234         
1235         this.expand();
1236     },
1237     
1238     expand : function()
1239     {
1240         if(this.fireEvent('expand', this)) {
1241             
1242             this.expanded = true;
1243             
1244             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1245             
1246             this.el.select('.panel-body',true).first().removeClass('hide');
1247             
1248             var toggleEl = this.toggleEl();
1249
1250             if(!toggleEl){
1251                 return;
1252             }
1253
1254             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1255         }
1256         
1257     },
1258     
1259     collapse : function()
1260     {
1261         if(this.fireEvent('collapse', this)) {
1262             
1263             this.expanded = false;
1264             
1265             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1266             this.el.select('.panel-body',true).first().addClass('hide');
1267         
1268             var toggleEl = this.toggleEl();
1269
1270             if(!toggleEl){
1271                 return;
1272             }
1273
1274             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1275         }
1276     },
1277     
1278     toggleEl : function()
1279     {
1280         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1281             return;
1282         }
1283         
1284         return this.el.select('.panel-heading .fa',true).first();
1285     },
1286     
1287     headerEl : function()
1288     {
1289         if(!this.el || !this.panel.length || !this.header.length){
1290             return;
1291         }
1292         
1293         return this.el.select('.panel-heading',true).first()
1294     },
1295     
1296     titleEl : function()
1297     {
1298         if(!this.el || !this.panel.length || !this.header.length){
1299             return;
1300         }
1301         
1302         return this.el.select('.panel-title',true).first();
1303     },
1304     
1305     setTitle : function(v)
1306     {
1307         var titleEl = this.titleEl();
1308         
1309         if(!titleEl){
1310             return;
1311         }
1312         
1313         titleEl.dom.innerHTML = v;
1314     },
1315     
1316     getTitle : function()
1317     {
1318         
1319         var titleEl = this.titleEl();
1320         
1321         if(!titleEl){
1322             return '';
1323         }
1324         
1325         return titleEl.dom.innerHTML;
1326     },
1327     
1328     setRightTitle : function(v)
1329     {
1330         var t = this.el.select('.panel-header-right',true).first();
1331         
1332         if(!t){
1333             return;
1334         }
1335         
1336         t.dom.innerHTML = v;
1337     },
1338     
1339     onClick : function(e)
1340     {
1341         e.preventDefault();
1342         
1343         this.fireEvent('click', this, e);
1344     }
1345    
1346 });
1347
1348  /*
1349  * - LGPL
1350  *
1351  * image
1352  * 
1353  */
1354
1355
1356 /**
1357  * @class Roo.bootstrap.Img
1358  * @extends Roo.bootstrap.Component
1359  * Bootstrap Img class
1360  * @cfg {Boolean} imgResponsive false | true
1361  * @cfg {String} border rounded | circle | thumbnail
1362  * @cfg {String} src image source
1363  * @cfg {String} alt image alternative text
1364  * @cfg {String} href a tag href
1365  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1366  * @cfg {String} xsUrl xs image source
1367  * @cfg {String} smUrl sm image source
1368  * @cfg {String} mdUrl md image source
1369  * @cfg {String} lgUrl lg image source
1370  * 
1371  * @constructor
1372  * Create a new Input
1373  * @param {Object} config The config object
1374  */
1375
1376 Roo.bootstrap.Img = function(config){
1377     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1378     
1379     this.addEvents({
1380         // img events
1381         /**
1382          * @event click
1383          * The img click event for the img.
1384          * @param {Roo.EventObject} e
1385          */
1386         "click" : true
1387     });
1388 };
1389
1390 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1391     
1392     imgResponsive: true,
1393     border: '',
1394     src: 'about:blank',
1395     href: false,
1396     target: false,
1397     xsUrl: '',
1398     smUrl: '',
1399     mdUrl: '',
1400     lgUrl: '',
1401
1402     getAutoCreate : function()
1403     {   
1404         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1405             return this.createSingleImg();
1406         }
1407         
1408         var cfg = {
1409             tag: 'div',
1410             cls: 'roo-image-responsive-group',
1411             cn: []
1412         };
1413         var _this = this;
1414         
1415         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1416             
1417             if(!_this[size + 'Url']){
1418                 return;
1419             }
1420             
1421             var img = {
1422                 tag: 'img',
1423                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1424                 html: _this.html || cfg.html,
1425                 src: _this[size + 'Url']
1426             };
1427             
1428             img.cls += ' roo-image-responsive-' + size;
1429             
1430             var s = ['xs', 'sm', 'md', 'lg'];
1431             
1432             s.splice(s.indexOf(size), 1);
1433             
1434             Roo.each(s, function(ss){
1435                 img.cls += ' hidden-' + ss;
1436             });
1437             
1438             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1439                 cfg.cls += ' img-' + _this.border;
1440             }
1441             
1442             if(_this.alt){
1443                 cfg.alt = _this.alt;
1444             }
1445             
1446             if(_this.href){
1447                 var a = {
1448                     tag: 'a',
1449                     href: _this.href,
1450                     cn: [
1451                         img
1452                     ]
1453                 };
1454
1455                 if(this.target){
1456                     a.target = _this.target;
1457                 }
1458             }
1459             
1460             cfg.cn.push((_this.href) ? a : img);
1461             
1462         });
1463         
1464         return cfg;
1465     },
1466     
1467     createSingleImg : function()
1468     {
1469         var cfg = {
1470             tag: 'img',
1471             cls: (this.imgResponsive) ? 'img-responsive' : '',
1472             html : null,
1473             src : 'about:blank'  // just incase src get's set to undefined?!?
1474         };
1475         
1476         cfg.html = this.html || cfg.html;
1477         
1478         cfg.src = this.src || cfg.src;
1479         
1480         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1481             cfg.cls += ' img-' + this.border;
1482         }
1483         
1484         if(this.alt){
1485             cfg.alt = this.alt;
1486         }
1487         
1488         if(this.href){
1489             var a = {
1490                 tag: 'a',
1491                 href: this.href,
1492                 cn: [
1493                     cfg
1494                 ]
1495             };
1496             
1497             if(this.target){
1498                 a.target = this.target;
1499             }
1500             
1501         }
1502         
1503         return (this.href) ? a : cfg;
1504     },
1505     
1506     initEvents: function() 
1507     {
1508         if(!this.href){
1509             this.el.on('click', this.onClick, this);
1510         }
1511         
1512     },
1513     
1514     onClick : function(e)
1515     {
1516         Roo.log('img onclick');
1517         this.fireEvent('click', this, e);
1518     },
1519     /**
1520      * Sets the url of the image - used to update it
1521      * @param {String} url the url of the image
1522      */
1523     
1524     setSrc : function(url)
1525     {
1526         this.src =  url;
1527         this.el.select('img', true).first().dom.src =  url;
1528     }
1529     
1530     
1531    
1532 });
1533
1534  /*
1535  * - LGPL
1536  *
1537  * image
1538  * 
1539  */
1540
1541
1542 /**
1543  * @class Roo.bootstrap.Link
1544  * @extends Roo.bootstrap.Component
1545  * Bootstrap Link Class
1546  * @cfg {String} alt image alternative text
1547  * @cfg {String} href a tag href
1548  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1549  * @cfg {String} html the content of the link.
1550  * @cfg {String} anchor name for the anchor link
1551  * @cfg {String} fa - favicon
1552
1553  * @cfg {Boolean} preventDefault (true | false) default false
1554
1555  * 
1556  * @constructor
1557  * Create a new Input
1558  * @param {Object} config The config object
1559  */
1560
1561 Roo.bootstrap.Link = function(config){
1562     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1563     
1564     this.addEvents({
1565         // img events
1566         /**
1567          * @event click
1568          * The img click event for the img.
1569          * @param {Roo.EventObject} e
1570          */
1571         "click" : true
1572     });
1573 };
1574
1575 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1576     
1577     href: false,
1578     target: false,
1579     preventDefault: false,
1580     anchor : false,
1581     alt : false,
1582     fa: false,
1583
1584
1585     getAutoCreate : function()
1586     {
1587         var html = this.html || '';
1588         
1589         if (this.fa !== false) {
1590             html = '<i class="fa fa-' + this.fa + '"></i>';
1591         }
1592         var cfg = {
1593             tag: 'a'
1594         };
1595         // anchor's do not require html/href...
1596         if (this.anchor === false) {
1597             cfg.html = html;
1598             cfg.href = this.href || '#';
1599         } else {
1600             cfg.name = this.anchor;
1601             if (this.html !== false || this.fa !== false) {
1602                 cfg.html = html;
1603             }
1604             if (this.href !== false) {
1605                 cfg.href = this.href;
1606             }
1607         }
1608         
1609         if(this.alt !== false){
1610             cfg.alt = this.alt;
1611         }
1612         
1613         
1614         if(this.target !== false) {
1615             cfg.target = this.target;
1616         }
1617         
1618         return cfg;
1619     },
1620     
1621     initEvents: function() {
1622         
1623         if(!this.href || this.preventDefault){
1624             this.el.on('click', this.onClick, this);
1625         }
1626     },
1627     
1628     onClick : function(e)
1629     {
1630         if(this.preventDefault){
1631             e.preventDefault();
1632         }
1633         //Roo.log('img onclick');
1634         this.fireEvent('click', this, e);
1635     }
1636    
1637 });
1638
1639  /*
1640  * - LGPL
1641  *
1642  * header
1643  * 
1644  */
1645
1646 /**
1647  * @class Roo.bootstrap.Header
1648  * @extends Roo.bootstrap.Component
1649  * Bootstrap Header class
1650  * @cfg {String} html content of header
1651  * @cfg {Number} level (1|2|3|4|5|6) default 1
1652  * 
1653  * @constructor
1654  * Create a new Header
1655  * @param {Object} config The config object
1656  */
1657
1658
1659 Roo.bootstrap.Header  = function(config){
1660     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1661 };
1662
1663 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1664     
1665     //href : false,
1666     html : false,
1667     level : 1,
1668     
1669     
1670     
1671     getAutoCreate : function(){
1672         
1673         
1674         
1675         var cfg = {
1676             tag: 'h' + (1 *this.level),
1677             html: this.html || ''
1678         } ;
1679         
1680         return cfg;
1681     }
1682    
1683 });
1684
1685  
1686
1687  /*
1688  * Based on:
1689  * Ext JS Library 1.1.1
1690  * Copyright(c) 2006-2007, Ext JS, LLC.
1691  *
1692  * Originally Released Under LGPL - original licence link has changed is not relivant.
1693  *
1694  * Fork - LGPL
1695  * <script type="text/javascript">
1696  */
1697  
1698 /**
1699  * @class Roo.bootstrap.MenuMgr
1700  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1701  * @singleton
1702  */
1703 Roo.bootstrap.MenuMgr = function(){
1704    var menus, active, groups = {}, attached = false, lastShow = new Date();
1705
1706    // private - called when first menu is created
1707    function init(){
1708        menus = {};
1709        active = new Roo.util.MixedCollection();
1710        Roo.get(document).addKeyListener(27, function(){
1711            if(active.length > 0){
1712                hideAll();
1713            }
1714        });
1715    }
1716
1717    // private
1718    function hideAll(){
1719        if(active && active.length > 0){
1720            var c = active.clone();
1721            c.each(function(m){
1722                m.hide();
1723            });
1724        }
1725    }
1726
1727    // private
1728    function onHide(m){
1729        active.remove(m);
1730        if(active.length < 1){
1731            Roo.get(document).un("mouseup", onMouseDown);
1732             
1733            attached = false;
1734        }
1735    }
1736
1737    // private
1738    function onShow(m){
1739        var last = active.last();
1740        lastShow = new Date();
1741        active.add(m);
1742        if(!attached){
1743           Roo.get(document).on("mouseup", onMouseDown);
1744            
1745            attached = true;
1746        }
1747        if(m.parentMenu){
1748           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1749           m.parentMenu.activeChild = m;
1750        }else if(last && last.isVisible()){
1751           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1752        }
1753    }
1754
1755    // private
1756    function onBeforeHide(m){
1757        if(m.activeChild){
1758            m.activeChild.hide();
1759        }
1760        if(m.autoHideTimer){
1761            clearTimeout(m.autoHideTimer);
1762            delete m.autoHideTimer;
1763        }
1764    }
1765
1766    // private
1767    function onBeforeShow(m){
1768        var pm = m.parentMenu;
1769        if(!pm && !m.allowOtherMenus){
1770            hideAll();
1771        }else if(pm && pm.activeChild && active != m){
1772            pm.activeChild.hide();
1773        }
1774    }
1775
1776    // private this should really trigger on mouseup..
1777    function onMouseDown(e){
1778         Roo.log("on Mouse Up");
1779         
1780         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1781             Roo.log("MenuManager hideAll");
1782             hideAll();
1783             e.stopEvent();
1784         }
1785         
1786         
1787    }
1788
1789    // private
1790    function onBeforeCheck(mi, state){
1791        if(state){
1792            var g = groups[mi.group];
1793            for(var i = 0, l = g.length; i < l; i++){
1794                if(g[i] != mi){
1795                    g[i].setChecked(false);
1796                }
1797            }
1798        }
1799    }
1800
1801    return {
1802
1803        /**
1804         * Hides all menus that are currently visible
1805         */
1806        hideAll : function(){
1807             hideAll();  
1808        },
1809
1810        // private
1811        register : function(menu){
1812            if(!menus){
1813                init();
1814            }
1815            menus[menu.id] = menu;
1816            menu.on("beforehide", onBeforeHide);
1817            menu.on("hide", onHide);
1818            menu.on("beforeshow", onBeforeShow);
1819            menu.on("show", onShow);
1820            var g = menu.group;
1821            if(g && menu.events["checkchange"]){
1822                if(!groups[g]){
1823                    groups[g] = [];
1824                }
1825                groups[g].push(menu);
1826                menu.on("checkchange", onCheck);
1827            }
1828        },
1829
1830         /**
1831          * Returns a {@link Roo.menu.Menu} object
1832          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1833          * be used to generate and return a new Menu instance.
1834          */
1835        get : function(menu){
1836            if(typeof menu == "string"){ // menu id
1837                return menus[menu];
1838            }else if(menu.events){  // menu instance
1839                return menu;
1840            }
1841            /*else if(typeof menu.length == 'number'){ // array of menu items?
1842                return new Roo.bootstrap.Menu({items:menu});
1843            }else{ // otherwise, must be a config
1844                return new Roo.bootstrap.Menu(menu);
1845            }
1846            */
1847            return false;
1848        },
1849
1850        // private
1851        unregister : function(menu){
1852            delete menus[menu.id];
1853            menu.un("beforehide", onBeforeHide);
1854            menu.un("hide", onHide);
1855            menu.un("beforeshow", onBeforeShow);
1856            menu.un("show", onShow);
1857            var g = menu.group;
1858            if(g && menu.events["checkchange"]){
1859                groups[g].remove(menu);
1860                menu.un("checkchange", onCheck);
1861            }
1862        },
1863
1864        // private
1865        registerCheckable : function(menuItem){
1866            var g = menuItem.group;
1867            if(g){
1868                if(!groups[g]){
1869                    groups[g] = [];
1870                }
1871                groups[g].push(menuItem);
1872                menuItem.on("beforecheckchange", onBeforeCheck);
1873            }
1874        },
1875
1876        // private
1877        unregisterCheckable : function(menuItem){
1878            var g = menuItem.group;
1879            if(g){
1880                groups[g].remove(menuItem);
1881                menuItem.un("beforecheckchange", onBeforeCheck);
1882            }
1883        }
1884    };
1885 }();/*
1886  * - LGPL
1887  *
1888  * menu
1889  * 
1890  */
1891
1892 /**
1893  * @class Roo.bootstrap.Menu
1894  * @extends Roo.bootstrap.Component
1895  * Bootstrap Menu class - container for MenuItems
1896  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1897  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1898  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1899  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1900  * 
1901  * @constructor
1902  * Create a new Menu
1903  * @param {Object} config The config object
1904  */
1905
1906
1907 Roo.bootstrap.Menu = function(config){
1908     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1909     if (this.registerMenu && this.type != 'treeview')  {
1910         Roo.bootstrap.MenuMgr.register(this);
1911     }
1912     this.addEvents({
1913         /**
1914          * @event beforeshow
1915          * Fires before this menu is displayed
1916          * @param {Roo.menu.Menu} this
1917          */
1918         beforeshow : true,
1919         /**
1920          * @event beforehide
1921          * Fires before this menu is hidden
1922          * @param {Roo.menu.Menu} this
1923          */
1924         beforehide : true,
1925         /**
1926          * @event show
1927          * Fires after this menu is displayed
1928          * @param {Roo.menu.Menu} this
1929          */
1930         show : true,
1931         /**
1932          * @event hide
1933          * Fires after this menu is hidden
1934          * @param {Roo.menu.Menu} this
1935          */
1936         hide : true,
1937         /**
1938          * @event click
1939          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1940          * @param {Roo.menu.Menu} this
1941          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1942          * @param {Roo.EventObject} e
1943          */
1944         click : true,
1945         /**
1946          * @event mouseover
1947          * Fires when the mouse is hovering over this menu
1948          * @param {Roo.menu.Menu} this
1949          * @param {Roo.EventObject} e
1950          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1951          */
1952         mouseover : true,
1953         /**
1954          * @event mouseout
1955          * Fires when the mouse exits this menu
1956          * @param {Roo.menu.Menu} this
1957          * @param {Roo.EventObject} e
1958          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1959          */
1960         mouseout : true,
1961         /**
1962          * @event itemclick
1963          * Fires when a menu item contained in this menu is clicked
1964          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1965          * @param {Roo.EventObject} e
1966          */
1967         itemclick: true
1968     });
1969     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1970 };
1971
1972 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1973     
1974    /// html : false,
1975     //align : '',
1976     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1977     type: false,
1978     /**
1979      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1980      */
1981     registerMenu : true,
1982     
1983     menuItems :false, // stores the menu items..
1984     
1985     hidden:true,
1986         
1987     parentMenu : false,
1988     
1989     stopEvent : true,
1990     
1991     isLink : false,
1992     
1993     getChildContainer : function() {
1994         return this.el;  
1995     },
1996     
1997     getAutoCreate : function(){
1998          
1999         //if (['right'].indexOf(this.align)!==-1) {
2000         //    cfg.cn[1].cls += ' pull-right'
2001         //}
2002         
2003         
2004         var cfg = {
2005             tag : 'ul',
2006             cls : 'dropdown-menu' ,
2007             style : 'z-index:1000'
2008             
2009         };
2010         
2011         if (this.type === 'submenu') {
2012             cfg.cls = 'submenu active';
2013         }
2014         if (this.type === 'treeview') {
2015             cfg.cls = 'treeview-menu';
2016         }
2017         
2018         return cfg;
2019     },
2020     initEvents : function() {
2021         
2022        // Roo.log("ADD event");
2023        // Roo.log(this.triggerEl.dom);
2024         
2025         this.triggerEl.on('click', this.onTriggerClick, this);
2026         
2027         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2028         
2029         this.triggerEl.addClass('dropdown-toggle');
2030         
2031         if (Roo.isTouch) {
2032             this.el.on('touchstart'  , this.onTouch, this);
2033         }
2034         this.el.on('click' , this.onClick, this);
2035
2036         this.el.on("mouseover", this.onMouseOver, this);
2037         this.el.on("mouseout", this.onMouseOut, this);
2038         
2039     },
2040     
2041     findTargetItem : function(e)
2042     {
2043         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2044         if(!t){
2045             return false;
2046         }
2047         //Roo.log(t);         Roo.log(t.id);
2048         if(t && t.id){
2049             //Roo.log(this.menuitems);
2050             return this.menuitems.get(t.id);
2051             
2052             //return this.items.get(t.menuItemId);
2053         }
2054         
2055         return false;
2056     },
2057     
2058     onTouch : function(e) 
2059     {
2060         Roo.log("menu.onTouch");
2061         //e.stopEvent(); this make the user popdown broken
2062         this.onClick(e);
2063     },
2064     
2065     onClick : function(e)
2066     {
2067         Roo.log("menu.onClick");
2068         
2069         var t = this.findTargetItem(e);
2070         if(!t || t.isContainer){
2071             return;
2072         }
2073         Roo.log(e);
2074         /*
2075         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2076             if(t == this.activeItem && t.shouldDeactivate(e)){
2077                 this.activeItem.deactivate();
2078                 delete this.activeItem;
2079                 return;
2080             }
2081             if(t.canActivate){
2082                 this.setActiveItem(t, true);
2083             }
2084             return;
2085             
2086             
2087         }
2088         */
2089        
2090         Roo.log('pass click event');
2091         
2092         t.onClick(e);
2093         
2094         this.fireEvent("click", this, t, e);
2095         
2096         var _this = this;
2097         
2098         (function() { _this.hide(); }).defer(100);
2099     },
2100     
2101     onMouseOver : function(e){
2102         var t  = this.findTargetItem(e);
2103         //Roo.log(t);
2104         //if(t){
2105         //    if(t.canActivate && !t.disabled){
2106         //        this.setActiveItem(t, true);
2107         //    }
2108         //}
2109         
2110         this.fireEvent("mouseover", this, e, t);
2111     },
2112     isVisible : function(){
2113         return !this.hidden;
2114     },
2115      onMouseOut : function(e){
2116         var t  = this.findTargetItem(e);
2117         
2118         //if(t ){
2119         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2120         //        this.activeItem.deactivate();
2121         //        delete this.activeItem;
2122         //    }
2123         //}
2124         this.fireEvent("mouseout", this, e, t);
2125     },
2126     
2127     
2128     /**
2129      * Displays this menu relative to another element
2130      * @param {String/HTMLElement/Roo.Element} element The element to align to
2131      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2132      * the element (defaults to this.defaultAlign)
2133      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2134      */
2135     show : function(el, pos, parentMenu){
2136         this.parentMenu = parentMenu;
2137         if(!this.el){
2138             this.render();
2139         }
2140         this.fireEvent("beforeshow", this);
2141         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2142     },
2143      /**
2144      * Displays this menu at a specific xy position
2145      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2146      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2147      */
2148     showAt : function(xy, parentMenu, /* private: */_e){
2149         this.parentMenu = parentMenu;
2150         if(!this.el){
2151             this.render();
2152         }
2153         if(_e !== false){
2154             this.fireEvent("beforeshow", this);
2155             //xy = this.el.adjustForConstraints(xy);
2156         }
2157         
2158         //this.el.show();
2159         this.hideMenuItems();
2160         this.hidden = false;
2161         this.triggerEl.addClass('open');
2162         
2163         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2164             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2165         }
2166         
2167         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2168             this.el.setXY(xy);
2169         }
2170         
2171         this.focus();
2172         this.fireEvent("show", this);
2173     },
2174     
2175     focus : function(){
2176         return;
2177         if(!this.hidden){
2178             this.doFocus.defer(50, this);
2179         }
2180     },
2181
2182     doFocus : function(){
2183         if(!this.hidden){
2184             this.focusEl.focus();
2185         }
2186     },
2187
2188     /**
2189      * Hides this menu and optionally all parent menus
2190      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2191      */
2192     hide : function(deep)
2193     {
2194         
2195         this.hideMenuItems();
2196         if(this.el && this.isVisible()){
2197             this.fireEvent("beforehide", this);
2198             if(this.activeItem){
2199                 this.activeItem.deactivate();
2200                 this.activeItem = null;
2201             }
2202             this.triggerEl.removeClass('open');;
2203             this.hidden = true;
2204             this.fireEvent("hide", this);
2205         }
2206         if(deep === true && this.parentMenu){
2207             this.parentMenu.hide(true);
2208         }
2209     },
2210     
2211     onTriggerClick : function(e)
2212     {
2213         Roo.log('trigger click');
2214         
2215         var target = e.getTarget();
2216         
2217         Roo.log(target.nodeName.toLowerCase());
2218         
2219         if(target.nodeName.toLowerCase() === 'i'){
2220             e.preventDefault();
2221         }
2222         
2223     },
2224     
2225     onTriggerPress  : function(e)
2226     {
2227         Roo.log('trigger press');
2228         //Roo.log(e.getTarget());
2229        // Roo.log(this.triggerEl.dom);
2230        
2231         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2232         var pel = Roo.get(e.getTarget());
2233         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2234             Roo.log('is treeview or dropdown?');
2235             return;
2236         }
2237         
2238         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2239             return;
2240         }
2241         
2242         if (this.isVisible()) {
2243             Roo.log('hide');
2244             this.hide();
2245         } else {
2246             Roo.log('show');
2247             this.show(this.triggerEl, false, false);
2248         }
2249         
2250         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2251             e.stopEvent();
2252         }
2253         
2254     },
2255        
2256     
2257     hideMenuItems : function()
2258     {
2259         Roo.log("hide Menu Items");
2260         if (!this.el) { 
2261             return;
2262         }
2263         //$(backdrop).remove()
2264         this.el.select('.open',true).each(function(aa) {
2265             
2266             aa.removeClass('open');
2267           //var parent = getParent($(this))
2268           //var relatedTarget = { relatedTarget: this }
2269           
2270            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2271           //if (e.isDefaultPrevented()) return
2272            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2273         });
2274     },
2275     addxtypeChild : function (tree, cntr) {
2276         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2277           
2278         this.menuitems.add(comp);
2279         return comp;
2280
2281     },
2282     getEl : function()
2283     {
2284         Roo.log(this.el);
2285         return this.el;
2286     }
2287 });
2288
2289  
2290  /*
2291  * - LGPL
2292  *
2293  * menu item
2294  * 
2295  */
2296
2297
2298 /**
2299  * @class Roo.bootstrap.MenuItem
2300  * @extends Roo.bootstrap.Component
2301  * Bootstrap MenuItem class
2302  * @cfg {String} html the menu label
2303  * @cfg {String} href the link
2304  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2305  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2306  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2307  * @cfg {String} fa favicon to show on left of menu item.
2308  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2309  * 
2310  * 
2311  * @constructor
2312  * Create a new MenuItem
2313  * @param {Object} config The config object
2314  */
2315
2316
2317 Roo.bootstrap.MenuItem = function(config){
2318     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2319     this.addEvents({
2320         // raw events
2321         /**
2322          * @event click
2323          * The raw click event for the entire grid.
2324          * @param {Roo.bootstrap.MenuItem} this
2325          * @param {Roo.EventObject} e
2326          */
2327         "click" : true
2328     });
2329 };
2330
2331 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2332     
2333     href : false,
2334     html : false,
2335     preventDefault: false,
2336     isContainer : false,
2337     active : false,
2338     fa: false,
2339     
2340     getAutoCreate : function(){
2341         
2342         if(this.isContainer){
2343             return {
2344                 tag: 'li',
2345                 cls: 'dropdown-menu-item'
2346             };
2347         }
2348         var ctag = {
2349             tag: 'span',
2350             html: 'Link'
2351         };
2352         
2353         var anc = {
2354             tag : 'a',
2355             href : '#',
2356             cn : [  ]
2357         };
2358         
2359         if (this.fa !== false) {
2360             anc.cn.push({
2361                 tag : 'i',
2362                 cls : 'fa fa-' + this.fa
2363             });
2364         }
2365         
2366         anc.cn.push(ctag);
2367         
2368         
2369         var cfg= {
2370             tag: 'li',
2371             cls: 'dropdown-menu-item',
2372             cn: [ anc ]
2373         };
2374         if (this.parent().type == 'treeview') {
2375             cfg.cls = 'treeview-menu';
2376         }
2377         if (this.active) {
2378             cfg.cls += ' active';
2379         }
2380         
2381         
2382         
2383         anc.href = this.href || cfg.cn[0].href ;
2384         ctag.html = this.html || cfg.cn[0].html ;
2385         return cfg;
2386     },
2387     
2388     initEvents: function()
2389     {
2390         if (this.parent().type == 'treeview') {
2391             this.el.select('a').on('click', this.onClick, this);
2392         }
2393         if (this.menu) {
2394             this.menu.parentType = this.xtype;
2395             this.menu.triggerEl = this.el;
2396             this.menu = this.addxtype(Roo.apply({}, this.menu));
2397         }
2398         
2399     },
2400     onClick : function(e)
2401     {
2402         Roo.log('item on click ');
2403         
2404         if(this.preventDefault){
2405             e.preventDefault();
2406         }
2407         //this.parent().hideMenuItems();
2408         
2409         this.fireEvent('click', this, e);
2410     },
2411     getEl : function()
2412     {
2413         return this.el;
2414     } 
2415 });
2416
2417  
2418
2419  /*
2420  * - LGPL
2421  *
2422  * menu separator
2423  * 
2424  */
2425
2426
2427 /**
2428  * @class Roo.bootstrap.MenuSeparator
2429  * @extends Roo.bootstrap.Component
2430  * Bootstrap MenuSeparator class
2431  * 
2432  * @constructor
2433  * Create a new MenuItem
2434  * @param {Object} config The config object
2435  */
2436
2437
2438 Roo.bootstrap.MenuSeparator = function(config){
2439     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2440 };
2441
2442 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2443     
2444     getAutoCreate : function(){
2445         var cfg = {
2446             cls: 'divider',
2447             tag : 'li'
2448         };
2449         
2450         return cfg;
2451     }
2452    
2453 });
2454
2455  
2456
2457  
2458 /*
2459 * Licence: LGPL
2460 */
2461
2462 /**
2463  * @class Roo.bootstrap.Modal
2464  * @extends Roo.bootstrap.Component
2465  * Bootstrap Modal class
2466  * @cfg {String} title Title of dialog
2467  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2468  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2469  * @cfg {Boolean} specificTitle default false
2470  * @cfg {Array} buttons Array of buttons or standard button set..
2471  * @cfg {String} buttonPosition (left|right|center) default right
2472  * @cfg {Boolean} animate default true
2473  * @cfg {Boolean} allow_close default true
2474  * @cfg {Boolean} fitwindow default false
2475  * @cfg {String} size (sm|lg) default empty
2476  *
2477  *
2478  * @constructor
2479  * Create a new Modal Dialog
2480  * @param {Object} config The config object
2481  */
2482
2483 Roo.bootstrap.Modal = function(config){
2484     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2485     this.addEvents({
2486         // raw events
2487         /**
2488          * @event btnclick
2489          * The raw btnclick event for the button
2490          * @param {Roo.EventObject} e
2491          */
2492         "btnclick" : true
2493     });
2494     this.buttons = this.buttons || [];
2495
2496     if (this.tmpl) {
2497         this.tmpl = Roo.factory(this.tmpl);
2498     }
2499
2500 };
2501
2502 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2503
2504     title : 'test dialog',
2505
2506     buttons : false,
2507
2508     // set on load...
2509
2510     html: false,
2511
2512     tmp: false,
2513
2514     specificTitle: false,
2515
2516     buttonPosition: 'right',
2517
2518     allow_close : true,
2519
2520     animate : true,
2521
2522     fitwindow: false,
2523
2524
2525      // private
2526     dialogEl: false,
2527     bodyEl:  false,
2528     footerEl:  false,
2529     titleEl:  false,
2530     closeEl:  false,
2531
2532     size: '',
2533
2534
2535     onRender : function(ct, position)
2536     {
2537         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2538
2539         if(!this.el){
2540             var cfg = Roo.apply({},  this.getAutoCreate());
2541             cfg.id = Roo.id();
2542             //if(!cfg.name){
2543             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2544             //}
2545             //if (!cfg.name.length) {
2546             //    delete cfg.name;
2547            // }
2548             if (this.cls) {
2549                 cfg.cls += ' ' + this.cls;
2550             }
2551             if (this.style) {
2552                 cfg.style = this.style;
2553             }
2554             this.el = Roo.get(document.body).createChild(cfg, position);
2555         }
2556         //var type = this.el.dom.type;
2557
2558
2559         if(this.tabIndex !== undefined){
2560             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2561         }
2562
2563         this.dialogEl = this.el.select('.modal-dialog',true).first();
2564         this.bodyEl = this.el.select('.modal-body',true).first();
2565         this.closeEl = this.el.select('.modal-header .close', true).first();
2566         this.footerEl = this.el.select('.modal-footer',true).first();
2567         this.titleEl = this.el.select('.modal-title',true).first();
2568
2569
2570
2571         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2572         this.maskEl.enableDisplayMode("block");
2573         this.maskEl.hide();
2574         //this.el.addClass("x-dlg-modal");
2575
2576         if (this.buttons.length) {
2577             Roo.each(this.buttons, function(bb) {
2578                 var b = Roo.apply({}, bb);
2579                 b.xns = b.xns || Roo.bootstrap;
2580                 b.xtype = b.xtype || 'Button';
2581                 if (typeof(b.listeners) == 'undefined') {
2582                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2583                 }
2584
2585                 var btn = Roo.factory(b);
2586
2587                 btn.render(this.el.select('.modal-footer div').first());
2588
2589             },this);
2590         }
2591         // render the children.
2592         var nitems = [];
2593
2594         if(typeof(this.items) != 'undefined'){
2595             var items = this.items;
2596             delete this.items;
2597
2598             for(var i =0;i < items.length;i++) {
2599                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2600             }
2601         }
2602
2603         this.items = nitems;
2604
2605         // where are these used - they used to be body/close/footer
2606
2607
2608         this.initEvents();
2609         //this.el.addClass([this.fieldClass, this.cls]);
2610
2611     },
2612
2613     getAutoCreate : function(){
2614
2615
2616         var bdy = {
2617                 cls : 'modal-body',
2618                 html : this.html || ''
2619         };
2620
2621         var title = {
2622             tag: 'h4',
2623             cls : 'modal-title',
2624             html : this.title
2625         };
2626
2627         if(this.specificTitle){
2628             title = this.title;
2629
2630         };
2631
2632         var header = [];
2633         if (this.allow_close) {
2634             header.push({
2635                 tag: 'button',
2636                 cls : 'close',
2637                 html : '&times'
2638             });
2639         }
2640
2641         header.push(title);
2642
2643         var size = '';
2644
2645         if(this.size.length){
2646             size = 'modal-' + this.size;
2647         }
2648
2649         var modal = {
2650             cls: "modal",
2651             style : 'display: none',
2652             cn : [
2653                 {
2654                     cls: "modal-dialog " + size,
2655                     cn : [
2656                         {
2657                             cls : "modal-content",
2658                             cn : [
2659                                 {
2660                                     cls : 'modal-header',
2661                                     cn : header
2662                                 },
2663                                 bdy,
2664                                 {
2665                                     cls : 'modal-footer',
2666                                     cn : [
2667                                         {
2668                                             tag: 'div',
2669                                             cls: 'btn-' + this.buttonPosition
2670                                         }
2671                                     ]
2672
2673                                 }
2674
2675
2676                             ]
2677
2678                         }
2679                     ]
2680
2681                 }
2682             ]
2683         };
2684
2685         if(this.animate){
2686             modal.cls += ' fade';
2687         }
2688
2689         return modal;
2690
2691     },
2692     getChildContainer : function() {
2693
2694          return this.bodyEl;
2695
2696     },
2697     getButtonContainer : function() {
2698          return this.el.select('.modal-footer div',true).first();
2699
2700     },
2701     initEvents : function()
2702     {
2703         if (this.allow_close) {
2704             this.closeEl.on('click', this.hide, this);
2705         }
2706         Roo.EventManager.onWindowResize(this.resize, this, true);
2707
2708
2709     },
2710
2711     resize : function()
2712     {
2713         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2714         if (this.fitwindow) {
2715             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2716             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2717             this.setSize(w,h);
2718         }
2719     },
2720
2721     setSize : function(w,h)
2722     {
2723         if (!w && !h) {
2724             return;
2725         }
2726         this.resizeTo(w,h);
2727     },
2728
2729     show : function() {
2730
2731         if (!this.rendered) {
2732             this.render();
2733         }
2734
2735         this.el.setStyle('display', 'block');
2736
2737         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2738             var _this = this;
2739             (function(){
2740                 this.el.addClass('in');
2741             }).defer(50, this);
2742         }else{
2743             this.el.addClass('in');
2744
2745         }
2746
2747         // not sure how we can show data in here..
2748         //if (this.tmpl) {
2749         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2750         //}
2751
2752         Roo.get(document.body).addClass("x-body-masked");
2753         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2754         this.maskEl.show();
2755         this.el.setStyle('zIndex', '10001');
2756
2757         this.fireEvent('show', this);
2758
2759         this.resize();
2760
2761         (function () {
2762             this.items.forEach( function(e) {
2763                 e.layout ? e.layout() : false;
2764
2765             });
2766         }).defer(100,this);
2767
2768     },
2769     hide : function()
2770     {
2771         if(this.fireEvent("beforehide", this) !== false){
2772             this.maskEl.hide();
2773             Roo.get(document.body).removeClass("x-body-masked");
2774             this.el.removeClass('in');
2775             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2776
2777             if(this.animate){ // why
2778                 var _this = this;
2779                 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2780             }else{
2781                 this.el.setStyle('display', 'none');
2782             }
2783             this.fireEvent('hide', this);
2784         }
2785     },
2786
2787     addButton : function(str, cb)
2788     {
2789
2790
2791         var b = Roo.apply({}, { html : str } );
2792         b.xns = b.xns || Roo.bootstrap;
2793         b.xtype = b.xtype || 'Button';
2794         if (typeof(b.listeners) == 'undefined') {
2795             b.listeners = { click : cb.createDelegate(this)  };
2796         }
2797
2798         var btn = Roo.factory(b);
2799
2800         btn.render(this.el.select('.modal-footer div').first());
2801
2802         return btn;
2803
2804     },
2805
2806     setDefaultButton : function(btn)
2807     {
2808         //this.el.select('.modal-footer').()
2809     },
2810     diff : false,
2811
2812     resizeTo: function(w,h)
2813     {
2814         // skip.. ?? why??
2815
2816         this.dialogEl.setWidth(w);
2817         if (this.diff === false) {
2818             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2819         }
2820
2821         this.bodyEl.setHeight(h-this.diff);
2822
2823
2824     },
2825     setContentSize  : function(w, h)
2826     {
2827
2828     },
2829     onButtonClick: function(btn,e)
2830     {
2831         //Roo.log([a,b,c]);
2832         this.fireEvent('btnclick', btn.name, e);
2833     },
2834      /**
2835      * Set the title of the Dialog
2836      * @param {String} str new Title
2837      */
2838     setTitle: function(str) {
2839         this.titleEl.dom.innerHTML = str;
2840     },
2841     /**
2842      * Set the body of the Dialog
2843      * @param {String} str new Title
2844      */
2845     setBody: function(str) {
2846         this.bodyEl.dom.innerHTML = str;
2847     },
2848     /**
2849      * Set the body of the Dialog using the template
2850      * @param {Obj} data - apply this data to the template and replace the body contents.
2851      */
2852     applyBody: function(obj)
2853     {
2854         if (!this.tmpl) {
2855             Roo.log("Error - using apply Body without a template");
2856             //code
2857         }
2858         this.tmpl.overwrite(this.bodyEl, obj);
2859     }
2860
2861 });
2862
2863
2864 Roo.apply(Roo.bootstrap.Modal,  {
2865     /**
2866          * Button config that displays a single OK button
2867          * @type Object
2868          */
2869         OK :  [{
2870             name : 'ok',
2871             weight : 'primary',
2872             html : 'OK'
2873         }],
2874         /**
2875          * Button config that displays Yes and No buttons
2876          * @type Object
2877          */
2878         YESNO : [
2879             {
2880                 name  : 'no',
2881                 html : 'No'
2882             },
2883             {
2884                 name  :'yes',
2885                 weight : 'primary',
2886                 html : 'Yes'
2887             }
2888         ],
2889
2890         /**
2891          * Button config that displays OK and Cancel buttons
2892          * @type Object
2893          */
2894         OKCANCEL : [
2895             {
2896                name : 'cancel',
2897                 html : 'Cancel'
2898             },
2899             {
2900                 name : 'ok',
2901                 weight : 'primary',
2902                 html : 'OK'
2903             }
2904         ],
2905         /**
2906          * Button config that displays Yes, No and Cancel buttons
2907          * @type Object
2908          */
2909         YESNOCANCEL : [
2910             {
2911                 name : 'yes',
2912                 weight : 'primary',
2913                 html : 'Yes'
2914             },
2915             {
2916                 name : 'no',
2917                 html : 'No'
2918             },
2919             {
2920                 name : 'cancel',
2921                 html : 'Cancel'
2922             }
2923         ]
2924 });
2925 /*
2926  * - LGPL
2927  *
2928  * messagebox - can be used as a replace
2929  * 
2930  */
2931 /**
2932  * @class Roo.MessageBox
2933  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2934  * Example usage:
2935  *<pre><code>
2936 // Basic alert:
2937 Roo.Msg.alert('Status', 'Changes saved successfully.');
2938
2939 // Prompt for user data:
2940 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2941     if (btn == 'ok'){
2942         // process text value...
2943     }
2944 });
2945
2946 // Show a dialog using config options:
2947 Roo.Msg.show({
2948    title:'Save Changes?',
2949    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2950    buttons: Roo.Msg.YESNOCANCEL,
2951    fn: processResult,
2952    animEl: 'elId'
2953 });
2954 </code></pre>
2955  * @singleton
2956  */
2957 Roo.bootstrap.MessageBox = function(){
2958     var dlg, opt, mask, waitTimer;
2959     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2960     var buttons, activeTextEl, bwidth;
2961
2962     
2963     // private
2964     var handleButton = function(button){
2965         dlg.hide();
2966         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2967     };
2968
2969     // private
2970     var handleHide = function(){
2971         if(opt && opt.cls){
2972             dlg.el.removeClass(opt.cls);
2973         }
2974         //if(waitTimer){
2975         //    Roo.TaskMgr.stop(waitTimer);
2976         //    waitTimer = null;
2977         //}
2978     };
2979
2980     // private
2981     var updateButtons = function(b){
2982         var width = 0;
2983         if(!b){
2984             buttons["ok"].hide();
2985             buttons["cancel"].hide();
2986             buttons["yes"].hide();
2987             buttons["no"].hide();
2988             //dlg.footer.dom.style.display = 'none';
2989             return width;
2990         }
2991         dlg.footerEl.dom.style.display = '';
2992         for(var k in buttons){
2993             if(typeof buttons[k] != "function"){
2994                 if(b[k]){
2995                     buttons[k].show();
2996                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2997                     width += buttons[k].el.getWidth()+15;
2998                 }else{
2999                     buttons[k].hide();
3000                 }
3001             }
3002         }
3003         return width;
3004     };
3005
3006     // private
3007     var handleEsc = function(d, k, e){
3008         if(opt && opt.closable !== false){
3009             dlg.hide();
3010         }
3011         if(e){
3012             e.stopEvent();
3013         }
3014     };
3015
3016     return {
3017         /**
3018          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3019          * @return {Roo.BasicDialog} The BasicDialog element
3020          */
3021         getDialog : function(){
3022            if(!dlg){
3023                 dlg = new Roo.bootstrap.Modal( {
3024                     //draggable: true,
3025                     //resizable:false,
3026                     //constraintoviewport:false,
3027                     //fixedcenter:true,
3028                     //collapsible : false,
3029                     //shim:true,
3030                     //modal: true,
3031                   //  width:400,
3032                   //  height:100,
3033                     //buttonAlign:"center",
3034                     closeClick : function(){
3035                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3036                             handleButton("no");
3037                         }else{
3038                             handleButton("cancel");
3039                         }
3040                     }
3041                 });
3042                 dlg.render();
3043                 dlg.on("hide", handleHide);
3044                 mask = dlg.mask;
3045                 //dlg.addKeyListener(27, handleEsc);
3046                 buttons = {};
3047                 this.buttons = buttons;
3048                 var bt = this.buttonText;
3049                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3050                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3051                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3052                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3053                 //Roo.log(buttons);
3054                 bodyEl = dlg.bodyEl.createChild({
3055
3056                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3057                         '<textarea class="roo-mb-textarea"></textarea>' +
3058                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3059                 });
3060                 msgEl = bodyEl.dom.firstChild;
3061                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3062                 textboxEl.enableDisplayMode();
3063                 textboxEl.addKeyListener([10,13], function(){
3064                     if(dlg.isVisible() && opt && opt.buttons){
3065                         if(opt.buttons.ok){
3066                             handleButton("ok");
3067                         }else if(opt.buttons.yes){
3068                             handleButton("yes");
3069                         }
3070                     }
3071                 });
3072                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3073                 textareaEl.enableDisplayMode();
3074                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3075                 progressEl.enableDisplayMode();
3076                 var pf = progressEl.dom.firstChild;
3077                 if (pf) {
3078                     pp = Roo.get(pf.firstChild);
3079                     pp.setHeight(pf.offsetHeight);
3080                 }
3081                 
3082             }
3083             return dlg;
3084         },
3085
3086         /**
3087          * Updates the message box body text
3088          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3089          * the XHTML-compliant non-breaking space character '&amp;#160;')
3090          * @return {Roo.MessageBox} This message box
3091          */
3092         updateText : function(text){
3093             if(!dlg.isVisible() && !opt.width){
3094                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
3095             }
3096             msgEl.innerHTML = text || '&#160;';
3097       
3098             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3099             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3100             var w = Math.max(
3101                     Math.min(opt.width || cw , this.maxWidth), 
3102                     Math.max(opt.minWidth || this.minWidth, bwidth)
3103             );
3104             if(opt.prompt){
3105                 activeTextEl.setWidth(w);
3106             }
3107             if(dlg.isVisible()){
3108                 dlg.fixedcenter = false;
3109             }
3110             // to big, make it scroll. = But as usual stupid IE does not support
3111             // !important..
3112             
3113             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3114                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3115                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3116             } else {
3117                 bodyEl.dom.style.height = '';
3118                 bodyEl.dom.style.overflowY = '';
3119             }
3120             if (cw > w) {
3121                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3122             } else {
3123                 bodyEl.dom.style.overflowX = '';
3124             }
3125             
3126             dlg.setContentSize(w, bodyEl.getHeight());
3127             if(dlg.isVisible()){
3128                 dlg.fixedcenter = true;
3129             }
3130             return this;
3131         },
3132
3133         /**
3134          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3135          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3136          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3137          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3138          * @return {Roo.MessageBox} This message box
3139          */
3140         updateProgress : function(value, text){
3141             if(text){
3142                 this.updateText(text);
3143             }
3144             if (pp) { // weird bug on my firefox - for some reason this is not defined
3145                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3146             }
3147             return this;
3148         },        
3149
3150         /**
3151          * Returns true if the message box is currently displayed
3152          * @return {Boolean} True if the message box is visible, else false
3153          */
3154         isVisible : function(){
3155             return dlg && dlg.isVisible();  
3156         },
3157
3158         /**
3159          * Hides the message box if it is displayed
3160          */
3161         hide : function(){
3162             if(this.isVisible()){
3163                 dlg.hide();
3164             }  
3165         },
3166
3167         /**
3168          * Displays a new message box, or reinitializes an existing message box, based on the config options
3169          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3170          * The following config object properties are supported:
3171          * <pre>
3172 Property    Type             Description
3173 ----------  ---------------  ------------------------------------------------------------------------------------
3174 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3175                                    closes (defaults to undefined)
3176 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3177                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3178 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3179                                    progress and wait dialogs will ignore this property and always hide the
3180                                    close button as they can only be closed programmatically.
3181 cls               String           A custom CSS class to apply to the message box element
3182 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3183                                    displayed (defaults to 75)
3184 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3185                                    function will be btn (the name of the button that was clicked, if applicable,
3186                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3187                                    Progress and wait dialogs will ignore this option since they do not respond to
3188                                    user actions and can only be closed programmatically, so any required function
3189                                    should be called by the same code after it closes the dialog.
3190 icon              String           A CSS class that provides a background image to be used as an icon for
3191                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3192 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3193 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3194 modal             Boolean          False to allow user interaction with the page while the message box is
3195                                    displayed (defaults to true)
3196 msg               String           A string that will replace the existing message box body text (defaults
3197                                    to the XHTML-compliant non-breaking space character '&#160;')
3198 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3199 progress          Boolean          True to display a progress bar (defaults to false)
3200 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3201 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3202 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3203 title             String           The title text
3204 value             String           The string value to set into the active textbox element if displayed
3205 wait              Boolean          True to display a progress bar (defaults to false)
3206 width             Number           The width of the dialog in pixels
3207 </pre>
3208          *
3209          * Example usage:
3210          * <pre><code>
3211 Roo.Msg.show({
3212    title: 'Address',
3213    msg: 'Please enter your address:',
3214    width: 300,
3215    buttons: Roo.MessageBox.OKCANCEL,
3216    multiline: true,
3217    fn: saveAddress,
3218    animEl: 'addAddressBtn'
3219 });
3220 </code></pre>
3221          * @param {Object} config Configuration options
3222          * @return {Roo.MessageBox} This message box
3223          */
3224         show : function(options)
3225         {
3226             
3227             // this causes nightmares if you show one dialog after another
3228             // especially on callbacks..
3229              
3230             if(this.isVisible()){
3231                 
3232                 this.hide();
3233                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3234                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3235                 Roo.log("New Dialog Message:" +  options.msg )
3236                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3237                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3238                 
3239             }
3240             var d = this.getDialog();
3241             opt = options;
3242             d.setTitle(opt.title || "&#160;");
3243             d.closeEl.setDisplayed(opt.closable !== false);
3244             activeTextEl = textboxEl;
3245             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3246             if(opt.prompt){
3247                 if(opt.multiline){
3248                     textboxEl.hide();
3249                     textareaEl.show();
3250                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3251                         opt.multiline : this.defaultTextHeight);
3252                     activeTextEl = textareaEl;
3253                 }else{
3254                     textboxEl.show();
3255                     textareaEl.hide();
3256                 }
3257             }else{
3258                 textboxEl.hide();
3259                 textareaEl.hide();
3260             }
3261             progressEl.setDisplayed(opt.progress === true);
3262             this.updateProgress(0);
3263             activeTextEl.dom.value = opt.value || "";
3264             if(opt.prompt){
3265                 dlg.setDefaultButton(activeTextEl);
3266             }else{
3267                 var bs = opt.buttons;
3268                 var db = null;
3269                 if(bs && bs.ok){
3270                     db = buttons["ok"];
3271                 }else if(bs && bs.yes){
3272                     db = buttons["yes"];
3273                 }
3274                 dlg.setDefaultButton(db);
3275             }
3276             bwidth = updateButtons(opt.buttons);
3277             this.updateText(opt.msg);
3278             if(opt.cls){
3279                 d.el.addClass(opt.cls);
3280             }
3281             d.proxyDrag = opt.proxyDrag === true;
3282             d.modal = opt.modal !== false;
3283             d.mask = opt.modal !== false ? mask : false;
3284             if(!d.isVisible()){
3285                 // force it to the end of the z-index stack so it gets a cursor in FF
3286                 document.body.appendChild(dlg.el.dom);
3287                 d.animateTarget = null;
3288                 d.show(options.animEl);
3289             }
3290             return this;
3291         },
3292
3293         /**
3294          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3295          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3296          * and closing the message box when the process is complete.
3297          * @param {String} title The title bar text
3298          * @param {String} msg The message box body text
3299          * @return {Roo.MessageBox} This message box
3300          */
3301         progress : function(title, msg){
3302             this.show({
3303                 title : title,
3304                 msg : msg,
3305                 buttons: false,
3306                 progress:true,
3307                 closable:false,
3308                 minWidth: this.minProgressWidth,
3309                 modal : true
3310             });
3311             return this;
3312         },
3313
3314         /**
3315          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3316          * If a callback function is passed it will be called after the user clicks the button, and the
3317          * id of the button that was clicked will be passed as the only parameter to the callback
3318          * (could also be the top-right close button).
3319          * @param {String} title The title bar text
3320          * @param {String} msg The message box body text
3321          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3322          * @param {Object} scope (optional) The scope of the callback function
3323          * @return {Roo.MessageBox} This message box
3324          */
3325         alert : function(title, msg, fn, scope){
3326             this.show({
3327                 title : title,
3328                 msg : msg,
3329                 buttons: this.OK,
3330                 fn: fn,
3331                 scope : scope,
3332                 modal : true
3333             });
3334             return this;
3335         },
3336
3337         /**
3338          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3339          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3340          * You are responsible for closing the message box when the process is complete.
3341          * @param {String} msg The message box body text
3342          * @param {String} title (optional) The title bar text
3343          * @return {Roo.MessageBox} This message box
3344          */
3345         wait : function(msg, title){
3346             this.show({
3347                 title : title,
3348                 msg : msg,
3349                 buttons: false,
3350                 closable:false,
3351                 progress:true,
3352                 modal:true,
3353                 width:300,
3354                 wait:true
3355             });
3356             waitTimer = Roo.TaskMgr.start({
3357                 run: function(i){
3358                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3359                 },
3360                 interval: 1000
3361             });
3362             return this;
3363         },
3364
3365         /**
3366          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3367          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3368          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3369          * @param {String} title The title bar text
3370          * @param {String} msg The message box body text
3371          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3372          * @param {Object} scope (optional) The scope of the callback function
3373          * @return {Roo.MessageBox} This message box
3374          */
3375         confirm : function(title, msg, fn, scope){
3376             this.show({
3377                 title : title,
3378                 msg : msg,
3379                 buttons: this.YESNO,
3380                 fn: fn,
3381                 scope : scope,
3382                 modal : true
3383             });
3384             return this;
3385         },
3386
3387         /**
3388          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3389          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3390          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3391          * (could also be the top-right close button) and the text that was entered will be passed as the two
3392          * parameters to the callback.
3393          * @param {String} title The title bar text
3394          * @param {String} msg The message box body text
3395          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3396          * @param {Object} scope (optional) The scope of the callback function
3397          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3398          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3399          * @return {Roo.MessageBox} This message box
3400          */
3401         prompt : function(title, msg, fn, scope, multiline){
3402             this.show({
3403                 title : title,
3404                 msg : msg,
3405                 buttons: this.OKCANCEL,
3406                 fn: fn,
3407                 minWidth:250,
3408                 scope : scope,
3409                 prompt:true,
3410                 multiline: multiline,
3411                 modal : true
3412             });
3413             return this;
3414         },
3415
3416         /**
3417          * Button config that displays a single OK button
3418          * @type Object
3419          */
3420         OK : {ok:true},
3421         /**
3422          * Button config that displays Yes and No buttons
3423          * @type Object
3424          */
3425         YESNO : {yes:true, no:true},
3426         /**
3427          * Button config that displays OK and Cancel buttons
3428          * @type Object
3429          */
3430         OKCANCEL : {ok:true, cancel:true},
3431         /**
3432          * Button config that displays Yes, No and Cancel buttons
3433          * @type Object
3434          */
3435         YESNOCANCEL : {yes:true, no:true, cancel:true},
3436
3437         /**
3438          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3439          * @type Number
3440          */
3441         defaultTextHeight : 75,
3442         /**
3443          * The maximum width in pixels of the message box (defaults to 600)
3444          * @type Number
3445          */
3446         maxWidth : 600,
3447         /**
3448          * The minimum width in pixels of the message box (defaults to 100)
3449          * @type Number
3450          */
3451         minWidth : 100,
3452         /**
3453          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3454          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3455          * @type Number
3456          */
3457         minProgressWidth : 250,
3458         /**
3459          * An object containing the default button text strings that can be overriden for localized language support.
3460          * Supported properties are: ok, cancel, yes and no.
3461          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3462          * @type Object
3463          */
3464         buttonText : {
3465             ok : "OK",
3466             cancel : "Cancel",
3467             yes : "Yes",
3468             no : "No"
3469         }
3470     };
3471 }();
3472
3473 /**
3474  * Shorthand for {@link Roo.MessageBox}
3475  */
3476 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3477 Roo.Msg = Roo.Msg || Roo.MessageBox;
3478 /*
3479  * - LGPL
3480  *
3481  * navbar
3482  * 
3483  */
3484
3485 /**
3486  * @class Roo.bootstrap.Navbar
3487  * @extends Roo.bootstrap.Component
3488  * Bootstrap Navbar class
3489
3490  * @constructor
3491  * Create a new Navbar
3492  * @param {Object} config The config object
3493  */
3494
3495
3496 Roo.bootstrap.Navbar = function(config){
3497     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3498     this.addEvents({
3499         // raw events
3500         /**
3501          * @event beforetoggle
3502          * Fire before toggle the menu
3503          * @param {Roo.EventObject} e
3504          */
3505         "beforetoggle" : true
3506     });
3507 };
3508
3509 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3510     
3511     
3512    
3513     // private
3514     navItems : false,
3515     loadMask : false,
3516     
3517     
3518     getAutoCreate : function(){
3519         
3520         
3521         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3522         
3523     },
3524     
3525     initEvents :function ()
3526     {
3527         //Roo.log(this.el.select('.navbar-toggle',true));
3528         this.el.select('.navbar-toggle',true).on('click', function() {
3529             if(this.fireEvent('beforetoggle', this) !== false){
3530                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3531             }
3532             
3533         }, this);
3534         
3535         var mark = {
3536             tag: "div",
3537             cls:"x-dlg-mask"
3538         };
3539         
3540         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3541         
3542         var size = this.el.getSize();
3543         this.maskEl.setSize(size.width, size.height);
3544         this.maskEl.enableDisplayMode("block");
3545         this.maskEl.hide();
3546         
3547         if(this.loadMask){
3548             this.maskEl.show();
3549         }
3550     },
3551     
3552     
3553     getChildContainer : function()
3554     {
3555         if (this.el.select('.collapse').getCount()) {
3556             return this.el.select('.collapse',true).first();
3557         }
3558         
3559         return this.el;
3560     },
3561     
3562     mask : function()
3563     {
3564         this.maskEl.show();
3565     },
3566     
3567     unmask : function()
3568     {
3569         this.maskEl.hide();
3570     } 
3571     
3572     
3573     
3574     
3575 });
3576
3577
3578
3579  
3580
3581  /*
3582  * - LGPL
3583  *
3584  * navbar
3585  * 
3586  */
3587
3588 /**
3589  * @class Roo.bootstrap.NavSimplebar
3590  * @extends Roo.bootstrap.Navbar
3591  * Bootstrap Sidebar class
3592  *
3593  * @cfg {Boolean} inverse is inverted color
3594  * 
3595  * @cfg {String} type (nav | pills | tabs)
3596  * @cfg {Boolean} arrangement stacked | justified
3597  * @cfg {String} align (left | right) alignment
3598  * 
3599  * @cfg {Boolean} main (true|false) main nav bar? default false
3600  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3601  * 
3602  * @cfg {String} tag (header|footer|nav|div) default is nav 
3603
3604  * 
3605  * 
3606  * 
3607  * @constructor
3608  * Create a new Sidebar
3609  * @param {Object} config The config object
3610  */
3611
3612
3613 Roo.bootstrap.NavSimplebar = function(config){
3614     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3615 };
3616
3617 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3618     
3619     inverse: false,
3620     
3621     type: false,
3622     arrangement: '',
3623     align : false,
3624     
3625     
3626     
3627     main : false,
3628     
3629     
3630     tag : false,
3631     
3632     
3633     getAutoCreate : function(){
3634         
3635         
3636         var cfg = {
3637             tag : this.tag || 'div',
3638             cls : 'navbar'
3639         };
3640           
3641         
3642         cfg.cn = [
3643             {
3644                 cls: 'nav',
3645                 tag : 'ul'
3646             }
3647         ];
3648         
3649          
3650         this.type = this.type || 'nav';
3651         if (['tabs','pills'].indexOf(this.type)!==-1) {
3652             cfg.cn[0].cls += ' nav-' + this.type
3653         
3654         
3655         } else {
3656             if (this.type!=='nav') {
3657                 Roo.log('nav type must be nav/tabs/pills')
3658             }
3659             cfg.cn[0].cls += ' navbar-nav'
3660         }
3661         
3662         
3663         
3664         
3665         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3666             cfg.cn[0].cls += ' nav-' + this.arrangement;
3667         }
3668         
3669         
3670         if (this.align === 'right') {
3671             cfg.cn[0].cls += ' navbar-right';
3672         }
3673         
3674         if (this.inverse) {
3675             cfg.cls += ' navbar-inverse';
3676             
3677         }
3678         
3679         
3680         return cfg;
3681     
3682         
3683     }
3684     
3685     
3686     
3687 });
3688
3689
3690
3691  
3692
3693  
3694        /*
3695  * - LGPL
3696  *
3697  * navbar
3698  * 
3699  */
3700
3701 /**
3702  * @class Roo.bootstrap.NavHeaderbar
3703  * @extends Roo.bootstrap.NavSimplebar
3704  * Bootstrap Sidebar class
3705  *
3706  * @cfg {String} brand what is brand
3707  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3708  * @cfg {String} brand_href href of the brand
3709  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3710  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3711  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3712  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3713  * 
3714  * @constructor
3715  * Create a new Sidebar
3716  * @param {Object} config The config object
3717  */
3718
3719
3720 Roo.bootstrap.NavHeaderbar = function(config){
3721     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3722       
3723 };
3724
3725 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3726     
3727     position: '',
3728     brand: '',
3729     brand_href: false,
3730     srButton : true,
3731     autohide : false,
3732     desktopCenter : false,
3733    
3734     
3735     getAutoCreate : function(){
3736         
3737         var   cfg = {
3738             tag: this.nav || 'nav',
3739             cls: 'navbar',
3740             role: 'navigation',
3741             cn: []
3742         };
3743         
3744         var cn = cfg.cn;
3745         if (this.desktopCenter) {
3746             cn.push({cls : 'container', cn : []});
3747             cn = cn[0].cn;
3748         }
3749         
3750         if(this.srButton){
3751             cn.push({
3752                 tag: 'div',
3753                 cls: 'navbar-header',
3754                 cn: [
3755                     {
3756                         tag: 'button',
3757                         type: 'button',
3758                         cls: 'navbar-toggle',
3759                         'data-toggle': 'collapse',
3760                         cn: [
3761                             {
3762                                 tag: 'span',
3763                                 cls: 'sr-only',
3764                                 html: 'Toggle navigation'
3765                             },
3766                             {
3767                                 tag: 'span',
3768                                 cls: 'icon-bar'
3769                             },
3770                             {
3771                                 tag: 'span',
3772                                 cls: 'icon-bar'
3773                             },
3774                             {
3775                                 tag: 'span',
3776                                 cls: 'icon-bar'
3777                             }
3778                         ]
3779                     }
3780                 ]
3781             });
3782         }
3783         
3784         cn.push({
3785             tag: 'div',
3786             cls: 'collapse navbar-collapse',
3787             cn : []
3788         });
3789         
3790         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3791         
3792         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3793             cfg.cls += ' navbar-' + this.position;
3794             
3795             // tag can override this..
3796             
3797             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3798         }
3799         
3800         if (this.brand !== '') {
3801             cn[0].cn.push({
3802                 tag: 'a',
3803                 href: this.brand_href ? this.brand_href : '#',
3804                 cls: 'navbar-brand',
3805                 cn: [
3806                 this.brand
3807                 ]
3808             });
3809         }
3810         
3811         if(this.main){
3812             cfg.cls += ' main-nav';
3813         }
3814         
3815         
3816         return cfg;
3817
3818         
3819     },
3820     getHeaderChildContainer : function()
3821     {
3822         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3823             return this.el.select('.navbar-header',true).first();
3824         }
3825         
3826         return this.getChildContainer();
3827     },
3828     
3829     
3830     initEvents : function()
3831     {
3832         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3833         
3834         if (this.autohide) {
3835             
3836             var prevScroll = 0;
3837             var ft = this.el;
3838             
3839             Roo.get(document).on('scroll',function(e) {
3840                 var ns = Roo.get(document).getScroll().top;
3841                 var os = prevScroll;
3842                 prevScroll = ns;
3843                 
3844                 if(ns > os){
3845                     ft.removeClass('slideDown');
3846                     ft.addClass('slideUp');
3847                     return;
3848                 }
3849                 ft.removeClass('slideUp');
3850                 ft.addClass('slideDown');
3851                  
3852               
3853           },this);
3854         }
3855     }    
3856     
3857 });
3858
3859
3860
3861  
3862
3863  /*
3864  * - LGPL
3865  *
3866  * navbar
3867  * 
3868  */
3869
3870 /**
3871  * @class Roo.bootstrap.NavSidebar
3872  * @extends Roo.bootstrap.Navbar
3873  * Bootstrap Sidebar class
3874  * 
3875  * @constructor
3876  * Create a new Sidebar
3877  * @param {Object} config The config object
3878  */
3879
3880
3881 Roo.bootstrap.NavSidebar = function(config){
3882     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3883 };
3884
3885 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3886     
3887     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3888     
3889     getAutoCreate : function(){
3890         
3891         
3892         return  {
3893             tag: 'div',
3894             cls: 'sidebar sidebar-nav'
3895         };
3896     
3897         
3898     }
3899     
3900     
3901     
3902 });
3903
3904
3905
3906  
3907
3908  /*
3909  * - LGPL
3910  *
3911  * nav group
3912  * 
3913  */
3914
3915 /**
3916  * @class Roo.bootstrap.NavGroup
3917  * @extends Roo.bootstrap.Component
3918  * Bootstrap NavGroup class
3919  * @cfg {String} align (left|right)
3920  * @cfg {Boolean} inverse
3921  * @cfg {String} type (nav|pills|tab) default nav
3922  * @cfg {String} navId - reference Id for navbar.
3923
3924  * 
3925  * @constructor
3926  * Create a new nav group
3927  * @param {Object} config The config object
3928  */
3929
3930 Roo.bootstrap.NavGroup = function(config){
3931     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3932     this.navItems = [];
3933    
3934     Roo.bootstrap.NavGroup.register(this);
3935      this.addEvents({
3936         /**
3937              * @event changed
3938              * Fires when the active item changes
3939              * @param {Roo.bootstrap.NavGroup} this
3940              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3941              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3942          */
3943         'changed': true
3944      });
3945     
3946 };
3947
3948 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3949     
3950     align: '',
3951     inverse: false,
3952     form: false,
3953     type: 'nav',
3954     navId : '',
3955     // private
3956     
3957     navItems : false, 
3958     
3959     getAutoCreate : function()
3960     {
3961         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3962         
3963         cfg = {
3964             tag : 'ul',
3965             cls: 'nav' 
3966         };
3967         
3968         if (['tabs','pills'].indexOf(this.type)!==-1) {
3969             cfg.cls += ' nav-' + this.type
3970         } else {
3971             if (this.type!=='nav') {
3972                 Roo.log('nav type must be nav/tabs/pills')
3973             }
3974             cfg.cls += ' navbar-nav'
3975         }
3976         
3977         if (this.parent().sidebar) {
3978             cfg = {
3979                 tag: 'ul',
3980                 cls: 'dashboard-menu sidebar-menu'
3981             };
3982             
3983             return cfg;
3984         }
3985         
3986         if (this.form === true) {
3987             cfg = {
3988                 tag: 'form',
3989                 cls: 'navbar-form'
3990             };
3991             
3992             if (this.align === 'right') {
3993                 cfg.cls += ' navbar-right';
3994             } else {
3995                 cfg.cls += ' navbar-left';
3996             }
3997         }
3998         
3999         if (this.align === 'right') {
4000             cfg.cls += ' navbar-right';
4001         }
4002         
4003         if (this.inverse) {
4004             cfg.cls += ' navbar-inverse';
4005             
4006         }
4007         
4008         
4009         return cfg;
4010     },
4011     /**
4012     * sets the active Navigation item
4013     * @param {Roo.bootstrap.NavItem} the new current navitem
4014     */
4015     setActiveItem : function(item)
4016     {
4017         var prev = false;
4018         Roo.each(this.navItems, function(v){
4019             if (v == item) {
4020                 return ;
4021             }
4022             if (v.isActive()) {
4023                 v.setActive(false, true);
4024                 prev = v;
4025                 
4026             }
4027             
4028         });
4029
4030         item.setActive(true, true);
4031         this.fireEvent('changed', this, item, prev);
4032         
4033         
4034     },
4035     /**
4036     * gets the active Navigation item
4037     * @return {Roo.bootstrap.NavItem} the current navitem
4038     */
4039     getActive : function()
4040     {
4041         
4042         var prev = false;
4043         Roo.each(this.navItems, function(v){
4044             
4045             if (v.isActive()) {
4046                 prev = v;
4047                 
4048             }
4049             
4050         });
4051         return prev;
4052     },
4053     
4054     indexOfNav : function()
4055     {
4056         
4057         var prev = false;
4058         Roo.each(this.navItems, function(v,i){
4059             
4060             if (v.isActive()) {
4061                 prev = i;
4062                 
4063             }
4064             
4065         });
4066         return prev;
4067     },
4068     /**
4069     * adds a Navigation item
4070     * @param {Roo.bootstrap.NavItem} the navitem to add
4071     */
4072     addItem : function(cfg)
4073     {
4074         var cn = new Roo.bootstrap.NavItem(cfg);
4075         this.register(cn);
4076         cn.parentId = this.id;
4077         cn.onRender(this.el, null);
4078         return cn;
4079     },
4080     /**
4081     * register a Navigation item
4082     * @param {Roo.bootstrap.NavItem} the navitem to add
4083     */
4084     register : function(item)
4085     {
4086         this.navItems.push( item);
4087         item.navId = this.navId;
4088     
4089     },
4090     
4091     /**
4092     * clear all the Navigation item
4093     */
4094    
4095     clearAll : function()
4096     {
4097         this.navItems = [];
4098         this.el.dom.innerHTML = '';
4099     },
4100     
4101     getNavItem: function(tabId)
4102     {
4103         var ret = false;
4104         Roo.each(this.navItems, function(e) {
4105             if (e.tabId == tabId) {
4106                ret =  e;
4107                return false;
4108             }
4109             return true;
4110             
4111         });
4112         return ret;
4113     },
4114     
4115     setActiveNext : function()
4116     {
4117         var i = this.indexOfNav(this.getActive());
4118         if (i > this.navItems.length) {
4119             return;
4120         }
4121         this.setActiveItem(this.navItems[i+1]);
4122     },
4123     setActivePrev : function()
4124     {
4125         var i = this.indexOfNav(this.getActive());
4126         if (i  < 1) {
4127             return;
4128         }
4129         this.setActiveItem(this.navItems[i-1]);
4130     },
4131     clearWasActive : function(except) {
4132         Roo.each(this.navItems, function(e) {
4133             if (e.tabId != except.tabId && e.was_active) {
4134                e.was_active = false;
4135                return false;
4136             }
4137             return true;
4138             
4139         });
4140     },
4141     getWasActive : function ()
4142     {
4143         var r = false;
4144         Roo.each(this.navItems, function(e) {
4145             if (e.was_active) {
4146                r = e;
4147                return false;
4148             }
4149             return true;
4150             
4151         });
4152         return r;
4153     }
4154     
4155     
4156 });
4157
4158  
4159 Roo.apply(Roo.bootstrap.NavGroup, {
4160     
4161     groups: {},
4162      /**
4163     * register a Navigation Group
4164     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4165     */
4166     register : function(navgrp)
4167     {
4168         this.groups[navgrp.navId] = navgrp;
4169         
4170     },
4171     /**
4172     * fetch a Navigation Group based on the navigation ID
4173     * @param {string} the navgroup to add
4174     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4175     */
4176     get: function(navId) {
4177         if (typeof(this.groups[navId]) == 'undefined') {
4178             return false;
4179             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4180         }
4181         return this.groups[navId] ;
4182     }
4183     
4184     
4185     
4186 });
4187
4188  /*
4189  * - LGPL
4190  *
4191  * row
4192  * 
4193  */
4194
4195 /**
4196  * @class Roo.bootstrap.NavItem
4197  * @extends Roo.bootstrap.Component
4198  * Bootstrap Navbar.NavItem class
4199  * @cfg {String} href  link to
4200  * @cfg {String} html content of button
4201  * @cfg {String} badge text inside badge
4202  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4203  * @cfg {String} glyphicon name of glyphicon
4204  * @cfg {String} icon name of font awesome icon
4205  * @cfg {Boolean} active Is item active
4206  * @cfg {Boolean} disabled Is item disabled
4207  
4208  * @cfg {Boolean} preventDefault (true | false) default false
4209  * @cfg {String} tabId the tab that this item activates.
4210  * @cfg {String} tagtype (a|span) render as a href or span?
4211  * @cfg {Boolean} animateRef (true|false) link to element default false  
4212   
4213  * @constructor
4214  * Create a new Navbar Item
4215  * @param {Object} config The config object
4216  */
4217 Roo.bootstrap.NavItem = function(config){
4218     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4219     this.addEvents({
4220         // raw events
4221         /**
4222          * @event click
4223          * The raw click event for the entire grid.
4224          * @param {Roo.EventObject} e
4225          */
4226         "click" : true,
4227          /**
4228             * @event changed
4229             * Fires when the active item active state changes
4230             * @param {Roo.bootstrap.NavItem} this
4231             * @param {boolean} state the new state
4232              
4233          */
4234         'changed': true,
4235         /**
4236             * @event scrollto
4237             * Fires when scroll to element
4238             * @param {Roo.bootstrap.NavItem} this
4239             * @param {Object} options
4240             * @param {Roo.EventObject} e
4241              
4242          */
4243         'scrollto': true
4244     });
4245    
4246 };
4247
4248 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4249     
4250     href: false,
4251     html: '',
4252     badge: '',
4253     icon: false,
4254     glyphicon: false,
4255     active: false,
4256     preventDefault : false,
4257     tabId : false,
4258     tagtype : 'a',
4259     disabled : false,
4260     animateRef : false,
4261     was_active : false,
4262     
4263     getAutoCreate : function(){
4264          
4265         var cfg = {
4266             tag: 'li',
4267             cls: 'nav-item'
4268             
4269         };
4270         
4271         if (this.active) {
4272             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4273         }
4274         if (this.disabled) {
4275             cfg.cls += ' disabled';
4276         }
4277         
4278         if (this.href || this.html || this.glyphicon || this.icon) {
4279             cfg.cn = [
4280                 {
4281                     tag: this.tagtype,
4282                     href : this.href || "#",
4283                     html: this.html || ''
4284                 }
4285             ];
4286             
4287             if (this.icon) {
4288                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4289             }
4290
4291             if(this.glyphicon) {
4292                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4293             }
4294             
4295             if (this.menu) {
4296                 
4297                 cfg.cn[0].html += " <span class='caret'></span>";
4298              
4299             }
4300             
4301             if (this.badge !== '') {
4302                  
4303                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4304             }
4305         }
4306         
4307         
4308         
4309         return cfg;
4310     },
4311     initEvents: function() 
4312     {
4313         if (typeof (this.menu) != 'undefined') {
4314             this.menu.parentType = this.xtype;
4315             this.menu.triggerEl = this.el;
4316             this.menu = this.addxtype(Roo.apply({}, this.menu));
4317         }
4318         
4319         this.el.select('a',true).on('click', this.onClick, this);
4320         
4321         if(this.tagtype == 'span'){
4322             this.el.select('span',true).on('click', this.onClick, this);
4323         }
4324        
4325         // at this point parent should be available..
4326         this.parent().register(this);
4327     },
4328     
4329     onClick : function(e)
4330     {
4331         if (e.getTarget('.dropdown-menu-item')) {
4332             // did you click on a menu itemm.... - then don't trigger onclick..
4333             return;
4334         }
4335         
4336         if(
4337                 this.preventDefault || 
4338                 this.href == '#' 
4339         ){
4340             Roo.log("NavItem - prevent Default?");
4341             e.preventDefault();
4342         }
4343         
4344         if (this.disabled) {
4345             return;
4346         }
4347         
4348         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4349         if (tg && tg.transition) {
4350             Roo.log("waiting for the transitionend");
4351             return;
4352         }
4353         
4354         
4355         
4356         //Roo.log("fire event clicked");
4357         if(this.fireEvent('click', this, e) === false){
4358             return;
4359         };
4360         
4361         if(this.tagtype == 'span'){
4362             return;
4363         }
4364         
4365         //Roo.log(this.href);
4366         var ael = this.el.select('a',true).first();
4367         //Roo.log(ael);
4368         
4369         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4370             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4371             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4372                 return; // ignore... - it's a 'hash' to another page.
4373             }
4374             Roo.log("NavItem - prevent Default?");
4375             e.preventDefault();
4376             this.scrollToElement(e);
4377         }
4378         
4379         
4380         var p =  this.parent();
4381    
4382         if (['tabs','pills'].indexOf(p.type)!==-1) {
4383             if (typeof(p.setActiveItem) !== 'undefined') {
4384                 p.setActiveItem(this);
4385             }
4386         }
4387         
4388         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4389         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4390             // remove the collapsed menu expand...
4391             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4392         }
4393     },
4394     
4395     isActive: function () {
4396         return this.active
4397     },
4398     setActive : function(state, fire, is_was_active)
4399     {
4400         if (this.active && !state && this.navId) {
4401             this.was_active = true;
4402             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4403             if (nv) {
4404                 nv.clearWasActive(this);
4405             }
4406             
4407         }
4408         this.active = state;
4409         
4410         if (!state ) {
4411             this.el.removeClass('active');
4412         } else if (!this.el.hasClass('active')) {
4413             this.el.addClass('active');
4414         }
4415         if (fire) {
4416             this.fireEvent('changed', this, state);
4417         }
4418         
4419         // show a panel if it's registered and related..
4420         
4421         if (!this.navId || !this.tabId || !state || is_was_active) {
4422             return;
4423         }
4424         
4425         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4426         if (!tg) {
4427             return;
4428         }
4429         var pan = tg.getPanelByName(this.tabId);
4430         if (!pan) {
4431             return;
4432         }
4433         // if we can not flip to new panel - go back to old nav highlight..
4434         if (false == tg.showPanel(pan)) {
4435             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4436             if (nv) {
4437                 var onav = nv.getWasActive();
4438                 if (onav) {
4439                     onav.setActive(true, false, true);
4440                 }
4441             }
4442             
4443         }
4444         
4445         
4446         
4447     },
4448      // this should not be here...
4449     setDisabled : function(state)
4450     {
4451         this.disabled = state;
4452         if (!state ) {
4453             this.el.removeClass('disabled');
4454         } else if (!this.el.hasClass('disabled')) {
4455             this.el.addClass('disabled');
4456         }
4457         
4458     },
4459     
4460     /**
4461      * Fetch the element to display the tooltip on.
4462      * @return {Roo.Element} defaults to this.el
4463      */
4464     tooltipEl : function()
4465     {
4466         return this.el.select('' + this.tagtype + '', true).first();
4467     },
4468     
4469     scrollToElement : function(e)
4470     {
4471         var c = document.body;
4472         
4473         /*
4474          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4475          */
4476         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4477             c = document.documentElement;
4478         }
4479         
4480         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4481         
4482         if(!target){
4483             return;
4484         }
4485
4486         var o = target.calcOffsetsTo(c);
4487         
4488         var options = {
4489             target : target,
4490             value : o[1]
4491         };
4492         
4493         this.fireEvent('scrollto', this, options, e);
4494         
4495         Roo.get(c).scrollTo('top', options.value, true);
4496         
4497         return;
4498     }
4499 });
4500  
4501
4502  /*
4503  * - LGPL
4504  *
4505  * sidebar item
4506  *
4507  *  li
4508  *    <span> icon </span>
4509  *    <span> text </span>
4510  *    <span>badge </span>
4511  */
4512
4513 /**
4514  * @class Roo.bootstrap.NavSidebarItem
4515  * @extends Roo.bootstrap.NavItem
4516  * Bootstrap Navbar.NavSidebarItem class
4517  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4518  * {bool} open is the menu open
4519  * @constructor
4520  * Create a new Navbar Button
4521  * @param {Object} config The config object
4522  */
4523 Roo.bootstrap.NavSidebarItem = function(config){
4524     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4525     this.addEvents({
4526         // raw events
4527         /**
4528          * @event click
4529          * The raw click event for the entire grid.
4530          * @param {Roo.EventObject} e
4531          */
4532         "click" : true,
4533          /**
4534             * @event changed
4535             * Fires when the active item active state changes
4536             * @param {Roo.bootstrap.NavSidebarItem} this
4537             * @param {boolean} state the new state
4538              
4539          */
4540         'changed': true
4541     });
4542    
4543 };
4544
4545 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4546     
4547     badgeWeight : 'default',
4548     
4549     open: false,
4550     
4551     getAutoCreate : function(){
4552         
4553         
4554         var a = {
4555                 tag: 'a',
4556                 href : this.href || '#',
4557                 cls: '',
4558                 html : '',
4559                 cn : []
4560         };
4561         var cfg = {
4562             tag: 'li',
4563             cls: '',
4564             cn: [ a ]
4565         };
4566         var span = {
4567             tag: 'span',
4568             html : this.html || ''
4569         };
4570         
4571         
4572         if (this.active) {
4573             cfg.cls += ' active';
4574         }
4575         
4576         if (this.disabled) {
4577             cfg.cls += ' disabled';
4578         }
4579         if (this.open) {
4580             cfg.cls += ' open x-open';
4581         }
4582         // left icon..
4583         if (this.glyphicon || this.icon) {
4584             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4585             a.cn.push({ tag : 'i', cls : c }) ;
4586         }
4587         // html..
4588         a.cn.push(span);
4589         // then badge..
4590         if (this.badge !== '') {
4591             
4592             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4593         }
4594         // fi
4595         if (this.menu) {
4596             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4597             a.cls += 'dropdown-toggle treeview' ;
4598         }
4599         
4600         return cfg;
4601          
4602            
4603     },
4604     
4605     initEvents : function()
4606     { 
4607         if (typeof (this.menu) != 'undefined') {
4608             this.menu.parentType = this.xtype;
4609             this.menu.triggerEl = this.el;
4610             this.menu = this.addxtype(Roo.apply({}, this.menu));
4611         }
4612         
4613         this.el.on('click', this.onClick, this);
4614        
4615     
4616         if(this.badge !== ''){
4617  
4618             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4619         }
4620         
4621     },
4622     
4623     onClick : function(e)
4624     {
4625         if(this.disabled){
4626             e.preventDefault();
4627             return;
4628         }
4629         
4630         if(this.preventDefault){
4631             e.preventDefault();
4632         }
4633         
4634         this.fireEvent('click', this);
4635     },
4636     
4637     disable : function()
4638     {
4639         this.setDisabled(true);
4640     },
4641     
4642     enable : function()
4643     {
4644         this.setDisabled(false);
4645     },
4646     
4647     setDisabled : function(state)
4648     {
4649         if(this.disabled == state){
4650             return;
4651         }
4652         
4653         this.disabled = state;
4654         
4655         if (state) {
4656             this.el.addClass('disabled');
4657             return;
4658         }
4659         
4660         this.el.removeClass('disabled');
4661         
4662         return;
4663     },
4664     
4665     setActive : function(state)
4666     {
4667         if(this.active == state){
4668             return;
4669         }
4670         
4671         this.active = state;
4672         
4673         if (state) {
4674             this.el.addClass('active');
4675             return;
4676         }
4677         
4678         this.el.removeClass('active');
4679         
4680         return;
4681     },
4682     
4683     isActive: function () 
4684     {
4685         return this.active;
4686     },
4687     
4688     setBadge : function(str)
4689     {
4690         if(!this.badgeEl){
4691             return;
4692         }
4693         
4694         this.badgeEl.dom.innerHTML = str;
4695     }
4696     
4697    
4698      
4699  
4700 });
4701  
4702
4703  /*
4704  * - LGPL
4705  *
4706  * row
4707  * 
4708  */
4709
4710 /**
4711  * @class Roo.bootstrap.Row
4712  * @extends Roo.bootstrap.Component
4713  * Bootstrap Row class (contains columns...)
4714  * 
4715  * @constructor
4716  * Create a new Row
4717  * @param {Object} config The config object
4718  */
4719
4720 Roo.bootstrap.Row = function(config){
4721     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4722 };
4723
4724 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4725     
4726     getAutoCreate : function(){
4727        return {
4728             cls: 'row clearfix'
4729        };
4730     }
4731     
4732     
4733 });
4734
4735  
4736
4737  /*
4738  * - LGPL
4739  *
4740  * element
4741  * 
4742  */
4743
4744 /**
4745  * @class Roo.bootstrap.Element
4746  * @extends Roo.bootstrap.Component
4747  * Bootstrap Element class
4748  * @cfg {String} html contents of the element
4749  * @cfg {String} tag tag of the element
4750  * @cfg {String} cls class of the element
4751  * @cfg {Boolean} preventDefault (true|false) default false
4752  * @cfg {Boolean} clickable (true|false) default false
4753  * 
4754  * @constructor
4755  * Create a new Element
4756  * @param {Object} config The config object
4757  */
4758
4759 Roo.bootstrap.Element = function(config){
4760     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4761     
4762     this.addEvents({
4763         // raw events
4764         /**
4765          * @event click
4766          * When a element is chick
4767          * @param {Roo.bootstrap.Element} this
4768          * @param {Roo.EventObject} e
4769          */
4770         "click" : true
4771     });
4772 };
4773
4774 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4775     
4776     tag: 'div',
4777     cls: '',
4778     html: '',
4779     preventDefault: false, 
4780     clickable: false,
4781     
4782     getAutoCreate : function(){
4783         
4784         var cfg = {
4785             tag: this.tag,
4786             cls: this.cls,
4787             html: this.html
4788         };
4789         
4790         return cfg;
4791     },
4792     
4793     initEvents: function() 
4794     {
4795         Roo.bootstrap.Element.superclass.initEvents.call(this);
4796         
4797         if(this.clickable){
4798             this.el.on('click', this.onClick, this);
4799         }
4800         
4801     },
4802     
4803     onClick : function(e)
4804     {
4805         if(this.preventDefault){
4806             e.preventDefault();
4807         }
4808         
4809         this.fireEvent('click', this, e);
4810     },
4811     
4812     getValue : function()
4813     {
4814         return this.el.dom.innerHTML;
4815     },
4816     
4817     setValue : function(value)
4818     {
4819         this.el.dom.innerHTML = value;
4820     }
4821    
4822 });
4823
4824  
4825
4826  /*
4827  * - LGPL
4828  *
4829  * pagination
4830  * 
4831  */
4832
4833 /**
4834  * @class Roo.bootstrap.Pagination
4835  * @extends Roo.bootstrap.Component
4836  * Bootstrap Pagination class
4837  * @cfg {String} size xs | sm | md | lg
4838  * @cfg {Boolean} inverse false | true
4839  * 
4840  * @constructor
4841  * Create a new Pagination
4842  * @param {Object} config The config object
4843  */
4844
4845 Roo.bootstrap.Pagination = function(config){
4846     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4847 };
4848
4849 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4850     
4851     cls: false,
4852     size: false,
4853     inverse: false,
4854     
4855     getAutoCreate : function(){
4856         var cfg = {
4857             tag: 'ul',
4858                 cls: 'pagination'
4859         };
4860         if (this.inverse) {
4861             cfg.cls += ' inverse';
4862         }
4863         if (this.html) {
4864             cfg.html=this.html;
4865         }
4866         if (this.cls) {
4867             cfg.cls += " " + this.cls;
4868         }
4869         return cfg;
4870     }
4871    
4872 });
4873
4874  
4875
4876  /*
4877  * - LGPL
4878  *
4879  * Pagination item
4880  * 
4881  */
4882
4883
4884 /**
4885  * @class Roo.bootstrap.PaginationItem
4886  * @extends Roo.bootstrap.Component
4887  * Bootstrap PaginationItem class
4888  * @cfg {String} html text
4889  * @cfg {String} href the link
4890  * @cfg {Boolean} preventDefault (true | false) default true
4891  * @cfg {Boolean} active (true | false) default false
4892  * @cfg {Boolean} disabled default false
4893  * 
4894  * 
4895  * @constructor
4896  * Create a new PaginationItem
4897  * @param {Object} config The config object
4898  */
4899
4900
4901 Roo.bootstrap.PaginationItem = function(config){
4902     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4903     this.addEvents({
4904         // raw events
4905         /**
4906          * @event click
4907          * The raw click event for the entire grid.
4908          * @param {Roo.EventObject} e
4909          */
4910         "click" : true
4911     });
4912 };
4913
4914 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4915     
4916     href : false,
4917     html : false,
4918     preventDefault: true,
4919     active : false,
4920     cls : false,
4921     disabled: false,
4922     
4923     getAutoCreate : function(){
4924         var cfg= {
4925             tag: 'li',
4926             cn: [
4927                 {
4928                     tag : 'a',
4929                     href : this.href ? this.href : '#',
4930                     html : this.html ? this.html : ''
4931                 }
4932             ]
4933         };
4934         
4935         if(this.cls){
4936             cfg.cls = this.cls;
4937         }
4938         
4939         if(this.disabled){
4940             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4941         }
4942         
4943         if(this.active){
4944             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4945         }
4946         
4947         return cfg;
4948     },
4949     
4950     initEvents: function() {
4951         
4952         this.el.on('click', this.onClick, this);
4953         
4954     },
4955     onClick : function(e)
4956     {
4957         Roo.log('PaginationItem on click ');
4958         if(this.preventDefault){
4959             e.preventDefault();
4960         }
4961         
4962         if(this.disabled){
4963             return;
4964         }
4965         
4966         this.fireEvent('click', this, e);
4967     }
4968    
4969 });
4970
4971  
4972
4973  /*
4974  * - LGPL
4975  *
4976  * slider
4977  * 
4978  */
4979
4980
4981 /**
4982  * @class Roo.bootstrap.Slider
4983  * @extends Roo.bootstrap.Component
4984  * Bootstrap Slider class
4985  *    
4986  * @constructor
4987  * Create a new Slider
4988  * @param {Object} config The config object
4989  */
4990
4991 Roo.bootstrap.Slider = function(config){
4992     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4993 };
4994
4995 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4996     
4997     getAutoCreate : function(){
4998         
4999         var cfg = {
5000             tag: 'div',
5001             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5002             cn: [
5003                 {
5004                     tag: 'a',
5005                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5006                 }
5007             ]
5008         };
5009         
5010         return cfg;
5011     }
5012    
5013 });
5014
5015  /*
5016  * Based on:
5017  * Ext JS Library 1.1.1
5018  * Copyright(c) 2006-2007, Ext JS, LLC.
5019  *
5020  * Originally Released Under LGPL - original licence link has changed is not relivant.
5021  *
5022  * Fork - LGPL
5023  * <script type="text/javascript">
5024  */
5025  
5026
5027 /**
5028  * @class Roo.grid.ColumnModel
5029  * @extends Roo.util.Observable
5030  * This is the default implementation of a ColumnModel used by the Grid. It defines
5031  * the columns in the grid.
5032  * <br>Usage:<br>
5033  <pre><code>
5034  var colModel = new Roo.grid.ColumnModel([
5035         {header: "Ticker", width: 60, sortable: true, locked: true},
5036         {header: "Company Name", width: 150, sortable: true},
5037         {header: "Market Cap.", width: 100, sortable: true},
5038         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5039         {header: "Employees", width: 100, sortable: true, resizable: false}
5040  ]);
5041  </code></pre>
5042  * <p>
5043  
5044  * The config options listed for this class are options which may appear in each
5045  * individual column definition.
5046  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5047  * @constructor
5048  * @param {Object} config An Array of column config objects. See this class's
5049  * config objects for details.
5050 */
5051 Roo.grid.ColumnModel = function(config){
5052         /**
5053      * The config passed into the constructor
5054      */
5055     this.config = config;
5056     this.lookup = {};
5057
5058     // if no id, create one
5059     // if the column does not have a dataIndex mapping,
5060     // map it to the order it is in the config
5061     for(var i = 0, len = config.length; i < len; i++){
5062         var c = config[i];
5063         if(typeof c.dataIndex == "undefined"){
5064             c.dataIndex = i;
5065         }
5066         if(typeof c.renderer == "string"){
5067             c.renderer = Roo.util.Format[c.renderer];
5068         }
5069         if(typeof c.id == "undefined"){
5070             c.id = Roo.id();
5071         }
5072         if(c.editor && c.editor.xtype){
5073             c.editor  = Roo.factory(c.editor, Roo.grid);
5074         }
5075         if(c.editor && c.editor.isFormField){
5076             c.editor = new Roo.grid.GridEditor(c.editor);
5077         }
5078         this.lookup[c.id] = c;
5079     }
5080
5081     /**
5082      * The width of columns which have no width specified (defaults to 100)
5083      * @type Number
5084      */
5085     this.defaultWidth = 100;
5086
5087     /**
5088      * Default sortable of columns which have no sortable specified (defaults to false)
5089      * @type Boolean
5090      */
5091     this.defaultSortable = false;
5092
5093     this.addEvents({
5094         /**
5095              * @event widthchange
5096              * Fires when the width of a column changes.
5097              * @param {ColumnModel} this
5098              * @param {Number} columnIndex The column index
5099              * @param {Number} newWidth The new width
5100              */
5101             "widthchange": true,
5102         /**
5103              * @event headerchange
5104              * Fires when the text of a header changes.
5105              * @param {ColumnModel} this
5106              * @param {Number} columnIndex The column index
5107              * @param {Number} newText The new header text
5108              */
5109             "headerchange": true,
5110         /**
5111              * @event hiddenchange
5112              * Fires when a column is hidden or "unhidden".
5113              * @param {ColumnModel} this
5114              * @param {Number} columnIndex The column index
5115              * @param {Boolean} hidden true if hidden, false otherwise
5116              */
5117             "hiddenchange": true,
5118             /**
5119          * @event columnmoved
5120          * Fires when a column is moved.
5121          * @param {ColumnModel} this
5122          * @param {Number} oldIndex
5123          * @param {Number} newIndex
5124          */
5125         "columnmoved" : true,
5126         /**
5127          * @event columlockchange
5128          * Fires when a column's locked state is changed
5129          * @param {ColumnModel} this
5130          * @param {Number} colIndex
5131          * @param {Boolean} locked true if locked
5132          */
5133         "columnlockchange" : true
5134     });
5135     Roo.grid.ColumnModel.superclass.constructor.call(this);
5136 };
5137 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5138     /**
5139      * @cfg {String} header The header text to display in the Grid view.
5140      */
5141     /**
5142      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5143      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5144      * specified, the column's index is used as an index into the Record's data Array.
5145      */
5146     /**
5147      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5148      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5149      */
5150     /**
5151      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5152      * Defaults to the value of the {@link #defaultSortable} property.
5153      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5154      */
5155     /**
5156      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5157      */
5158     /**
5159      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5160      */
5161     /**
5162      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5163      */
5164     /**
5165      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5166      */
5167     /**
5168      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5169      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5170      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5171      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5172      */
5173        /**
5174      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5175      */
5176     /**
5177      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5178      */
5179     /**
5180      * @cfg {String} cursor (Optional)
5181      */
5182     /**
5183      * @cfg {String} tooltip (Optional)
5184      */
5185     /**
5186      * @cfg {Number} xs (Optional)
5187      */
5188     /**
5189      * @cfg {Number} sm (Optional)
5190      */
5191     /**
5192      * @cfg {Number} md (Optional)
5193      */
5194     /**
5195      * @cfg {Number} lg (Optional)
5196      */
5197     /**
5198      * Returns the id of the column at the specified index.
5199      * @param {Number} index The column index
5200      * @return {String} the id
5201      */
5202     getColumnId : function(index){
5203         return this.config[index].id;
5204     },
5205
5206     /**
5207      * Returns the column for a specified id.
5208      * @param {String} id The column id
5209      * @return {Object} the column
5210      */
5211     getColumnById : function(id){
5212         return this.lookup[id];
5213     },
5214
5215     
5216     /**
5217      * Returns the column for a specified dataIndex.
5218      * @param {String} dataIndex The column dataIndex
5219      * @return {Object|Boolean} the column or false if not found
5220      */
5221     getColumnByDataIndex: function(dataIndex){
5222         var index = this.findColumnIndex(dataIndex);
5223         return index > -1 ? this.config[index] : false;
5224     },
5225     
5226     /**
5227      * Returns the index for a specified column id.
5228      * @param {String} id The column id
5229      * @return {Number} the index, or -1 if not found
5230      */
5231     getIndexById : function(id){
5232         for(var i = 0, len = this.config.length; i < len; i++){
5233             if(this.config[i].id == id){
5234                 return i;
5235             }
5236         }
5237         return -1;
5238     },
5239     
5240     /**
5241      * Returns the index for a specified column dataIndex.
5242      * @param {String} dataIndex The column dataIndex
5243      * @return {Number} the index, or -1 if not found
5244      */
5245     
5246     findColumnIndex : function(dataIndex){
5247         for(var i = 0, len = this.config.length; i < len; i++){
5248             if(this.config[i].dataIndex == dataIndex){
5249                 return i;
5250             }
5251         }
5252         return -1;
5253     },
5254     
5255     
5256     moveColumn : function(oldIndex, newIndex){
5257         var c = this.config[oldIndex];
5258         this.config.splice(oldIndex, 1);
5259         this.config.splice(newIndex, 0, c);
5260         this.dataMap = null;
5261         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5262     },
5263
5264     isLocked : function(colIndex){
5265         return this.config[colIndex].locked === true;
5266     },
5267
5268     setLocked : function(colIndex, value, suppressEvent){
5269         if(this.isLocked(colIndex) == value){
5270             return;
5271         }
5272         this.config[colIndex].locked = value;
5273         if(!suppressEvent){
5274             this.fireEvent("columnlockchange", this, colIndex, value);
5275         }
5276     },
5277
5278     getTotalLockedWidth : function(){
5279         var totalWidth = 0;
5280         for(var i = 0; i < this.config.length; i++){
5281             if(this.isLocked(i) && !this.isHidden(i)){
5282                 this.totalWidth += this.getColumnWidth(i);
5283             }
5284         }
5285         return totalWidth;
5286     },
5287
5288     getLockedCount : function(){
5289         for(var i = 0, len = this.config.length; i < len; i++){
5290             if(!this.isLocked(i)){
5291                 return i;
5292             }
5293         }
5294         
5295         return this.config.length;
5296     },
5297
5298     /**
5299      * Returns the number of columns.
5300      * @return {Number}
5301      */
5302     getColumnCount : function(visibleOnly){
5303         if(visibleOnly === true){
5304             var c = 0;
5305             for(var i = 0, len = this.config.length; i < len; i++){
5306                 if(!this.isHidden(i)){
5307                     c++;
5308                 }
5309             }
5310             return c;
5311         }
5312         return this.config.length;
5313     },
5314
5315     /**
5316      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5317      * @param {Function} fn
5318      * @param {Object} scope (optional)
5319      * @return {Array} result
5320      */
5321     getColumnsBy : function(fn, scope){
5322         var r = [];
5323         for(var i = 0, len = this.config.length; i < len; i++){
5324             var c = this.config[i];
5325             if(fn.call(scope||this, c, i) === true){
5326                 r[r.length] = c;
5327             }
5328         }
5329         return r;
5330     },
5331
5332     /**
5333      * Returns true if the specified column is sortable.
5334      * @param {Number} col The column index
5335      * @return {Boolean}
5336      */
5337     isSortable : function(col){
5338         if(typeof this.config[col].sortable == "undefined"){
5339             return this.defaultSortable;
5340         }
5341         return this.config[col].sortable;
5342     },
5343
5344     /**
5345      * Returns the rendering (formatting) function defined for the column.
5346      * @param {Number} col The column index.
5347      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5348      */
5349     getRenderer : function(col){
5350         if(!this.config[col].renderer){
5351             return Roo.grid.ColumnModel.defaultRenderer;
5352         }
5353         return this.config[col].renderer;
5354     },
5355
5356     /**
5357      * Sets the rendering (formatting) function for a column.
5358      * @param {Number} col The column index
5359      * @param {Function} fn The function to use to process the cell's raw data
5360      * to return HTML markup for the grid view. The render function is called with
5361      * the following parameters:<ul>
5362      * <li>Data value.</li>
5363      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5364      * <li>css A CSS style string to apply to the table cell.</li>
5365      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5366      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5367      * <li>Row index</li>
5368      * <li>Column index</li>
5369      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5370      */
5371     setRenderer : function(col, fn){
5372         this.config[col].renderer = fn;
5373     },
5374
5375     /**
5376      * Returns the width for the specified column.
5377      * @param {Number} col The column index
5378      * @return {Number}
5379      */
5380     getColumnWidth : function(col){
5381         return this.config[col].width * 1 || this.defaultWidth;
5382     },
5383
5384     /**
5385      * Sets the width for a column.
5386      * @param {Number} col The column index
5387      * @param {Number} width The new width
5388      */
5389     setColumnWidth : function(col, width, suppressEvent){
5390         this.config[col].width = width;
5391         this.totalWidth = null;
5392         if(!suppressEvent){
5393              this.fireEvent("widthchange", this, col, width);
5394         }
5395     },
5396
5397     /**
5398      * Returns the total width of all columns.
5399      * @param {Boolean} includeHidden True to include hidden column widths
5400      * @return {Number}
5401      */
5402     getTotalWidth : function(includeHidden){
5403         if(!this.totalWidth){
5404             this.totalWidth = 0;
5405             for(var i = 0, len = this.config.length; i < len; i++){
5406                 if(includeHidden || !this.isHidden(i)){
5407                     this.totalWidth += this.getColumnWidth(i);
5408                 }
5409             }
5410         }
5411         return this.totalWidth;
5412     },
5413
5414     /**
5415      * Returns the header for the specified column.
5416      * @param {Number} col The column index
5417      * @return {String}
5418      */
5419     getColumnHeader : function(col){
5420         return this.config[col].header;
5421     },
5422
5423     /**
5424      * Sets the header for a column.
5425      * @param {Number} col The column index
5426      * @param {String} header The new header
5427      */
5428     setColumnHeader : function(col, header){
5429         this.config[col].header = header;
5430         this.fireEvent("headerchange", this, col, header);
5431     },
5432
5433     /**
5434      * Returns the tooltip for the specified column.
5435      * @param {Number} col The column index
5436      * @return {String}
5437      */
5438     getColumnTooltip : function(col){
5439             return this.config[col].tooltip;
5440     },
5441     /**
5442      * Sets the tooltip for a column.
5443      * @param {Number} col The column index
5444      * @param {String} tooltip The new tooltip
5445      */
5446     setColumnTooltip : function(col, tooltip){
5447             this.config[col].tooltip = tooltip;
5448     },
5449
5450     /**
5451      * Returns the dataIndex for the specified column.
5452      * @param {Number} col The column index
5453      * @return {Number}
5454      */
5455     getDataIndex : function(col){
5456         return this.config[col].dataIndex;
5457     },
5458
5459     /**
5460      * Sets the dataIndex for a column.
5461      * @param {Number} col The column index
5462      * @param {Number} dataIndex The new dataIndex
5463      */
5464     setDataIndex : function(col, dataIndex){
5465         this.config[col].dataIndex = dataIndex;
5466     },
5467
5468     
5469     
5470     /**
5471      * Returns true if the cell is editable.
5472      * @param {Number} colIndex The column index
5473      * @param {Number} rowIndex The row index - this is nto actually used..?
5474      * @return {Boolean}
5475      */
5476     isCellEditable : function(colIndex, rowIndex){
5477         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5478     },
5479
5480     /**
5481      * Returns the editor defined for the cell/column.
5482      * return false or null to disable editing.
5483      * @param {Number} colIndex The column index
5484      * @param {Number} rowIndex The row index
5485      * @return {Object}
5486      */
5487     getCellEditor : function(colIndex, rowIndex){
5488         return this.config[colIndex].editor;
5489     },
5490
5491     /**
5492      * Sets if a column is editable.
5493      * @param {Number} col The column index
5494      * @param {Boolean} editable True if the column is editable
5495      */
5496     setEditable : function(col, editable){
5497         this.config[col].editable = editable;
5498     },
5499
5500
5501     /**
5502      * Returns true if the column is hidden.
5503      * @param {Number} colIndex The column index
5504      * @return {Boolean}
5505      */
5506     isHidden : function(colIndex){
5507         return this.config[colIndex].hidden;
5508     },
5509
5510
5511     /**
5512      * Returns true if the column width cannot be changed
5513      */
5514     isFixed : function(colIndex){
5515         return this.config[colIndex].fixed;
5516     },
5517
5518     /**
5519      * Returns true if the column can be resized
5520      * @return {Boolean}
5521      */
5522     isResizable : function(colIndex){
5523         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5524     },
5525     /**
5526      * Sets if a column is hidden.
5527      * @param {Number} colIndex The column index
5528      * @param {Boolean} hidden True if the column is hidden
5529      */
5530     setHidden : function(colIndex, hidden){
5531         this.config[colIndex].hidden = hidden;
5532         this.totalWidth = null;
5533         this.fireEvent("hiddenchange", this, colIndex, hidden);
5534     },
5535
5536     /**
5537      * Sets the editor for a column.
5538      * @param {Number} col The column index
5539      * @param {Object} editor The editor object
5540      */
5541     setEditor : function(col, editor){
5542         this.config[col].editor = editor;
5543     }
5544 });
5545
5546 Roo.grid.ColumnModel.defaultRenderer = function(value)
5547 {
5548     if(typeof value == "object") {
5549         return value;
5550     }
5551         if(typeof value == "string" && value.length < 1){
5552             return "&#160;";
5553         }
5554     
5555         return String.format("{0}", value);
5556 };
5557
5558 // Alias for backwards compatibility
5559 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5560 /*
5561  * Based on:
5562  * Ext JS Library 1.1.1
5563  * Copyright(c) 2006-2007, Ext JS, LLC.
5564  *
5565  * Originally Released Under LGPL - original licence link has changed is not relivant.
5566  *
5567  * Fork - LGPL
5568  * <script type="text/javascript">
5569  */
5570  
5571 /**
5572  * @class Roo.LoadMask
5573  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5574  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5575  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5576  * element's UpdateManager load indicator and will be destroyed after the initial load.
5577  * @constructor
5578  * Create a new LoadMask
5579  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5580  * @param {Object} config The config object
5581  */
5582 Roo.LoadMask = function(el, config){
5583     this.el = Roo.get(el);
5584     Roo.apply(this, config);
5585     if(this.store){
5586         this.store.on('beforeload', this.onBeforeLoad, this);
5587         this.store.on('load', this.onLoad, this);
5588         this.store.on('loadexception', this.onLoadException, this);
5589         this.removeMask = false;
5590     }else{
5591         var um = this.el.getUpdateManager();
5592         um.showLoadIndicator = false; // disable the default indicator
5593         um.on('beforeupdate', this.onBeforeLoad, this);
5594         um.on('update', this.onLoad, this);
5595         um.on('failure', this.onLoad, this);
5596         this.removeMask = true;
5597     }
5598 };
5599
5600 Roo.LoadMask.prototype = {
5601     /**
5602      * @cfg {Boolean} removeMask
5603      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5604      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5605      */
5606     /**
5607      * @cfg {String} msg
5608      * The text to display in a centered loading message box (defaults to 'Loading...')
5609      */
5610     msg : 'Loading...',
5611     /**
5612      * @cfg {String} msgCls
5613      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5614      */
5615     msgCls : 'x-mask-loading',
5616
5617     /**
5618      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5619      * @type Boolean
5620      */
5621     disabled: false,
5622
5623     /**
5624      * Disables the mask to prevent it from being displayed
5625      */
5626     disable : function(){
5627        this.disabled = true;
5628     },
5629
5630     /**
5631      * Enables the mask so that it can be displayed
5632      */
5633     enable : function(){
5634         this.disabled = false;
5635     },
5636     
5637     onLoadException : function()
5638     {
5639         Roo.log(arguments);
5640         
5641         if (typeof(arguments[3]) != 'undefined') {
5642             Roo.MessageBox.alert("Error loading",arguments[3]);
5643         } 
5644         /*
5645         try {
5646             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5647                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5648             }   
5649         } catch(e) {
5650             
5651         }
5652         */
5653     
5654         
5655         
5656         this.el.unmask(this.removeMask);
5657     },
5658     // private
5659     onLoad : function()
5660     {
5661         this.el.unmask(this.removeMask);
5662     },
5663
5664     // private
5665     onBeforeLoad : function(){
5666         if(!this.disabled){
5667             this.el.mask(this.msg, this.msgCls);
5668         }
5669     },
5670
5671     // private
5672     destroy : function(){
5673         if(this.store){
5674             this.store.un('beforeload', this.onBeforeLoad, this);
5675             this.store.un('load', this.onLoad, this);
5676             this.store.un('loadexception', this.onLoadException, this);
5677         }else{
5678             var um = this.el.getUpdateManager();
5679             um.un('beforeupdate', this.onBeforeLoad, this);
5680             um.un('update', this.onLoad, this);
5681             um.un('failure', this.onLoad, this);
5682         }
5683     }
5684 };/*
5685  * - LGPL
5686  *
5687  * table
5688  * 
5689  */
5690
5691 /**
5692  * @class Roo.bootstrap.Table
5693  * @extends Roo.bootstrap.Component
5694  * Bootstrap Table class
5695  * @cfg {String} cls table class
5696  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5697  * @cfg {String} bgcolor Specifies the background color for a table
5698  * @cfg {Number} border Specifies whether the table cells should have borders or not
5699  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5700  * @cfg {Number} cellspacing Specifies the space between cells
5701  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5702  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5703  * @cfg {String} sortable Specifies that the table should be sortable
5704  * @cfg {String} summary Specifies a summary of the content of a table
5705  * @cfg {Number} width Specifies the width of a table
5706  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5707  * 
5708  * @cfg {boolean} striped Should the rows be alternative striped
5709  * @cfg {boolean} bordered Add borders to the table
5710  * @cfg {boolean} hover Add hover highlighting
5711  * @cfg {boolean} condensed Format condensed
5712  * @cfg {boolean} responsive Format condensed
5713  * @cfg {Boolean} loadMask (true|false) default false
5714  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5715  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5716  * @cfg {Boolean} rowSelection (true|false) default false
5717  * @cfg {Boolean} cellSelection (true|false) default false
5718  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5719  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5720  
5721  * 
5722  * @constructor
5723  * Create a new Table
5724  * @param {Object} config The config object
5725  */
5726
5727 Roo.bootstrap.Table = function(config){
5728     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5729     
5730   
5731     
5732     // BC...
5733     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5734     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5735     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5736     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5737     
5738     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5739     if (this.sm) {
5740         this.sm.grid = this;
5741         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5742         this.sm = this.selModel;
5743         this.sm.xmodule = this.xmodule || false;
5744     }
5745     
5746     if (this.cm && typeof(this.cm.config) == 'undefined') {
5747         this.colModel = new Roo.grid.ColumnModel(this.cm);
5748         this.cm = this.colModel;
5749         this.cm.xmodule = this.xmodule || false;
5750     }
5751     if (this.store) {
5752         this.store= Roo.factory(this.store, Roo.data);
5753         this.ds = this.store;
5754         this.ds.xmodule = this.xmodule || false;
5755          
5756     }
5757     if (this.footer && this.store) {
5758         this.footer.dataSource = this.ds;
5759         this.footer = Roo.factory(this.footer);
5760     }
5761     
5762     /** @private */
5763     this.addEvents({
5764         /**
5765          * @event cellclick
5766          * Fires when a cell is clicked
5767          * @param {Roo.bootstrap.Table} this
5768          * @param {Roo.Element} el
5769          * @param {Number} rowIndex
5770          * @param {Number} columnIndex
5771          * @param {Roo.EventObject} e
5772          */
5773         "cellclick" : true,
5774         /**
5775          * @event celldblclick
5776          * Fires when a cell is double clicked
5777          * @param {Roo.bootstrap.Table} this
5778          * @param {Roo.Element} el
5779          * @param {Number} rowIndex
5780          * @param {Number} columnIndex
5781          * @param {Roo.EventObject} e
5782          */
5783         "celldblclick" : true,
5784         /**
5785          * @event rowclick
5786          * Fires when a row is clicked
5787          * @param {Roo.bootstrap.Table} this
5788          * @param {Roo.Element} el
5789          * @param {Number} rowIndex
5790          * @param {Roo.EventObject} e
5791          */
5792         "rowclick" : true,
5793         /**
5794          * @event rowdblclick
5795          * Fires when a row is double clicked
5796          * @param {Roo.bootstrap.Table} this
5797          * @param {Roo.Element} el
5798          * @param {Number} rowIndex
5799          * @param {Roo.EventObject} e
5800          */
5801         "rowdblclick" : true,
5802         /**
5803          * @event mouseover
5804          * Fires when a mouseover occur
5805          * @param {Roo.bootstrap.Table} this
5806          * @param {Roo.Element} el
5807          * @param {Number} rowIndex
5808          * @param {Number} columnIndex
5809          * @param {Roo.EventObject} e
5810          */
5811         "mouseover" : true,
5812         /**
5813          * @event mouseout
5814          * Fires when a mouseout occur
5815          * @param {Roo.bootstrap.Table} this
5816          * @param {Roo.Element} el
5817          * @param {Number} rowIndex
5818          * @param {Number} columnIndex
5819          * @param {Roo.EventObject} e
5820          */
5821         "mouseout" : true,
5822         /**
5823          * @event rowclass
5824          * Fires when a row is rendered, so you can change add a style to it.
5825          * @param {Roo.bootstrap.Table} this
5826          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5827          */
5828         'rowclass' : true,
5829           /**
5830          * @event rowsrendered
5831          * Fires when all the  rows have been rendered
5832          * @param {Roo.bootstrap.Table} this
5833          */
5834         'rowsrendered' : true,
5835         /**
5836          * @event contextmenu
5837          * The raw contextmenu event for the entire grid.
5838          * @param {Roo.EventObject} e
5839          */
5840         "contextmenu" : true,
5841         /**
5842          * @event rowcontextmenu
5843          * Fires when a row is right clicked
5844          * @param {Roo.bootstrap.Table} this
5845          * @param {Number} rowIndex
5846          * @param {Roo.EventObject} e
5847          */
5848         "rowcontextmenu" : true,
5849         /**
5850          * @event cellcontextmenu
5851          * Fires when a cell is right clicked
5852          * @param {Roo.bootstrap.Table} this
5853          * @param {Number} rowIndex
5854          * @param {Number} cellIndex
5855          * @param {Roo.EventObject} e
5856          */
5857          "cellcontextmenu" : true,
5858          /**
5859          * @event headercontextmenu
5860          * Fires when a header is right clicked
5861          * @param {Roo.bootstrap.Table} this
5862          * @param {Number} columnIndex
5863          * @param {Roo.EventObject} e
5864          */
5865         "headercontextmenu" : true
5866     });
5867 };
5868
5869 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5870     
5871     cls: false,
5872     align: false,
5873     bgcolor: false,
5874     border: false,
5875     cellpadding: false,
5876     cellspacing: false,
5877     frame: false,
5878     rules: false,
5879     sortable: false,
5880     summary: false,
5881     width: false,
5882     striped : false,
5883     scrollBody : false,
5884     bordered: false,
5885     hover:  false,
5886     condensed : false,
5887     responsive : false,
5888     sm : false,
5889     cm : false,
5890     store : false,
5891     loadMask : false,
5892     footerShow : true,
5893     headerShow : true,
5894   
5895     rowSelection : false,
5896     cellSelection : false,
5897     layout : false,
5898     
5899     // Roo.Element - the tbody
5900     mainBody: false,
5901     // Roo.Element - thead element
5902     mainHead: false,
5903     
5904     container: false, // used by gridpanel...
5905     
5906     getAutoCreate : function()
5907     {
5908         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5909         
5910         cfg = {
5911             tag: 'table',
5912             cls : 'table',
5913             cn : []
5914         };
5915         if (this.scrollBody) {
5916             cfg.cls += ' table-body-fixed';
5917         }    
5918         if (this.striped) {
5919             cfg.cls += ' table-striped';
5920         }
5921         
5922         if (this.hover) {
5923             cfg.cls += ' table-hover';
5924         }
5925         if (this.bordered) {
5926             cfg.cls += ' table-bordered';
5927         }
5928         if (this.condensed) {
5929             cfg.cls += ' table-condensed';
5930         }
5931         if (this.responsive) {
5932             cfg.cls += ' table-responsive';
5933         }
5934         
5935         if (this.cls) {
5936             cfg.cls+=  ' ' +this.cls;
5937         }
5938         
5939         // this lot should be simplifed...
5940         
5941         if (this.align) {
5942             cfg.align=this.align;
5943         }
5944         if (this.bgcolor) {
5945             cfg.bgcolor=this.bgcolor;
5946         }
5947         if (this.border) {
5948             cfg.border=this.border;
5949         }
5950         if (this.cellpadding) {
5951             cfg.cellpadding=this.cellpadding;
5952         }
5953         if (this.cellspacing) {
5954             cfg.cellspacing=this.cellspacing;
5955         }
5956         if (this.frame) {
5957             cfg.frame=this.frame;
5958         }
5959         if (this.rules) {
5960             cfg.rules=this.rules;
5961         }
5962         if (this.sortable) {
5963             cfg.sortable=this.sortable;
5964         }
5965         if (this.summary) {
5966             cfg.summary=this.summary;
5967         }
5968         if (this.width) {
5969             cfg.width=this.width;
5970         }
5971         if (this.layout) {
5972             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5973         }
5974         
5975         if(this.store || this.cm){
5976             if(this.headerShow){
5977                 cfg.cn.push(this.renderHeader());
5978             }
5979             
5980             cfg.cn.push(this.renderBody());
5981             
5982             if(this.footerShow){
5983                 cfg.cn.push(this.renderFooter());
5984             }
5985             // where does this come from?
5986             //cfg.cls+=  ' TableGrid';
5987         }
5988         
5989         return { cn : [ cfg ] };
5990     },
5991     
5992     initEvents : function()
5993     {   
5994         if(!this.store || !this.cm){
5995             return;
5996         }
5997         if (this.selModel) {
5998             this.selModel.initEvents();
5999         }
6000         
6001         
6002         //Roo.log('initEvents with ds!!!!');
6003         
6004         this.mainBody = this.el.select('tbody', true).first();
6005         this.mainHead = this.el.select('thead', true).first();
6006         
6007         
6008         
6009         
6010         var _this = this;
6011         
6012         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6013             e.on('click', _this.sort, _this);
6014         });
6015         
6016         this.mainBody.on("click", this.onClick, this);
6017         this.mainBody.on("dblclick", this.onDblClick, this);
6018         
6019         // why is this done????? = it breaks dialogs??
6020         //this.parent().el.setStyle('position', 'relative');
6021         
6022         
6023         if (this.footer) {
6024             this.footer.parentId = this.id;
6025             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
6026         } 
6027         
6028         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6029         
6030         this.store.on('load', this.onLoad, this);
6031         this.store.on('beforeload', this.onBeforeLoad, this);
6032         this.store.on('update', this.onUpdate, this);
6033         this.store.on('add', this.onAdd, this);
6034         this.store.on("clear", this.clear, this);
6035         
6036         this.el.on("contextmenu", this.onContextMenu, this);
6037         
6038         this.mainBody.on('scroll', this.onBodyScroll, this);
6039         
6040         
6041     },
6042     
6043     onContextMenu : function(e, t)
6044     {
6045         this.processEvent("contextmenu", e);
6046     },
6047     
6048     processEvent : function(name, e)
6049     {
6050         if (name != 'touchstart' ) {
6051             this.fireEvent(name, e);    
6052         }
6053         
6054         var t = e.getTarget();
6055         
6056         var cell = Roo.get(t);
6057         
6058         if(!cell){
6059             return;
6060         }
6061         
6062         if(cell.findParent('tfoot', false, true)){
6063             return;
6064         }
6065         
6066         if(cell.findParent('thead', false, true)){
6067             
6068             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6069                 cell = Roo.get(t).findParent('th', false, true);
6070                 if (!cell) {
6071                     Roo.log("failed to find th in thead?");
6072                     Roo.log(e.getTarget());
6073                     return;
6074                 }
6075             }
6076             
6077             var cellIndex = cell.dom.cellIndex;
6078             
6079             var ename = name == 'touchstart' ? 'click' : name;
6080             this.fireEvent("header" + ename, this, cellIndex, e);
6081             
6082             return;
6083         }
6084         
6085         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6086             cell = Roo.get(t).findParent('td', false, true);
6087             if (!cell) {
6088                 Roo.log("failed to find th in tbody?");
6089                 Roo.log(e.getTarget());
6090                 return;
6091             }
6092         }
6093         
6094         var row = cell.findParent('tr', false, true);
6095         var cellIndex = cell.dom.cellIndex;
6096         var rowIndex = row.dom.rowIndex - 1;
6097         
6098         if(row !== false){
6099             
6100             this.fireEvent("row" + name, this, rowIndex, e);
6101             
6102             if(cell !== false){
6103             
6104                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6105             }
6106         }
6107         
6108     },
6109     
6110     onMouseover : function(e, el)
6111     {
6112         var cell = Roo.get(el);
6113         
6114         if(!cell){
6115             return;
6116         }
6117         
6118         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6119             cell = cell.findParent('td', false, true);
6120         }
6121         
6122         var row = cell.findParent('tr', false, true);
6123         var cellIndex = cell.dom.cellIndex;
6124         var rowIndex = row.dom.rowIndex - 1; // start from 0
6125         
6126         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6127         
6128     },
6129     
6130     onMouseout : function(e, el)
6131     {
6132         var cell = Roo.get(el);
6133         
6134         if(!cell){
6135             return;
6136         }
6137         
6138         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6139             cell = cell.findParent('td', false, true);
6140         }
6141         
6142         var row = cell.findParent('tr', false, true);
6143         var cellIndex = cell.dom.cellIndex;
6144         var rowIndex = row.dom.rowIndex - 1; // start from 0
6145         
6146         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6147         
6148     },
6149     
6150     onClick : function(e, el)
6151     {
6152         var cell = Roo.get(el);
6153         
6154         if(!cell || (!this.cellSelection && !this.rowSelection)){
6155             return;
6156         }
6157         
6158         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6159             cell = cell.findParent('td', false, true);
6160         }
6161         
6162         if(!cell || typeof(cell) == 'undefined'){
6163             return;
6164         }
6165         
6166         var row = cell.findParent('tr', false, true);
6167         
6168         if(!row || typeof(row) == 'undefined'){
6169             return;
6170         }
6171         
6172         var cellIndex = cell.dom.cellIndex;
6173         var rowIndex = this.getRowIndex(row);
6174         
6175         // why??? - should these not be based on SelectionModel?
6176         if(this.cellSelection){
6177             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6178         }
6179         
6180         if(this.rowSelection){
6181             this.fireEvent('rowclick', this, row, rowIndex, e);
6182         }
6183         
6184         
6185     },
6186         
6187     onDblClick : function(e,el)
6188     {
6189         var cell = Roo.get(el);
6190         
6191         if(!cell || (!this.cellSelection && !this.rowSelection)){
6192             return;
6193         }
6194         
6195         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6196             cell = cell.findParent('td', false, true);
6197         }
6198         
6199         if(!cell || typeof(cell) == 'undefined'){
6200             return;
6201         }
6202         
6203         var row = cell.findParent('tr', false, true);
6204         
6205         if(!row || typeof(row) == 'undefined'){
6206             return;
6207         }
6208         
6209         var cellIndex = cell.dom.cellIndex;
6210         var rowIndex = this.getRowIndex(row);
6211         
6212         if(this.cellSelection){
6213             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6214         }
6215         
6216         if(this.rowSelection){
6217             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6218         }
6219     },
6220     
6221     sort : function(e,el)
6222     {
6223         var col = Roo.get(el);
6224         
6225         if(!col.hasClass('sortable')){
6226             return;
6227         }
6228         
6229         var sort = col.attr('sort');
6230         var dir = 'ASC';
6231         
6232         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6233             dir = 'DESC';
6234         }
6235         
6236         this.store.sortInfo = {field : sort, direction : dir};
6237         
6238         if (this.footer) {
6239             Roo.log("calling footer first");
6240             this.footer.onClick('first');
6241         } else {
6242         
6243             this.store.load({ params : { start : 0 } });
6244         }
6245     },
6246     
6247     renderHeader : function()
6248     {
6249         var header = {
6250             tag: 'thead',
6251             cn : []
6252         };
6253         
6254         var cm = this.cm;
6255         this.totalWidth = 0;
6256         
6257         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6258             
6259             var config = cm.config[i];
6260             
6261             var c = {
6262                 tag: 'th',
6263                 style : '',
6264                 html: cm.getColumnHeader(i)
6265             };
6266             
6267             var hh = '';
6268             
6269             if(typeof(config.sortable) != 'undefined' && config.sortable){
6270                 c.cls = 'sortable';
6271                 c.html = '<i class="glyphicon"></i>' + c.html;
6272             }
6273             
6274             if(typeof(config.lgHeader) != 'undefined'){
6275                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6276             }
6277             
6278             if(typeof(config.mdHeader) != 'undefined'){
6279                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6280             }
6281             
6282             if(typeof(config.smHeader) != 'undefined'){
6283                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6284             }
6285             
6286             if(typeof(config.xsHeader) != 'undefined'){
6287                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6288             }
6289             
6290             if(hh.length){
6291                 c.html = hh;
6292             }
6293             
6294             if(typeof(config.tooltip) != 'undefined'){
6295                 c.tooltip = config.tooltip;
6296             }
6297             
6298             if(typeof(config.colspan) != 'undefined'){
6299                 c.colspan = config.colspan;
6300             }
6301             
6302             if(typeof(config.hidden) != 'undefined' && config.hidden){
6303                 c.style += ' display:none;';
6304             }
6305             
6306             if(typeof(config.dataIndex) != 'undefined'){
6307                 c.sort = config.dataIndex;
6308             }
6309             
6310            
6311             
6312             if(typeof(config.align) != 'undefined' && config.align.length){
6313                 c.style += ' text-align:' + config.align + ';';
6314             }
6315             
6316             if(typeof(config.width) != 'undefined'){
6317                 c.style += ' width:' + config.width + 'px;';
6318                 this.totalWidth += config.width;
6319             } else {
6320                 this.totalWidth += 100; // assume minimum of 100 per column?
6321             }
6322             
6323             if(typeof(config.cls) != 'undefined'){
6324                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6325             }
6326             
6327             ['xs','sm','md','lg'].map(function(size){
6328                 
6329                 if(typeof(config[size]) == 'undefined'){
6330                     return;
6331                 }
6332                 
6333                 if (!config[size]) { // 0 = hidden
6334                     c.cls += ' hidden-' + size;
6335                     return;
6336                 }
6337                 
6338                 c.cls += ' col-' + size + '-' + config[size];
6339
6340             });
6341             
6342             header.cn.push(c)
6343         }
6344         
6345         return header;
6346     },
6347     
6348     renderBody : function()
6349     {
6350         var body = {
6351             tag: 'tbody',
6352             cn : [
6353                 {
6354                     tag: 'tr',
6355                     cn : [
6356                         {
6357                             tag : 'td',
6358                             colspan :  this.cm.getColumnCount()
6359                         }
6360                     ]
6361                 }
6362             ]
6363         };
6364         
6365         return body;
6366     },
6367     
6368     renderFooter : function()
6369     {
6370         var footer = {
6371             tag: 'tfoot',
6372             cn : [
6373                 {
6374                     tag: 'tr',
6375                     cn : [
6376                         {
6377                             tag : 'td',
6378                             colspan :  this.cm.getColumnCount()
6379                         }
6380                     ]
6381                 }
6382             ]
6383         };
6384         
6385         return footer;
6386     },
6387     
6388     
6389     
6390     onLoad : function()
6391     {
6392 //        Roo.log('ds onload');
6393         this.clear();
6394         
6395         var _this = this;
6396         var cm = this.cm;
6397         var ds = this.store;
6398         
6399         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6400             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6401             if (_this.store.sortInfo) {
6402                     
6403                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6404                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6405                 }
6406                 
6407                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6408                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6409                 }
6410             }
6411         });
6412         
6413         var tbody =  this.mainBody;
6414               
6415         if(ds.getCount() > 0){
6416             ds.data.each(function(d,rowIndex){
6417                 var row =  this.renderRow(cm, ds, rowIndex);
6418                 
6419                 tbody.createChild(row);
6420                 
6421                 var _this = this;
6422                 
6423                 if(row.cellObjects.length){
6424                     Roo.each(row.cellObjects, function(r){
6425                         _this.renderCellObject(r);
6426                     })
6427                 }
6428                 
6429             }, this);
6430         }
6431         
6432         Roo.each(this.el.select('tbody td', true).elements, function(e){
6433             e.on('mouseover', _this.onMouseover, _this);
6434         });
6435         
6436         Roo.each(this.el.select('tbody td', true).elements, function(e){
6437             e.on('mouseout', _this.onMouseout, _this);
6438         });
6439         this.fireEvent('rowsrendered', this);
6440         //if(this.loadMask){
6441         //    this.maskEl.hide();
6442         //}
6443         
6444         this.autoSize();
6445     },
6446     
6447     
6448     onUpdate : function(ds,record)
6449     {
6450         this.refreshRow(record);
6451         this.autoSize();
6452     },
6453     
6454     onRemove : function(ds, record, index, isUpdate){
6455         if(isUpdate !== true){
6456             this.fireEvent("beforerowremoved", this, index, record);
6457         }
6458         var bt = this.mainBody.dom;
6459         
6460         var rows = this.el.select('tbody > tr', true).elements;
6461         
6462         if(typeof(rows[index]) != 'undefined'){
6463             bt.removeChild(rows[index].dom);
6464         }
6465         
6466 //        if(bt.rows[index]){
6467 //            bt.removeChild(bt.rows[index]);
6468 //        }
6469         
6470         if(isUpdate !== true){
6471             //this.stripeRows(index);
6472             //this.syncRowHeights(index, index);
6473             //this.layout();
6474             this.fireEvent("rowremoved", this, index, record);
6475         }
6476     },
6477     
6478     onAdd : function(ds, records, rowIndex)
6479     {
6480         //Roo.log('on Add called');
6481         // - note this does not handle multiple adding very well..
6482         var bt = this.mainBody.dom;
6483         for (var i =0 ; i < records.length;i++) {
6484             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6485             //Roo.log(records[i]);
6486             //Roo.log(this.store.getAt(rowIndex+i));
6487             this.insertRow(this.store, rowIndex + i, false);
6488             return;
6489         }
6490         
6491     },
6492     
6493     
6494     refreshRow : function(record){
6495         var ds = this.store, index;
6496         if(typeof record == 'number'){
6497             index = record;
6498             record = ds.getAt(index);
6499         }else{
6500             index = ds.indexOf(record);
6501         }
6502         this.insertRow(ds, index, true);
6503         this.autoSize();
6504         this.onRemove(ds, record, index+1, true);
6505         this.autoSize();
6506         //this.syncRowHeights(index, index);
6507         //this.layout();
6508         this.fireEvent("rowupdated", this, index, record);
6509     },
6510     
6511     insertRow : function(dm, rowIndex, isUpdate){
6512         
6513         if(!isUpdate){
6514             this.fireEvent("beforerowsinserted", this, rowIndex);
6515         }
6516             //var s = this.getScrollState();
6517         var row = this.renderRow(this.cm, this.store, rowIndex);
6518         // insert before rowIndex..
6519         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6520         
6521         var _this = this;
6522                 
6523         if(row.cellObjects.length){
6524             Roo.each(row.cellObjects, function(r){
6525                 _this.renderCellObject(r);
6526             })
6527         }
6528             
6529         if(!isUpdate){
6530             this.fireEvent("rowsinserted", this, rowIndex);
6531             //this.syncRowHeights(firstRow, lastRow);
6532             //this.stripeRows(firstRow);
6533             //this.layout();
6534         }
6535         
6536     },
6537     
6538     
6539     getRowDom : function(rowIndex)
6540     {
6541         var rows = this.el.select('tbody > tr', true).elements;
6542         
6543         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6544         
6545     },
6546     // returns the object tree for a tr..
6547   
6548     
6549     renderRow : function(cm, ds, rowIndex) 
6550     {
6551         
6552         var d = ds.getAt(rowIndex);
6553         
6554         var row = {
6555             tag : 'tr',
6556             cn : []
6557         };
6558             
6559         var cellObjects = [];
6560         
6561         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6562             var config = cm.config[i];
6563             
6564             var renderer = cm.getRenderer(i);
6565             var value = '';
6566             var id = false;
6567             
6568             if(typeof(renderer) !== 'undefined'){
6569                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6570             }
6571             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6572             // and are rendered into the cells after the row is rendered - using the id for the element.
6573             
6574             if(typeof(value) === 'object'){
6575                 id = Roo.id();
6576                 cellObjects.push({
6577                     container : id,
6578                     cfg : value 
6579                 })
6580             }
6581             
6582             var rowcfg = {
6583                 record: d,
6584                 rowIndex : rowIndex,
6585                 colIndex : i,
6586                 rowClass : ''
6587             };
6588
6589             this.fireEvent('rowclass', this, rowcfg);
6590             
6591             var td = {
6592                 tag: 'td',
6593                 cls : rowcfg.rowClass,
6594                 style: '',
6595                 html: (typeof(value) === 'object') ? '' : value
6596             };
6597             
6598             if (id) {
6599                 td.id = id;
6600             }
6601             
6602             if(typeof(config.colspan) != 'undefined'){
6603                 td.colspan = config.colspan;
6604             }
6605             
6606             if(typeof(config.hidden) != 'undefined' && config.hidden){
6607                 td.style += ' display:none;';
6608             }
6609             
6610             if(typeof(config.align) != 'undefined' && config.align.length){
6611                 td.style += ' text-align:' + config.align + ';';
6612             }
6613             
6614             if(typeof(config.width) != 'undefined'){
6615                 td.style += ' width:' +  config.width + 'px;';
6616             }
6617             
6618             if(typeof(config.cursor) != 'undefined'){
6619                 td.style += ' cursor:' +  config.cursor + ';';
6620             }
6621             
6622             if(typeof(config.cls) != 'undefined'){
6623                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6624             }
6625             
6626             ['xs','sm','md','lg'].map(function(size){
6627                 
6628                 if(typeof(config[size]) == 'undefined'){
6629                     return;
6630                 }
6631                 
6632                 if (!config[size]) { // 0 = hidden
6633                     td.cls += ' hidden-' + size;
6634                     return;
6635                 }
6636                 
6637                 td.cls += ' col-' + size + '-' + config[size];
6638
6639             });
6640              
6641             row.cn.push(td);
6642            
6643         }
6644         
6645         row.cellObjects = cellObjects;
6646         
6647         return row;
6648           
6649     },
6650     
6651     
6652     
6653     onBeforeLoad : function()
6654     {
6655         //Roo.log('ds onBeforeLoad');
6656         
6657         //this.clear();
6658         
6659         //if(this.loadMask){
6660         //    this.maskEl.show();
6661         //}
6662     },
6663      /**
6664      * Remove all rows
6665      */
6666     clear : function()
6667     {
6668         this.el.select('tbody', true).first().dom.innerHTML = '';
6669     },
6670     /**
6671      * Show or hide a row.
6672      * @param {Number} rowIndex to show or hide
6673      * @param {Boolean} state hide
6674      */
6675     setRowVisibility : function(rowIndex, state)
6676     {
6677         var bt = this.mainBody.dom;
6678         
6679         var rows = this.el.select('tbody > tr', true).elements;
6680         
6681         if(typeof(rows[rowIndex]) == 'undefined'){
6682             return;
6683         }
6684         rows[rowIndex].dom.style.display = state ? '' : 'none';
6685     },
6686     
6687     
6688     getSelectionModel : function(){
6689         if(!this.selModel){
6690             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6691         }
6692         return this.selModel;
6693     },
6694     /*
6695      * Render the Roo.bootstrap object from renderder
6696      */
6697     renderCellObject : function(r)
6698     {
6699         var _this = this;
6700         
6701         var t = r.cfg.render(r.container);
6702         
6703         if(r.cfg.cn){
6704             Roo.each(r.cfg.cn, function(c){
6705                 var child = {
6706                     container: t.getChildContainer(),
6707                     cfg: c
6708                 };
6709                 _this.renderCellObject(child);
6710             })
6711         }
6712     },
6713     
6714     getRowIndex : function(row)
6715     {
6716         var rowIndex = -1;
6717         
6718         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6719             if(el != row){
6720                 return;
6721             }
6722             
6723             rowIndex = index;
6724         });
6725         
6726         return rowIndex;
6727     },
6728      /**
6729      * Returns the grid's underlying element = used by panel.Grid
6730      * @return {Element} The element
6731      */
6732     getGridEl : function(){
6733         return this.el;
6734     },
6735      /**
6736      * Forces a resize - used by panel.Grid
6737      * @return {Element} The element
6738      */
6739     autoSize : function()
6740     {
6741         //var ctr = Roo.get(this.container.dom.parentElement);
6742         var ctr = Roo.get(this.el.dom);
6743         
6744         var thd = this.getGridEl().select('thead',true).first();
6745         var tbd = this.getGridEl().select('tbody', true).first();
6746         var tfd = this.getGridEl().select('tfoot', true).first();
6747         
6748         var cw = ctr.getWidth();
6749         
6750         if (tbd) {
6751             
6752             tbd.setSize(ctr.getWidth(),
6753                         ctr.getHeight() - (thd.getHeight() + (tfd ? tfd.getHeight() : 0))
6754             );
6755             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6756             cw -= barsize;
6757         }
6758         cw = Math.max(cw, this.totalWidth);
6759         this.getGridEl().select('tr',true).setWidth(cw);
6760         // resize 'expandable coloumn?
6761         
6762         return; // we doe not have a view in this design..
6763         
6764     },
6765     onBodyScroll: function()
6766     {
6767         
6768         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6769         this.mainHead.setStyle({
6770                     'position' : 'relative',
6771                     'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6772         });
6773         
6774         
6775     }
6776 });
6777
6778  
6779
6780  /*
6781  * - LGPL
6782  *
6783  * table cell
6784  * 
6785  */
6786
6787 /**
6788  * @class Roo.bootstrap.TableCell
6789  * @extends Roo.bootstrap.Component
6790  * Bootstrap TableCell class
6791  * @cfg {String} html cell contain text
6792  * @cfg {String} cls cell class
6793  * @cfg {String} tag cell tag (td|th) default td
6794  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6795  * @cfg {String} align Aligns the content in a cell
6796  * @cfg {String} axis Categorizes cells
6797  * @cfg {String} bgcolor Specifies the background color of a cell
6798  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6799  * @cfg {Number} colspan Specifies the number of columns a cell should span
6800  * @cfg {String} headers Specifies one or more header cells a cell is related to
6801  * @cfg {Number} height Sets the height of a cell
6802  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6803  * @cfg {Number} rowspan Sets the number of rows a cell should span
6804  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6805  * @cfg {String} valign Vertical aligns the content in a cell
6806  * @cfg {Number} width Specifies the width of a cell
6807  * 
6808  * @constructor
6809  * Create a new TableCell
6810  * @param {Object} config The config object
6811  */
6812
6813 Roo.bootstrap.TableCell = function(config){
6814     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6815 };
6816
6817 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6818     
6819     html: false,
6820     cls: false,
6821     tag: false,
6822     abbr: false,
6823     align: false,
6824     axis: false,
6825     bgcolor: false,
6826     charoff: false,
6827     colspan: false,
6828     headers: false,
6829     height: false,
6830     nowrap: false,
6831     rowspan: false,
6832     scope: false,
6833     valign: false,
6834     width: false,
6835     
6836     
6837     getAutoCreate : function(){
6838         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6839         
6840         cfg = {
6841             tag: 'td'
6842         };
6843         
6844         if(this.tag){
6845             cfg.tag = this.tag;
6846         }
6847         
6848         if (this.html) {
6849             cfg.html=this.html
6850         }
6851         if (this.cls) {
6852             cfg.cls=this.cls
6853         }
6854         if (this.abbr) {
6855             cfg.abbr=this.abbr
6856         }
6857         if (this.align) {
6858             cfg.align=this.align
6859         }
6860         if (this.axis) {
6861             cfg.axis=this.axis
6862         }
6863         if (this.bgcolor) {
6864             cfg.bgcolor=this.bgcolor
6865         }
6866         if (this.charoff) {
6867             cfg.charoff=this.charoff
6868         }
6869         if (this.colspan) {
6870             cfg.colspan=this.colspan
6871         }
6872         if (this.headers) {
6873             cfg.headers=this.headers
6874         }
6875         if (this.height) {
6876             cfg.height=this.height
6877         }
6878         if (this.nowrap) {
6879             cfg.nowrap=this.nowrap
6880         }
6881         if (this.rowspan) {
6882             cfg.rowspan=this.rowspan
6883         }
6884         if (this.scope) {
6885             cfg.scope=this.scope
6886         }
6887         if (this.valign) {
6888             cfg.valign=this.valign
6889         }
6890         if (this.width) {
6891             cfg.width=this.width
6892         }
6893         
6894         
6895         return cfg;
6896     }
6897    
6898 });
6899
6900  
6901
6902  /*
6903  * - LGPL
6904  *
6905  * table row
6906  * 
6907  */
6908
6909 /**
6910  * @class Roo.bootstrap.TableRow
6911  * @extends Roo.bootstrap.Component
6912  * Bootstrap TableRow class
6913  * @cfg {String} cls row class
6914  * @cfg {String} align Aligns the content in a table row
6915  * @cfg {String} bgcolor Specifies a background color for a table row
6916  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6917  * @cfg {String} valign Vertical aligns the content in a table row
6918  * 
6919  * @constructor
6920  * Create a new TableRow
6921  * @param {Object} config The config object
6922  */
6923
6924 Roo.bootstrap.TableRow = function(config){
6925     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6926 };
6927
6928 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6929     
6930     cls: false,
6931     align: false,
6932     bgcolor: false,
6933     charoff: false,
6934     valign: false,
6935     
6936     getAutoCreate : function(){
6937         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6938         
6939         cfg = {
6940             tag: 'tr'
6941         };
6942             
6943         if(this.cls){
6944             cfg.cls = this.cls;
6945         }
6946         if(this.align){
6947             cfg.align = this.align;
6948         }
6949         if(this.bgcolor){
6950             cfg.bgcolor = this.bgcolor;
6951         }
6952         if(this.charoff){
6953             cfg.charoff = this.charoff;
6954         }
6955         if(this.valign){
6956             cfg.valign = this.valign;
6957         }
6958         
6959         return cfg;
6960     }
6961    
6962 });
6963
6964  
6965
6966  /*
6967  * - LGPL
6968  *
6969  * table body
6970  * 
6971  */
6972
6973 /**
6974  * @class Roo.bootstrap.TableBody
6975  * @extends Roo.bootstrap.Component
6976  * Bootstrap TableBody class
6977  * @cfg {String} cls element class
6978  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6979  * @cfg {String} align Aligns the content inside the element
6980  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6981  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6982  * 
6983  * @constructor
6984  * Create a new TableBody
6985  * @param {Object} config The config object
6986  */
6987
6988 Roo.bootstrap.TableBody = function(config){
6989     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6990 };
6991
6992 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6993     
6994     cls: false,
6995     tag: false,
6996     align: false,
6997     charoff: false,
6998     valign: false,
6999     
7000     getAutoCreate : function(){
7001         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7002         
7003         cfg = {
7004             tag: 'tbody'
7005         };
7006             
7007         if (this.cls) {
7008             cfg.cls=this.cls
7009         }
7010         if(this.tag){
7011             cfg.tag = this.tag;
7012         }
7013         
7014         if(this.align){
7015             cfg.align = this.align;
7016         }
7017         if(this.charoff){
7018             cfg.charoff = this.charoff;
7019         }
7020         if(this.valign){
7021             cfg.valign = this.valign;
7022         }
7023         
7024         return cfg;
7025     }
7026     
7027     
7028 //    initEvents : function()
7029 //    {
7030 //        
7031 //        if(!this.store){
7032 //            return;
7033 //        }
7034 //        
7035 //        this.store = Roo.factory(this.store, Roo.data);
7036 //        this.store.on('load', this.onLoad, this);
7037 //        
7038 //        this.store.load();
7039 //        
7040 //    },
7041 //    
7042 //    onLoad: function () 
7043 //    {   
7044 //        this.fireEvent('load', this);
7045 //    }
7046 //    
7047 //   
7048 });
7049
7050  
7051
7052  /*
7053  * Based on:
7054  * Ext JS Library 1.1.1
7055  * Copyright(c) 2006-2007, Ext JS, LLC.
7056  *
7057  * Originally Released Under LGPL - original licence link has changed is not relivant.
7058  *
7059  * Fork - LGPL
7060  * <script type="text/javascript">
7061  */
7062
7063 // as we use this in bootstrap.
7064 Roo.namespace('Roo.form');
7065  /**
7066  * @class Roo.form.Action
7067  * Internal Class used to handle form actions
7068  * @constructor
7069  * @param {Roo.form.BasicForm} el The form element or its id
7070  * @param {Object} config Configuration options
7071  */
7072
7073  
7074  
7075 // define the action interface
7076 Roo.form.Action = function(form, options){
7077     this.form = form;
7078     this.options = options || {};
7079 };
7080 /**
7081  * Client Validation Failed
7082  * @const 
7083  */
7084 Roo.form.Action.CLIENT_INVALID = 'client';
7085 /**
7086  * Server Validation Failed
7087  * @const 
7088  */
7089 Roo.form.Action.SERVER_INVALID = 'server';
7090  /**
7091  * Connect to Server Failed
7092  * @const 
7093  */
7094 Roo.form.Action.CONNECT_FAILURE = 'connect';
7095 /**
7096  * Reading Data from Server Failed
7097  * @const 
7098  */
7099 Roo.form.Action.LOAD_FAILURE = 'load';
7100
7101 Roo.form.Action.prototype = {
7102     type : 'default',
7103     failureType : undefined,
7104     response : undefined,
7105     result : undefined,
7106
7107     // interface method
7108     run : function(options){
7109
7110     },
7111
7112     // interface method
7113     success : function(response){
7114
7115     },
7116
7117     // interface method
7118     handleResponse : function(response){
7119
7120     },
7121
7122     // default connection failure
7123     failure : function(response){
7124         
7125         this.response = response;
7126         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7127         this.form.afterAction(this, false);
7128     },
7129
7130     processResponse : function(response){
7131         this.response = response;
7132         if(!response.responseText){
7133             return true;
7134         }
7135         this.result = this.handleResponse(response);
7136         return this.result;
7137     },
7138
7139     // utility functions used internally
7140     getUrl : function(appendParams){
7141         var url = this.options.url || this.form.url || this.form.el.dom.action;
7142         if(appendParams){
7143             var p = this.getParams();
7144             if(p){
7145                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7146             }
7147         }
7148         return url;
7149     },
7150
7151     getMethod : function(){
7152         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7153     },
7154
7155     getParams : function(){
7156         var bp = this.form.baseParams;
7157         var p = this.options.params;
7158         if(p){
7159             if(typeof p == "object"){
7160                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7161             }else if(typeof p == 'string' && bp){
7162                 p += '&' + Roo.urlEncode(bp);
7163             }
7164         }else if(bp){
7165             p = Roo.urlEncode(bp);
7166         }
7167         return p;
7168     },
7169
7170     createCallback : function(){
7171         return {
7172             success: this.success,
7173             failure: this.failure,
7174             scope: this,
7175             timeout: (this.form.timeout*1000),
7176             upload: this.form.fileUpload ? this.success : undefined
7177         };
7178     }
7179 };
7180
7181 Roo.form.Action.Submit = function(form, options){
7182     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7183 };
7184
7185 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7186     type : 'submit',
7187
7188     haveProgress : false,
7189     uploadComplete : false,
7190     
7191     // uploadProgress indicator.
7192     uploadProgress : function()
7193     {
7194         if (!this.form.progressUrl) {
7195             return;
7196         }
7197         
7198         if (!this.haveProgress) {
7199             Roo.MessageBox.progress("Uploading", "Uploading");
7200         }
7201         if (this.uploadComplete) {
7202            Roo.MessageBox.hide();
7203            return;
7204         }
7205         
7206         this.haveProgress = true;
7207    
7208         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7209         
7210         var c = new Roo.data.Connection();
7211         c.request({
7212             url : this.form.progressUrl,
7213             params: {
7214                 id : uid
7215             },
7216             method: 'GET',
7217             success : function(req){
7218                //console.log(data);
7219                 var rdata = false;
7220                 var edata;
7221                 try  {
7222                    rdata = Roo.decode(req.responseText)
7223                 } catch (e) {
7224                     Roo.log("Invalid data from server..");
7225                     Roo.log(edata);
7226                     return;
7227                 }
7228                 if (!rdata || !rdata.success) {
7229                     Roo.log(rdata);
7230                     Roo.MessageBox.alert(Roo.encode(rdata));
7231                     return;
7232                 }
7233                 var data = rdata.data;
7234                 
7235                 if (this.uploadComplete) {
7236                    Roo.MessageBox.hide();
7237                    return;
7238                 }
7239                    
7240                 if (data){
7241                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7242                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7243                     );
7244                 }
7245                 this.uploadProgress.defer(2000,this);
7246             },
7247        
7248             failure: function(data) {
7249                 Roo.log('progress url failed ');
7250                 Roo.log(data);
7251             },
7252             scope : this
7253         });
7254            
7255     },
7256     
7257     
7258     run : function()
7259     {
7260         // run get Values on the form, so it syncs any secondary forms.
7261         this.form.getValues();
7262         
7263         var o = this.options;
7264         var method = this.getMethod();
7265         var isPost = method == 'POST';
7266         if(o.clientValidation === false || this.form.isValid()){
7267             
7268             if (this.form.progressUrl) {
7269                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7270                     (new Date() * 1) + '' + Math.random());
7271                     
7272             } 
7273             
7274             
7275             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7276                 form:this.form.el.dom,
7277                 url:this.getUrl(!isPost),
7278                 method: method,
7279                 params:isPost ? this.getParams() : null,
7280                 isUpload: this.form.fileUpload
7281             }));
7282             
7283             this.uploadProgress();
7284
7285         }else if (o.clientValidation !== false){ // client validation failed
7286             this.failureType = Roo.form.Action.CLIENT_INVALID;
7287             this.form.afterAction(this, false);
7288         }
7289     },
7290
7291     success : function(response)
7292     {
7293         this.uploadComplete= true;
7294         if (this.haveProgress) {
7295             Roo.MessageBox.hide();
7296         }
7297         
7298         
7299         var result = this.processResponse(response);
7300         if(result === true || result.success){
7301             this.form.afterAction(this, true);
7302             return;
7303         }
7304         if(result.errors){
7305             this.form.markInvalid(result.errors);
7306             this.failureType = Roo.form.Action.SERVER_INVALID;
7307         }
7308         this.form.afterAction(this, false);
7309     },
7310     failure : function(response)
7311     {
7312         this.uploadComplete= true;
7313         if (this.haveProgress) {
7314             Roo.MessageBox.hide();
7315         }
7316         
7317         this.response = response;
7318         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7319         this.form.afterAction(this, false);
7320     },
7321     
7322     handleResponse : function(response){
7323         if(this.form.errorReader){
7324             var rs = this.form.errorReader.read(response);
7325             var errors = [];
7326             if(rs.records){
7327                 for(var i = 0, len = rs.records.length; i < len; i++) {
7328                     var r = rs.records[i];
7329                     errors[i] = r.data;
7330                 }
7331             }
7332             if(errors.length < 1){
7333                 errors = null;
7334             }
7335             return {
7336                 success : rs.success,
7337                 errors : errors
7338             };
7339         }
7340         var ret = false;
7341         try {
7342             ret = Roo.decode(response.responseText);
7343         } catch (e) {
7344             ret = {
7345                 success: false,
7346                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7347                 errors : []
7348             };
7349         }
7350         return ret;
7351         
7352     }
7353 });
7354
7355
7356 Roo.form.Action.Load = function(form, options){
7357     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7358     this.reader = this.form.reader;
7359 };
7360
7361 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7362     type : 'load',
7363
7364     run : function(){
7365         
7366         Roo.Ajax.request(Roo.apply(
7367                 this.createCallback(), {
7368                     method:this.getMethod(),
7369                     url:this.getUrl(false),
7370                     params:this.getParams()
7371         }));
7372     },
7373
7374     success : function(response){
7375         
7376         var result = this.processResponse(response);
7377         if(result === true || !result.success || !result.data){
7378             this.failureType = Roo.form.Action.LOAD_FAILURE;
7379             this.form.afterAction(this, false);
7380             return;
7381         }
7382         this.form.clearInvalid();
7383         this.form.setValues(result.data);
7384         this.form.afterAction(this, true);
7385     },
7386
7387     handleResponse : function(response){
7388         if(this.form.reader){
7389             var rs = this.form.reader.read(response);
7390             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7391             return {
7392                 success : rs.success,
7393                 data : data
7394             };
7395         }
7396         return Roo.decode(response.responseText);
7397     }
7398 });
7399
7400 Roo.form.Action.ACTION_TYPES = {
7401     'load' : Roo.form.Action.Load,
7402     'submit' : Roo.form.Action.Submit
7403 };/*
7404  * - LGPL
7405  *
7406  * form
7407  *
7408  */
7409
7410 /**
7411  * @class Roo.bootstrap.Form
7412  * @extends Roo.bootstrap.Component
7413  * Bootstrap Form class
7414  * @cfg {String} method  GET | POST (default POST)
7415  * @cfg {String} labelAlign top | left (default top)
7416  * @cfg {String} align left  | right - for navbars
7417  * @cfg {Boolean} loadMask load mask when submit (default true)
7418
7419  *
7420  * @constructor
7421  * Create a new Form
7422  * @param {Object} config The config object
7423  */
7424
7425
7426 Roo.bootstrap.Form = function(config){
7427     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7428     this.addEvents({
7429         /**
7430          * @event clientvalidation
7431          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7432          * @param {Form} this
7433          * @param {Boolean} valid true if the form has passed client-side validation
7434          */
7435         clientvalidation: true,
7436         /**
7437          * @event beforeaction
7438          * Fires before any action is performed. Return false to cancel the action.
7439          * @param {Form} this
7440          * @param {Action} action The action to be performed
7441          */
7442         beforeaction: true,
7443         /**
7444          * @event actionfailed
7445          * Fires when an action fails.
7446          * @param {Form} this
7447          * @param {Action} action The action that failed
7448          */
7449         actionfailed : true,
7450         /**
7451          * @event actioncomplete
7452          * Fires when an action is completed.
7453          * @param {Form} this
7454          * @param {Action} action The action that completed
7455          */
7456         actioncomplete : true
7457     });
7458
7459 };
7460
7461 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7462
7463      /**
7464      * @cfg {String} method
7465      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7466      */
7467     method : 'POST',
7468     /**
7469      * @cfg {String} url
7470      * The URL to use for form actions if one isn't supplied in the action options.
7471      */
7472     /**
7473      * @cfg {Boolean} fileUpload
7474      * Set to true if this form is a file upload.
7475      */
7476
7477     /**
7478      * @cfg {Object} baseParams
7479      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7480      */
7481
7482     /**
7483      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7484      */
7485     timeout: 30,
7486     /**
7487      * @cfg {Sting} align (left|right) for navbar forms
7488      */
7489     align : 'left',
7490
7491     // private
7492     activeAction : null,
7493
7494     /**
7495      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7496      * element by passing it or its id or mask the form itself by passing in true.
7497      * @type Mixed
7498      */
7499     waitMsgTarget : false,
7500
7501     loadMask : true,
7502
7503     getAutoCreate : function(){
7504
7505         var cfg = {
7506             tag: 'form',
7507             method : this.method || 'POST',
7508             id : this.id || Roo.id(),
7509             cls : ''
7510         };
7511         if (this.parent().xtype.match(/^Nav/)) {
7512             cfg.cls = 'navbar-form navbar-' + this.align;
7513
7514         }
7515
7516         if (this.labelAlign == 'left' ) {
7517             cfg.cls += ' form-horizontal';
7518         }
7519
7520
7521         return cfg;
7522     },
7523     initEvents : function()
7524     {
7525         this.el.on('submit', this.onSubmit, this);
7526         // this was added as random key presses on the form where triggering form submit.
7527         this.el.on('keypress', function(e) {
7528             if (e.getCharCode() != 13) {
7529                 return true;
7530             }
7531             // we might need to allow it for textareas.. and some other items.
7532             // check e.getTarget().
7533
7534             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7535                 return true;
7536             }
7537
7538             Roo.log("keypress blocked");
7539
7540             e.preventDefault();
7541             return false;
7542         });
7543
7544     },
7545     // private
7546     onSubmit : function(e){
7547         e.stopEvent();
7548     },
7549
7550      /**
7551      * Returns true if client-side validation on the form is successful.
7552      * @return Boolean
7553      */
7554     isValid : function(){
7555         var items = this.getItems();
7556         var valid = true;
7557         items.each(function(f){
7558            if(!f.validate()){
7559                valid = false;
7560
7561            }
7562         });
7563         return valid;
7564     },
7565     /**
7566      * Returns true if any fields in this form have changed since their original load.
7567      * @return Boolean
7568      */
7569     isDirty : function(){
7570         var dirty = false;
7571         var items = this.getItems();
7572         items.each(function(f){
7573            if(f.isDirty()){
7574                dirty = true;
7575                return false;
7576            }
7577            return true;
7578         });
7579         return dirty;
7580     },
7581      /**
7582      * Performs a predefined action (submit or load) or custom actions you define on this form.
7583      * @param {String} actionName The name of the action type
7584      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7585      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7586      * accept other config options):
7587      * <pre>
7588 Property          Type             Description
7589 ----------------  ---------------  ----------------------------------------------------------------------------------
7590 url               String           The url for the action (defaults to the form's url)
7591 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7592 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7593 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7594                                    validate the form on the client (defaults to false)
7595      * </pre>
7596      * @return {BasicForm} this
7597      */
7598     doAction : function(action, options){
7599         if(typeof action == 'string'){
7600             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7601         }
7602         if(this.fireEvent('beforeaction', this, action) !== false){
7603             this.beforeAction(action);
7604             action.run.defer(100, action);
7605         }
7606         return this;
7607     },
7608
7609     // private
7610     beforeAction : function(action){
7611         var o = action.options;
7612
7613         if(this.loadMask){
7614             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7615         }
7616         // not really supported yet.. ??
7617
7618         //if(this.waitMsgTarget === true){
7619         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7620         //}else if(this.waitMsgTarget){
7621         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7622         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7623         //}else {
7624         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7625        // }
7626
7627     },
7628
7629     // private
7630     afterAction : function(action, success){
7631         this.activeAction = null;
7632         var o = action.options;
7633
7634         //if(this.waitMsgTarget === true){
7635             this.el.unmask();
7636         //}else if(this.waitMsgTarget){
7637         //    this.waitMsgTarget.unmask();
7638         //}else{
7639         //    Roo.MessageBox.updateProgress(1);
7640         //    Roo.MessageBox.hide();
7641        // }
7642         //
7643         if(success){
7644             if(o.reset){
7645                 this.reset();
7646             }
7647             Roo.callback(o.success, o.scope, [this, action]);
7648             this.fireEvent('actioncomplete', this, action);
7649
7650         }else{
7651
7652             // failure condition..
7653             // we have a scenario where updates need confirming.
7654             // eg. if a locking scenario exists..
7655             // we look for { errors : { needs_confirm : true }} in the response.
7656             if (
7657                 (typeof(action.result) != 'undefined')  &&
7658                 (typeof(action.result.errors) != 'undefined')  &&
7659                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7660            ){
7661                 var _t = this;
7662                 Roo.log("not supported yet");
7663                  /*
7664
7665                 Roo.MessageBox.confirm(
7666                     "Change requires confirmation",
7667                     action.result.errorMsg,
7668                     function(r) {
7669                         if (r != 'yes') {
7670                             return;
7671                         }
7672                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7673                     }
7674
7675                 );
7676                 */
7677
7678
7679                 return;
7680             }
7681
7682             Roo.callback(o.failure, o.scope, [this, action]);
7683             // show an error message if no failed handler is set..
7684             if (!this.hasListener('actionfailed')) {
7685                 Roo.log("need to add dialog support");
7686                 /*
7687                 Roo.MessageBox.alert("Error",
7688                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7689                         action.result.errorMsg :
7690                         "Saving Failed, please check your entries or try again"
7691                 );
7692                 */
7693             }
7694
7695             this.fireEvent('actionfailed', this, action);
7696         }
7697
7698     },
7699     /**
7700      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7701      * @param {String} id The value to search for
7702      * @return Field
7703      */
7704     findField : function(id){
7705         var items = this.getItems();
7706         var field = items.get(id);
7707         if(!field){
7708              items.each(function(f){
7709                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7710                     field = f;
7711                     return false;
7712                 }
7713                 return true;
7714             });
7715         }
7716         return field || null;
7717     },
7718      /**
7719      * Mark fields in this form invalid in bulk.
7720      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7721      * @return {BasicForm} this
7722      */
7723     markInvalid : function(errors){
7724         if(errors instanceof Array){
7725             for(var i = 0, len = errors.length; i < len; i++){
7726                 var fieldError = errors[i];
7727                 var f = this.findField(fieldError.id);
7728                 if(f){
7729                     f.markInvalid(fieldError.msg);
7730                 }
7731             }
7732         }else{
7733             var field, id;
7734             for(id in errors){
7735                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7736                     field.markInvalid(errors[id]);
7737                 }
7738             }
7739         }
7740         //Roo.each(this.childForms || [], function (f) {
7741         //    f.markInvalid(errors);
7742         //});
7743
7744         return this;
7745     },
7746
7747     /**
7748      * Set values for fields in this form in bulk.
7749      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7750      * @return {BasicForm} this
7751      */
7752     setValues : function(values){
7753         if(values instanceof Array){ // array of objects
7754             for(var i = 0, len = values.length; i < len; i++){
7755                 var v = values[i];
7756                 var f = this.findField(v.id);
7757                 if(f){
7758                     f.setValue(v.value);
7759                     if(this.trackResetOnLoad){
7760                         f.originalValue = f.getValue();
7761                     }
7762                 }
7763             }
7764         }else{ // object hash
7765             var field, id;
7766             for(id in values){
7767                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7768
7769                     if (field.setFromData &&
7770                         field.valueField &&
7771                         field.displayField &&
7772                         // combos' with local stores can
7773                         // be queried via setValue()
7774                         // to set their value..
7775                         (field.store && !field.store.isLocal)
7776                         ) {
7777                         // it's a combo
7778                         var sd = { };
7779                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7780                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7781                         field.setFromData(sd);
7782
7783                     } else {
7784                         field.setValue(values[id]);
7785                     }
7786
7787
7788                     if(this.trackResetOnLoad){
7789                         field.originalValue = field.getValue();
7790                     }
7791                 }
7792             }
7793         }
7794
7795         //Roo.each(this.childForms || [], function (f) {
7796         //    f.setValues(values);
7797         //});
7798
7799         return this;
7800     },
7801
7802     /**
7803      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7804      * they are returned as an array.
7805      * @param {Boolean} asString
7806      * @return {Object}
7807      */
7808     getValues : function(asString){
7809         //if (this.childForms) {
7810             // copy values from the child forms
7811         //    Roo.each(this.childForms, function (f) {
7812         //        this.setValues(f.getValues());
7813         //    }, this);
7814         //}
7815
7816
7817
7818         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7819         if(asString === true){
7820             return fs;
7821         }
7822         return Roo.urlDecode(fs);
7823     },
7824
7825     /**
7826      * Returns the fields in this form as an object with key/value pairs.
7827      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7828      * @return {Object}
7829      */
7830     getFieldValues : function(with_hidden)
7831     {
7832         var items = this.getItems();
7833         var ret = {};
7834         items.each(function(f){
7835             if (!f.getName()) {
7836                 return;
7837             }
7838             var v = f.getValue();
7839             if (f.inputType =='radio') {
7840                 if (typeof(ret[f.getName()]) == 'undefined') {
7841                     ret[f.getName()] = ''; // empty..
7842                 }
7843
7844                 if (!f.el.dom.checked) {
7845                     return;
7846
7847                 }
7848                 v = f.el.dom.value;
7849
7850             }
7851
7852             // not sure if this supported any more..
7853             if ((typeof(v) == 'object') && f.getRawValue) {
7854                 v = f.getRawValue() ; // dates..
7855             }
7856             // combo boxes where name != hiddenName...
7857             if (f.name != f.getName()) {
7858                 ret[f.name] = f.getRawValue();
7859             }
7860             ret[f.getName()] = v;
7861         });
7862
7863         return ret;
7864     },
7865
7866     /**
7867      * Clears all invalid messages in this form.
7868      * @return {BasicForm} this
7869      */
7870     clearInvalid : function(){
7871         var items = this.getItems();
7872
7873         items.each(function(f){
7874            f.clearInvalid();
7875         });
7876
7877
7878
7879         return this;
7880     },
7881
7882     /**
7883      * Resets this form.
7884      * @return {BasicForm} this
7885      */
7886     reset : function(){
7887         var items = this.getItems();
7888         items.each(function(f){
7889             f.reset();
7890         });
7891
7892         Roo.each(this.childForms || [], function (f) {
7893             f.reset();
7894         });
7895
7896
7897         return this;
7898     },
7899     getItems : function()
7900     {
7901         var r=new Roo.util.MixedCollection(false, function(o){
7902             return o.id || (o.id = Roo.id());
7903         });
7904         var iter = function(el) {
7905             if (el.inputEl) {
7906                 r.add(el);
7907             }
7908             if (!el.items) {
7909                 return;
7910             }
7911             Roo.each(el.items,function(e) {
7912                 iter(e);
7913             });
7914
7915
7916         };
7917
7918         iter(this);
7919         return r;
7920
7921
7922
7923
7924     }
7925
7926 });
7927 /*
7928  * Based on:
7929  * Ext JS Library 1.1.1
7930  * Copyright(c) 2006-2007, Ext JS, LLC.
7931  *
7932  * Originally Released Under LGPL - original licence link has changed is not relivant.
7933  *
7934  * Fork - LGPL
7935  * <script type="text/javascript">
7936  */
7937 /**
7938  * @class Roo.form.VTypes
7939  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7940  * @singleton
7941  */
7942 Roo.form.VTypes = function(){
7943     // closure these in so they are only created once.
7944     var alpha = /^[a-zA-Z_]+$/;
7945     var alphanum = /^[a-zA-Z0-9_]+$/;
7946     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7947     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7948
7949     // All these messages and functions are configurable
7950     return {
7951         /**
7952          * The function used to validate email addresses
7953          * @param {String} value The email address
7954          */
7955         'email' : function(v){
7956             return email.test(v);
7957         },
7958         /**
7959          * The error text to display when the email validation function returns false
7960          * @type String
7961          */
7962         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7963         /**
7964          * The keystroke filter mask to be applied on email input
7965          * @type RegExp
7966          */
7967         'emailMask' : /[a-z0-9_\.\-@]/i,
7968
7969         /**
7970          * The function used to validate URLs
7971          * @param {String} value The URL
7972          */
7973         'url' : function(v){
7974             return url.test(v);
7975         },
7976         /**
7977          * The error text to display when the url validation function returns false
7978          * @type String
7979          */
7980         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7981         
7982         /**
7983          * The function used to validate alpha values
7984          * @param {String} value The value
7985          */
7986         'alpha' : function(v){
7987             return alpha.test(v);
7988         },
7989         /**
7990          * The error text to display when the alpha validation function returns false
7991          * @type String
7992          */
7993         'alphaText' : 'This field should only contain letters and _',
7994         /**
7995          * The keystroke filter mask to be applied on alpha input
7996          * @type RegExp
7997          */
7998         'alphaMask' : /[a-z_]/i,
7999
8000         /**
8001          * The function used to validate alphanumeric values
8002          * @param {String} value The value
8003          */
8004         'alphanum' : function(v){
8005             return alphanum.test(v);
8006         },
8007         /**
8008          * The error text to display when the alphanumeric validation function returns false
8009          * @type String
8010          */
8011         'alphanumText' : 'This field should only contain letters, numbers and _',
8012         /**
8013          * The keystroke filter mask to be applied on alphanumeric input
8014          * @type RegExp
8015          */
8016         'alphanumMask' : /[a-z0-9_]/i
8017     };
8018 }();/*
8019  * - LGPL
8020  *
8021  * Input
8022  * 
8023  */
8024
8025 /**
8026  * @class Roo.bootstrap.Input
8027  * @extends Roo.bootstrap.Component
8028  * Bootstrap Input class
8029  * @cfg {Boolean} disabled is it disabled
8030  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8031  * @cfg {String} name name of the input
8032  * @cfg {string} fieldLabel - the label associated
8033  * @cfg {string} placeholder - placeholder to put in text.
8034  * @cfg {string}  before - input group add on before
8035  * @cfg {string} after - input group add on after
8036  * @cfg {string} size - (lg|sm) or leave empty..
8037  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8038  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8039  * @cfg {Number} md colspan out of 12 for computer-sized screens
8040  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8041  * @cfg {string} value default value of the input
8042  * @cfg {Number} labelWidth set the width of label (0-12)
8043  * @cfg {String} labelAlign (top|left)
8044  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8045  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8046  * @cfg {String} indicatorpos (left|right) default left
8047
8048  * @cfg {String} align (left|center|right) Default left
8049  * @cfg {Boolean} forceFeedback (true|false) Default false
8050  * 
8051  * 
8052  * 
8053  * 
8054  * @constructor
8055  * Create a new Input
8056  * @param {Object} config The config object
8057  */
8058
8059 Roo.bootstrap.Input = function(config){
8060     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8061    
8062         this.addEvents({
8063             /**
8064              * @event focus
8065              * Fires when this field receives input focus.
8066              * @param {Roo.form.Field} this
8067              */
8068             focus : true,
8069             /**
8070              * @event blur
8071              * Fires when this field loses input focus.
8072              * @param {Roo.form.Field} this
8073              */
8074             blur : true,
8075             /**
8076              * @event specialkey
8077              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8078              * {@link Roo.EventObject#getKey} to determine which key was pressed.
8079              * @param {Roo.form.Field} this
8080              * @param {Roo.EventObject} e The event object
8081              */
8082             specialkey : true,
8083             /**
8084              * @event change
8085              * Fires just before the field blurs if the field value has changed.
8086              * @param {Roo.form.Field} this
8087              * @param {Mixed} newValue The new value
8088              * @param {Mixed} oldValue The original value
8089              */
8090             change : true,
8091             /**
8092              * @event invalid
8093              * Fires after the field has been marked as invalid.
8094              * @param {Roo.form.Field} this
8095              * @param {String} msg The validation message
8096              */
8097             invalid : true,
8098             /**
8099              * @event valid
8100              * Fires after the field has been validated with no errors.
8101              * @param {Roo.form.Field} this
8102              */
8103             valid : true,
8104              /**
8105              * @event keyup
8106              * Fires after the key up
8107              * @param {Roo.form.Field} this
8108              * @param {Roo.EventObject}  e The event Object
8109              */
8110             keyup : true
8111         });
8112 };
8113
8114 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8115      /**
8116      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8117       automatic validation (defaults to "keyup").
8118      */
8119     validationEvent : "keyup",
8120      /**
8121      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8122      */
8123     validateOnBlur : true,
8124     /**
8125      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8126      */
8127     validationDelay : 250,
8128      /**
8129      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8130      */
8131     focusClass : "x-form-focus",  // not needed???
8132     
8133        
8134     /**
8135      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8136      */
8137     invalidClass : "has-warning",
8138     
8139     /**
8140      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8141      */
8142     validClass : "has-success",
8143     
8144     /**
8145      * @cfg {Boolean} hasFeedback (true|false) default true
8146      */
8147     hasFeedback : true,
8148     
8149     /**
8150      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8151      */
8152     invalidFeedbackClass : "glyphicon-warning-sign",
8153     
8154     /**
8155      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8156      */
8157     validFeedbackClass : "glyphicon-ok",
8158     
8159     /**
8160      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8161      */
8162     selectOnFocus : false,
8163     
8164      /**
8165      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8166      */
8167     maskRe : null,
8168        /**
8169      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8170      */
8171     vtype : null,
8172     
8173       /**
8174      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8175      */
8176     disableKeyFilter : false,
8177     
8178        /**
8179      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8180      */
8181     disabled : false,
8182      /**
8183      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8184      */
8185     allowBlank : true,
8186     /**
8187      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8188      */
8189     blankText : "This field is required",
8190     
8191      /**
8192      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8193      */
8194     minLength : 0,
8195     /**
8196      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8197      */
8198     maxLength : Number.MAX_VALUE,
8199     /**
8200      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8201      */
8202     minLengthText : "The minimum length for this field is {0}",
8203     /**
8204      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8205      */
8206     maxLengthText : "The maximum length for this field is {0}",
8207   
8208     
8209     /**
8210      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8211      * If available, this function will be called only after the basic validators all return true, and will be passed the
8212      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8213      */
8214     validator : null,
8215     /**
8216      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8217      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8218      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8219      */
8220     regex : null,
8221     /**
8222      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8223      */
8224     regexText : "",
8225     
8226     autocomplete: false,
8227     
8228     
8229     fieldLabel : '',
8230     inputType : 'text',
8231     
8232     name : false,
8233     placeholder: false,
8234     before : false,
8235     after : false,
8236     size : false,
8237     hasFocus : false,
8238     preventMark: false,
8239     isFormField : true,
8240     value : '',
8241     labelWidth : 2,
8242     labelAlign : false,
8243     readOnly : false,
8244     align : false,
8245     formatedValue : false,
8246     forceFeedback : false,
8247     
8248     indicatorpos : 'left',
8249     
8250     parentLabelAlign : function()
8251     {
8252         var parent = this;
8253         while (parent.parent()) {
8254             parent = parent.parent();
8255             if (typeof(parent.labelAlign) !='undefined') {
8256                 return parent.labelAlign;
8257             }
8258         }
8259         return 'left';
8260         
8261     },
8262     
8263     getAutoCreate : function()
8264     {
8265         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8266         
8267         var id = Roo.id();
8268         
8269         var cfg = {};
8270         
8271         if(this.inputType != 'hidden'){
8272             cfg.cls = 'form-group' //input-group
8273         }
8274         
8275         var input =  {
8276             tag: 'input',
8277             id : id,
8278             type : this.inputType,
8279             value : this.value,
8280             cls : 'form-control',
8281             placeholder : this.placeholder || '',
8282             autocomplete : this.autocomplete || 'new-password'
8283         };
8284         
8285         if(this.align){
8286             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8287         }
8288         
8289         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8290             input.maxLength = this.maxLength;
8291         }
8292         
8293         if (this.disabled) {
8294             input.disabled=true;
8295         }
8296         
8297         if (this.readOnly) {
8298             input.readonly=true;
8299         }
8300         
8301         if (this.name) {
8302             input.name = this.name;
8303         }
8304         
8305         if (this.size) {
8306             input.cls += ' input-' + this.size;
8307         }
8308         
8309         var settings=this;
8310         ['xs','sm','md','lg'].map(function(size){
8311             if (settings[size]) {
8312                 cfg.cls += ' col-' + size + '-' + settings[size];
8313             }
8314         });
8315         
8316         var inputblock = input;
8317         
8318         var feedback = {
8319             tag: 'span',
8320             cls: 'glyphicon form-control-feedback'
8321         };
8322             
8323         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8324             
8325             inputblock = {
8326                 cls : 'has-feedback',
8327                 cn :  [
8328                     input,
8329                     feedback
8330                 ] 
8331             };  
8332         }
8333         
8334         if (this.before || this.after) {
8335             
8336             inputblock = {
8337                 cls : 'input-group',
8338                 cn :  [] 
8339             };
8340             
8341             if (this.before && typeof(this.before) == 'string') {
8342                 
8343                 inputblock.cn.push({
8344                     tag :'span',
8345                     cls : 'roo-input-before input-group-addon',
8346                     html : this.before
8347                 });
8348             }
8349             if (this.before && typeof(this.before) == 'object') {
8350                 this.before = Roo.factory(this.before);
8351                 
8352                 inputblock.cn.push({
8353                     tag :'span',
8354                     cls : 'roo-input-before input-group-' +
8355                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8356                 });
8357             }
8358             
8359             inputblock.cn.push(input);
8360             
8361             if (this.after && typeof(this.after) == 'string') {
8362                 inputblock.cn.push({
8363                     tag :'span',
8364                     cls : 'roo-input-after input-group-addon',
8365                     html : this.after
8366                 });
8367             }
8368             if (this.after && typeof(this.after) == 'object') {
8369                 this.after = Roo.factory(this.after);
8370                 
8371                 inputblock.cn.push({
8372                     tag :'span',
8373                     cls : 'roo-input-after input-group-' +
8374                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8375                 });
8376             }
8377             
8378             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8379                 inputblock.cls += ' has-feedback';
8380                 inputblock.cn.push(feedback);
8381             }
8382         };
8383         
8384         if (align ==='left' && this.fieldLabel.length) {
8385             
8386             cfg.cn = [
8387                 {
8388                     tag : 'i',
8389                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8390                     tooltip : 'This field is required'
8391                 },
8392                 {
8393                     tag: 'label',
8394                     'for' :  id,
8395                     cls : 'control-label col-sm-' + this.labelWidth,
8396                     html : this.fieldLabel
8397
8398                 },
8399                 {
8400                     cls : "col-sm-" + (12 - this.labelWidth), 
8401                     cn: [
8402                         inputblock
8403                     ]
8404                 }
8405
8406             ];
8407             
8408             if(this.indicatorpos == 'right'){
8409                 cfg.cn = [
8410                     {
8411                         tag: 'label',
8412                         'for' :  id,
8413                         cls : 'control-label col-sm-' + this.labelWidth,
8414                         html : this.fieldLabel
8415
8416                     },
8417                     {
8418                         tag : 'i',
8419                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8420                         tooltip : 'This field is required'
8421                     },
8422                     {
8423                         cls : "col-sm-" + (12 - this.labelWidth), 
8424                         cn: [
8425                             inputblock
8426                         ]
8427                     }
8428
8429                 ];
8430             }
8431             
8432         } else if ( this.fieldLabel.length) {
8433                 
8434             cfg.cn = [
8435                 {
8436                     tag : 'i',
8437                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8438                     tooltip : 'This field is required'
8439                 },
8440                 {
8441                     tag: 'label',
8442                    //cls : 'input-group-addon',
8443                     html : this.fieldLabel
8444
8445                 },
8446
8447                inputblock
8448
8449            ];
8450            
8451            if(this.indicatorpos == 'right'){
8452                 
8453                 cfg.cn = [
8454                     {
8455                         tag: 'label',
8456                        //cls : 'input-group-addon',
8457                         html : this.fieldLabel
8458
8459                     },
8460                     {
8461                         tag : 'i',
8462                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8463                         tooltip : 'This field is required'
8464                     },
8465
8466                    inputblock
8467
8468                ];
8469
8470             }
8471
8472         } else {
8473             
8474             cfg.cn = [
8475
8476                     inputblock
8477
8478             ];
8479                 
8480                 
8481         };
8482         
8483         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8484            cfg.cls += ' navbar-form';
8485         }
8486         
8487         if (this.parentType === 'NavGroup') {
8488            cfg.cls += ' navbar-form';
8489            cfg.tag = 'li';
8490         }
8491         
8492         return cfg;
8493         
8494     },
8495     /**
8496      * return the real input element.
8497      */
8498     inputEl: function ()
8499     {
8500         return this.el.select('input.form-control',true).first();
8501     },
8502     
8503     tooltipEl : function()
8504     {
8505         return this.inputEl();
8506     },
8507     
8508     indicatorEl : function()
8509     {
8510         var indicator = this.el.select('i.roo-required-indicator',true).first();
8511         
8512         if(!indicator){
8513             return false;
8514         }
8515         
8516         return indicator;
8517         
8518     },
8519     
8520     setDisabled : function(v)
8521     {
8522         var i  = this.inputEl().dom;
8523         if (!v) {
8524             i.removeAttribute('disabled');
8525             return;
8526             
8527         }
8528         i.setAttribute('disabled','true');
8529     },
8530     initEvents : function()
8531     {
8532           
8533         this.inputEl().on("keydown" , this.fireKey,  this);
8534         this.inputEl().on("focus", this.onFocus,  this);
8535         this.inputEl().on("blur", this.onBlur,  this);
8536         
8537         this.inputEl().relayEvent('keyup', this);
8538         
8539         this.indicator = this.indicatorEl();
8540         
8541         if(this.indicator){
8542             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8543             this.indicator.hide();
8544         }
8545  
8546         // reference to original value for reset
8547         this.originalValue = this.getValue();
8548         //Roo.form.TextField.superclass.initEvents.call(this);
8549         if(this.validationEvent == 'keyup'){
8550             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8551             this.inputEl().on('keyup', this.filterValidation, this);
8552         }
8553         else if(this.validationEvent !== false){
8554             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8555         }
8556         
8557         if(this.selectOnFocus){
8558             this.on("focus", this.preFocus, this);
8559             
8560         }
8561         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8562             this.inputEl().on("keypress", this.filterKeys, this);
8563         } else {
8564             this.inputEl().relayEvent('keypress', this);
8565         }
8566        /* if(this.grow){
8567             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8568             this.el.on("click", this.autoSize,  this);
8569         }
8570         */
8571         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8572             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8573         }
8574         
8575         if (typeof(this.before) == 'object') {
8576             this.before.render(this.el.select('.roo-input-before',true).first());
8577         }
8578         if (typeof(this.after) == 'object') {
8579             this.after.render(this.el.select('.roo-input-after',true).first());
8580         }
8581         
8582         
8583     },
8584     filterValidation : function(e){
8585         if(!e.isNavKeyPress()){
8586             this.validationTask.delay(this.validationDelay);
8587         }
8588     },
8589      /**
8590      * Validates the field value
8591      * @return {Boolean} True if the value is valid, else false
8592      */
8593     validate : function(){
8594         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8595         if(this.disabled || this.validateValue(this.getRawValue())){
8596             this.markValid();
8597             return true;
8598         }
8599         
8600         this.markInvalid();
8601         return false;
8602     },
8603     
8604     
8605     /**
8606      * Validates a value according to the field's validation rules and marks the field as invalid
8607      * if the validation fails
8608      * @param {Mixed} value The value to validate
8609      * @return {Boolean} True if the value is valid, else false
8610      */
8611     validateValue : function(value){
8612         if(value.length < 1)  { // if it's blank
8613             if(this.allowBlank){
8614                 return true;
8615             }
8616             return false;
8617         }
8618         
8619         if(value.length < this.minLength){
8620             return false;
8621         }
8622         if(value.length > this.maxLength){
8623             return false;
8624         }
8625         if(this.vtype){
8626             var vt = Roo.form.VTypes;
8627             if(!vt[this.vtype](value, this)){
8628                 return false;
8629             }
8630         }
8631         if(typeof this.validator == "function"){
8632             var msg = this.validator(value);
8633             if(msg !== true){
8634                 return false;
8635             }
8636         }
8637         
8638         if(this.regex && !this.regex.test(value)){
8639             return false;
8640         }
8641         
8642         return true;
8643     },
8644
8645     
8646     
8647      // private
8648     fireKey : function(e){
8649         //Roo.log('field ' + e.getKey());
8650         if(e.isNavKeyPress()){
8651             this.fireEvent("specialkey", this, e);
8652         }
8653     },
8654     focus : function (selectText){
8655         if(this.rendered){
8656             this.inputEl().focus();
8657             if(selectText === true){
8658                 this.inputEl().dom.select();
8659             }
8660         }
8661         return this;
8662     } ,
8663     
8664     onFocus : function(){
8665         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8666            // this.el.addClass(this.focusClass);
8667         }
8668         if(!this.hasFocus){
8669             this.hasFocus = true;
8670             this.startValue = this.getValue();
8671             this.fireEvent("focus", this);
8672         }
8673     },
8674     
8675     beforeBlur : Roo.emptyFn,
8676
8677     
8678     // private
8679     onBlur : function(){
8680         this.beforeBlur();
8681         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8682             //this.el.removeClass(this.focusClass);
8683         }
8684         this.hasFocus = false;
8685         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8686             this.validate();
8687         }
8688         var v = this.getValue();
8689         if(String(v) !== String(this.startValue)){
8690             this.fireEvent('change', this, v, this.startValue);
8691         }
8692         this.fireEvent("blur", this);
8693     },
8694     
8695     /**
8696      * Resets the current field value to the originally loaded value and clears any validation messages
8697      */
8698     reset : function(){
8699         this.setValue(this.originalValue);
8700         this.validate();
8701     },
8702      /**
8703      * Returns the name of the field
8704      * @return {Mixed} name The name field
8705      */
8706     getName: function(){
8707         return this.name;
8708     },
8709      /**
8710      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8711      * @return {Mixed} value The field value
8712      */
8713     getValue : function(){
8714         
8715         var v = this.inputEl().getValue();
8716         
8717         return v;
8718     },
8719     /**
8720      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8721      * @return {Mixed} value The field value
8722      */
8723     getRawValue : function(){
8724         var v = this.inputEl().getValue();
8725         
8726         return v;
8727     },
8728     
8729     /**
8730      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8731      * @param {Mixed} value The value to set
8732      */
8733     setRawValue : function(v){
8734         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8735     },
8736     
8737     selectText : function(start, end){
8738         var v = this.getRawValue();
8739         if(v.length > 0){
8740             start = start === undefined ? 0 : start;
8741             end = end === undefined ? v.length : end;
8742             var d = this.inputEl().dom;
8743             if(d.setSelectionRange){
8744                 d.setSelectionRange(start, end);
8745             }else if(d.createTextRange){
8746                 var range = d.createTextRange();
8747                 range.moveStart("character", start);
8748                 range.moveEnd("character", v.length-end);
8749                 range.select();
8750             }
8751         }
8752     },
8753     
8754     /**
8755      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8756      * @param {Mixed} value The value to set
8757      */
8758     setValue : function(v){
8759         this.value = v;
8760         if(this.rendered){
8761             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8762             this.validate();
8763         }
8764     },
8765     
8766     /*
8767     processValue : function(value){
8768         if(this.stripCharsRe){
8769             var newValue = value.replace(this.stripCharsRe, '');
8770             if(newValue !== value){
8771                 this.setRawValue(newValue);
8772                 return newValue;
8773             }
8774         }
8775         return value;
8776     },
8777   */
8778     preFocus : function(){
8779         
8780         if(this.selectOnFocus){
8781             this.inputEl().dom.select();
8782         }
8783     },
8784     filterKeys : function(e){
8785         var k = e.getKey();
8786         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8787             return;
8788         }
8789         var c = e.getCharCode(), cc = String.fromCharCode(c);
8790         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8791             return;
8792         }
8793         if(!this.maskRe.test(cc)){
8794             e.stopEvent();
8795         }
8796     },
8797      /**
8798      * Clear any invalid styles/messages for this field
8799      */
8800     clearInvalid : function(){
8801         
8802         if(!this.el || this.preventMark){ // not rendered
8803             return;
8804         }
8805         
8806         if(this.indicator){
8807             this.indicator.hide();
8808         }
8809         
8810         this.el.removeClass(this.invalidClass);
8811         
8812         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8813             
8814             var feedback = this.el.select('.form-control-feedback', true).first();
8815             
8816             if(feedback){
8817                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8818             }
8819             
8820         }
8821         
8822         this.fireEvent('valid', this);
8823     },
8824     
8825      /**
8826      * Mark this field as valid
8827      */
8828     markValid : function()
8829     {
8830         if(!this.el  || this.preventMark){ // not rendered
8831             return;
8832         }
8833         
8834         this.el.removeClass([this.invalidClass, this.validClass]);
8835         
8836         var feedback = this.el.select('.form-control-feedback', true).first();
8837             
8838         if(feedback){
8839             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8840         }
8841
8842         if(this.disabled || this.allowBlank){
8843             return;
8844         }
8845         
8846         if(this.indicator){
8847             this.indicator.hide();
8848         }
8849         
8850         this.el.addClass(this.validClass);
8851         
8852         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8853             
8854             var feedback = this.el.select('.form-control-feedback', true).first();
8855             
8856             if(feedback){
8857                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8858                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8859             }
8860             
8861         }
8862         
8863         this.fireEvent('valid', this);
8864     },
8865     
8866      /**
8867      * Mark this field as invalid
8868      * @param {String} msg The validation message
8869      */
8870     markInvalid : function(msg)
8871     {
8872         if(!this.el  || this.preventMark){ // not rendered
8873             return;
8874         }
8875         
8876         this.el.removeClass([this.invalidClass, this.validClass]);
8877         
8878         var feedback = this.el.select('.form-control-feedback', true).first();
8879             
8880         if(feedback){
8881             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8882         }
8883
8884         if(this.disabled || this.allowBlank){
8885             return;
8886         }
8887         
8888         if(this.indicator){
8889             this.indicator.show();
8890         }
8891         
8892         this.el.addClass(this.invalidClass);
8893         
8894         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8895             
8896             var feedback = this.el.select('.form-control-feedback', true).first();
8897             
8898             if(feedback){
8899                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8900                 
8901                 if(this.getValue().length || this.forceFeedback){
8902                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8903                 }
8904                 
8905             }
8906             
8907         }
8908         
8909         this.fireEvent('invalid', this, msg);
8910     },
8911     // private
8912     SafariOnKeyDown : function(event)
8913     {
8914         // this is a workaround for a password hang bug on chrome/ webkit.
8915         if (this.inputEl().dom.type != 'password') {
8916             return;
8917         }
8918         
8919         var isSelectAll = false;
8920         
8921         if(this.inputEl().dom.selectionEnd > 0){
8922             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8923         }
8924         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8925             event.preventDefault();
8926             this.setValue('');
8927             return;
8928         }
8929         
8930         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
8931             
8932             event.preventDefault();
8933             // this is very hacky as keydown always get's upper case.
8934             //
8935             var cc = String.fromCharCode(event.getCharCode());
8936             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8937             
8938         }
8939     },
8940     adjustWidth : function(tag, w){
8941         tag = tag.toLowerCase();
8942         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8943             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8944                 if(tag == 'input'){
8945                     return w + 2;
8946                 }
8947                 if(tag == 'textarea'){
8948                     return w-2;
8949                 }
8950             }else if(Roo.isOpera){
8951                 if(tag == 'input'){
8952                     return w + 2;
8953                 }
8954                 if(tag == 'textarea'){
8955                     return w-2;
8956                 }
8957             }
8958         }
8959         return w;
8960     }
8961     
8962 });
8963
8964  
8965 /*
8966  * - LGPL
8967  *
8968  * Input
8969  * 
8970  */
8971
8972 /**
8973  * @class Roo.bootstrap.TextArea
8974  * @extends Roo.bootstrap.Input
8975  * Bootstrap TextArea class
8976  * @cfg {Number} cols Specifies the visible width of a text area
8977  * @cfg {Number} rows Specifies the visible number of lines in a text area
8978  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8979  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8980  * @cfg {string} html text
8981  * 
8982  * @constructor
8983  * Create a new TextArea
8984  * @param {Object} config The config object
8985  */
8986
8987 Roo.bootstrap.TextArea = function(config){
8988     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8989    
8990 };
8991
8992 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8993      
8994     cols : false,
8995     rows : 5,
8996     readOnly : false,
8997     warp : 'soft',
8998     resize : false,
8999     value: false,
9000     html: false,
9001     
9002     getAutoCreate : function(){
9003         
9004         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9005         
9006         var id = Roo.id();
9007         
9008         var cfg = {};
9009         
9010         var input =  {
9011             tag: 'textarea',
9012             id : id,
9013             warp : this.warp,
9014             rows : this.rows,
9015             value : this.value || '',
9016             html: this.html || '',
9017             cls : 'form-control',
9018             placeholder : this.placeholder || '' 
9019             
9020         };
9021         
9022         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9023             input.maxLength = this.maxLength;
9024         }
9025         
9026         if(this.resize){
9027             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9028         }
9029         
9030         if(this.cols){
9031             input.cols = this.cols;
9032         }
9033         
9034         if (this.readOnly) {
9035             input.readonly = true;
9036         }
9037         
9038         if (this.name) {
9039             input.name = this.name;
9040         }
9041         
9042         if (this.size) {
9043             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9044         }
9045         
9046         var settings=this;
9047         ['xs','sm','md','lg'].map(function(size){
9048             if (settings[size]) {
9049                 cfg.cls += ' col-' + size + '-' + settings[size];
9050             }
9051         });
9052         
9053         var inputblock = input;
9054         
9055         if(this.hasFeedback && !this.allowBlank){
9056             
9057             var feedback = {
9058                 tag: 'span',
9059                 cls: 'glyphicon form-control-feedback'
9060             };
9061
9062             inputblock = {
9063                 cls : 'has-feedback',
9064                 cn :  [
9065                     input,
9066                     feedback
9067                 ] 
9068             };  
9069         }
9070         
9071         
9072         if (this.before || this.after) {
9073             
9074             inputblock = {
9075                 cls : 'input-group',
9076                 cn :  [] 
9077             };
9078             if (this.before) {
9079                 inputblock.cn.push({
9080                     tag :'span',
9081                     cls : 'input-group-addon',
9082                     html : this.before
9083                 });
9084             }
9085             
9086             inputblock.cn.push(input);
9087             
9088             if(this.hasFeedback && !this.allowBlank){
9089                 inputblock.cls += ' has-feedback';
9090                 inputblock.cn.push(feedback);
9091             }
9092             
9093             if (this.after) {
9094                 inputblock.cn.push({
9095                     tag :'span',
9096                     cls : 'input-group-addon',
9097                     html : this.after
9098                 });
9099             }
9100             
9101         }
9102         
9103         if (align ==='left' && this.fieldLabel.length) {
9104 //                Roo.log("left and has label");
9105                 cfg.cn = [
9106                     
9107                     {
9108                         tag: 'label',
9109                         'for' :  id,
9110                         cls : 'control-label col-sm-' + this.labelWidth,
9111                         html : this.fieldLabel
9112                         
9113                     },
9114                     {
9115                         cls : "col-sm-" + (12 - this.labelWidth), 
9116                         cn: [
9117                             inputblock
9118                         ]
9119                     }
9120                     
9121                 ];
9122         } else if ( this.fieldLabel.length) {
9123 //                Roo.log(" label");
9124                  cfg.cn = [
9125                    
9126                     {
9127                         tag: 'label',
9128                         //cls : 'input-group-addon',
9129                         html : this.fieldLabel
9130                         
9131                     },
9132                     
9133                     inputblock
9134                     
9135                 ];
9136
9137         } else {
9138             
9139 //                   Roo.log(" no label && no align");
9140                 cfg.cn = [
9141                     
9142                         inputblock
9143                     
9144                 ];
9145                 
9146                 
9147         }
9148         
9149         if (this.disabled) {
9150             input.disabled=true;
9151         }
9152         
9153         return cfg;
9154         
9155     },
9156     /**
9157      * return the real textarea element.
9158      */
9159     inputEl: function ()
9160     {
9161         return this.el.select('textarea.form-control',true).first();
9162     },
9163     
9164     /**
9165      * Clear any invalid styles/messages for this field
9166      */
9167     clearInvalid : function()
9168     {
9169         
9170         if(!this.el || this.preventMark){ // not rendered
9171             return;
9172         }
9173         
9174         var label = this.el.select('label', true).first();
9175         var icon = this.el.select('i.fa-star', true).first();
9176         
9177         if(label && icon){
9178             icon.remove();
9179         }
9180         
9181         this.el.removeClass(this.invalidClass);
9182         
9183         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9184             
9185             var feedback = this.el.select('.form-control-feedback', true).first();
9186             
9187             if(feedback){
9188                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9189             }
9190             
9191         }
9192         
9193         this.fireEvent('valid', this);
9194     },
9195     
9196      /**
9197      * Mark this field as valid
9198      */
9199     markValid : function()
9200     {
9201         if(!this.el  || this.preventMark){ // not rendered
9202             return;
9203         }
9204         
9205         this.el.removeClass([this.invalidClass, this.validClass]);
9206         
9207         var feedback = this.el.select('.form-control-feedback', true).first();
9208             
9209         if(feedback){
9210             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9211         }
9212
9213         if(this.disabled || this.allowBlank){
9214             return;
9215         }
9216         
9217         var label = this.el.select('label', true).first();
9218         var icon = this.el.select('i.fa-star', true).first();
9219         
9220         if(label && icon){
9221             icon.remove();
9222         }
9223         
9224         this.el.addClass(this.validClass);
9225         
9226         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9227             
9228             var feedback = this.el.select('.form-control-feedback', true).first();
9229             
9230             if(feedback){
9231                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9232                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9233             }
9234             
9235         }
9236         
9237         this.fireEvent('valid', this);
9238     },
9239     
9240      /**
9241      * Mark this field as invalid
9242      * @param {String} msg The validation message
9243      */
9244     markInvalid : function(msg)
9245     {
9246         if(!this.el  || this.preventMark){ // not rendered
9247             return;
9248         }
9249         
9250         this.el.removeClass([this.invalidClass, this.validClass]);
9251         
9252         var feedback = this.el.select('.form-control-feedback', true).first();
9253             
9254         if(feedback){
9255             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9256         }
9257
9258         if(this.disabled || this.allowBlank){
9259             return;
9260         }
9261         
9262         var label = this.el.select('label', true).first();
9263         var icon = this.el.select('i.fa-star', true).first();
9264         
9265         if(!this.getValue().length && label && !icon){
9266             this.el.createChild({
9267                 tag : 'i',
9268                 cls : 'text-danger fa fa-lg fa-star',
9269                 tooltip : 'This field is required',
9270                 style : 'margin-right:5px;'
9271             }, label, true);
9272         }
9273
9274         this.el.addClass(this.invalidClass);
9275         
9276         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9277             
9278             var feedback = this.el.select('.form-control-feedback', true).first();
9279             
9280             if(feedback){
9281                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9282                 
9283                 if(this.getValue().length || this.forceFeedback){
9284                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9285                 }
9286                 
9287             }
9288             
9289         }
9290         
9291         this.fireEvent('invalid', this, msg);
9292     }
9293 });
9294
9295  
9296 /*
9297  * - LGPL
9298  *
9299  * trigger field - base class for combo..
9300  * 
9301  */
9302  
9303 /**
9304  * @class Roo.bootstrap.TriggerField
9305  * @extends Roo.bootstrap.Input
9306  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9307  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9308  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9309  * for which you can provide a custom implementation.  For example:
9310  * <pre><code>
9311 var trigger = new Roo.bootstrap.TriggerField();
9312 trigger.onTriggerClick = myTriggerFn;
9313 trigger.applyTo('my-field');
9314 </code></pre>
9315  *
9316  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9317  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9318  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9319  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9320  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9321
9322  * @constructor
9323  * Create a new TriggerField.
9324  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9325  * to the base TextField)
9326  */
9327 Roo.bootstrap.TriggerField = function(config){
9328     this.mimicing = false;
9329     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9330 };
9331
9332 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9333     /**
9334      * @cfg {String} triggerClass A CSS class to apply to the trigger
9335      */
9336      /**
9337      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9338      */
9339     hideTrigger:false,
9340
9341     /**
9342      * @cfg {Boolean} removable (true|false) special filter default false
9343      */
9344     removable : false,
9345     
9346     /** @cfg {Boolean} grow @hide */
9347     /** @cfg {Number} growMin @hide */
9348     /** @cfg {Number} growMax @hide */
9349
9350     /**
9351      * @hide 
9352      * @method
9353      */
9354     autoSize: Roo.emptyFn,
9355     // private
9356     monitorTab : true,
9357     // private
9358     deferHeight : true,
9359
9360     
9361     actionMode : 'wrap',
9362     
9363     caret : false,
9364     
9365     
9366     getAutoCreate : function(){
9367        
9368         var align = this.labelAlign || this.parentLabelAlign();
9369         
9370         var id = Roo.id();
9371         
9372         var cfg = {
9373             cls: 'form-group' //input-group
9374         };
9375         
9376         
9377         var input =  {
9378             tag: 'input',
9379             id : id,
9380             type : this.inputType,
9381             cls : 'form-control',
9382             autocomplete: 'new-password',
9383             placeholder : this.placeholder || '' 
9384             
9385         };
9386         if (this.name) {
9387             input.name = this.name;
9388         }
9389         if (this.size) {
9390             input.cls += ' input-' + this.size;
9391         }
9392         
9393         if (this.disabled) {
9394             input.disabled=true;
9395         }
9396         
9397         var inputblock = input;
9398         
9399         if(this.hasFeedback && !this.allowBlank){
9400             
9401             var feedback = {
9402                 tag: 'span',
9403                 cls: 'glyphicon form-control-feedback'
9404             };
9405             
9406             if(this.removable && !this.editable && !this.tickable){
9407                 inputblock = {
9408                     cls : 'has-feedback',
9409                     cn :  [
9410                         inputblock,
9411                         {
9412                             tag: 'button',
9413                             html : 'x',
9414                             cls : 'roo-combo-removable-btn close'
9415                         },
9416                         feedback
9417                     ] 
9418                 };
9419             } else {
9420                 inputblock = {
9421                     cls : 'has-feedback',
9422                     cn :  [
9423                         inputblock,
9424                         feedback
9425                     ] 
9426                 };
9427             }
9428
9429         } else {
9430             if(this.removable && !this.editable && !this.tickable){
9431                 inputblock = {
9432                     cls : 'roo-removable',
9433                     cn :  [
9434                         inputblock,
9435                         {
9436                             tag: 'button',
9437                             html : 'x',
9438                             cls : 'roo-combo-removable-btn close'
9439                         }
9440                     ] 
9441                 };
9442             }
9443         }
9444         
9445         if (this.before || this.after) {
9446             
9447             inputblock = {
9448                 cls : 'input-group',
9449                 cn :  [] 
9450             };
9451             if (this.before) {
9452                 inputblock.cn.push({
9453                     tag :'span',
9454                     cls : 'input-group-addon',
9455                     html : this.before
9456                 });
9457             }
9458             
9459             inputblock.cn.push(input);
9460             
9461             if(this.hasFeedback && !this.allowBlank){
9462                 inputblock.cls += ' has-feedback';
9463                 inputblock.cn.push(feedback);
9464             }
9465             
9466             if (this.after) {
9467                 inputblock.cn.push({
9468                     tag :'span',
9469                     cls : 'input-group-addon',
9470                     html : this.after
9471                 });
9472             }
9473             
9474         };
9475         
9476         var box = {
9477             tag: 'div',
9478             cn: [
9479                 {
9480                     tag: 'input',
9481                     type : 'hidden',
9482                     cls: 'form-hidden-field'
9483                 },
9484                 inputblock
9485             ]
9486             
9487         };
9488         
9489         if(this.multiple){
9490             box = {
9491                 tag: 'div',
9492                 cn: [
9493                     {
9494                         tag: 'input',
9495                         type : 'hidden',
9496                         cls: 'form-hidden-field'
9497                     },
9498                     {
9499                         tag: 'ul',
9500                         cls: 'roo-select2-choices',
9501                         cn:[
9502                             {
9503                                 tag: 'li',
9504                                 cls: 'roo-select2-search-field',
9505                                 cn: [
9506
9507                                     inputblock
9508                                 ]
9509                             }
9510                         ]
9511                     }
9512                 ]
9513             }
9514         };
9515         
9516         var combobox = {
9517             cls: 'roo-select2-container input-group',
9518             cn: [
9519                 box
9520 //                {
9521 //                    tag: 'ul',
9522 //                    cls: 'typeahead typeahead-long dropdown-menu',
9523 //                    style: 'display:none'
9524 //                }
9525             ]
9526         };
9527         
9528         if(!this.multiple && this.showToggleBtn){
9529             
9530             var caret = {
9531                         tag: 'span',
9532                         cls: 'caret'
9533              };
9534             if (this.caret != false) {
9535                 caret = {
9536                      tag: 'i',
9537                      cls: 'fa fa-' + this.caret
9538                 };
9539                 
9540             }
9541             
9542             combobox.cn.push({
9543                 tag :'span',
9544                 cls : 'input-group-addon btn dropdown-toggle',
9545                 cn : [
9546                     caret,
9547                     {
9548                         tag: 'span',
9549                         cls: 'combobox-clear',
9550                         cn  : [
9551                             {
9552                                 tag : 'i',
9553                                 cls: 'icon-remove'
9554                             }
9555                         ]
9556                     }
9557                 ]
9558
9559             })
9560         }
9561         
9562         if(this.multiple){
9563             combobox.cls += ' roo-select2-container-multi';
9564         }
9565         
9566         if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
9567             
9568 //                Roo.log("left and has label");
9569             cfg.cn = [
9570                 {
9571                     tag : 'i',
9572                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9573                     tooltip : 'This field is required'
9574                 },
9575                 {
9576                     tag: 'label',
9577                     'for' :  id,
9578                     cls : 'control-label col-sm-' + this.labelWidth,
9579                     html : this.fieldLabel
9580
9581                 },
9582                 {
9583                     cls : "col-sm-" + (12 - this.labelWidth), 
9584                     cn: [
9585                         combobox
9586                     ]
9587                 }
9588
9589             ];
9590             
9591             if(this.indicatorpos == 'right'){
9592                 cfg.cn = [
9593                     {
9594                         tag: 'label',
9595                         'for' :  id,
9596                         cls : 'control-label col-sm-' + this.labelWidth,
9597                         html : this.fieldLabel
9598
9599                     },
9600                     {
9601                         tag : 'i',
9602                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9603                         tooltip : 'This field is required'
9604                     },
9605                     {
9606                         cls : "col-sm-" + (12 - this.labelWidth), 
9607                         cn: [
9608                             combobox
9609                         ]
9610                     }
9611
9612                 ];
9613             }
9614             
9615         } else if ( this.fieldLabel.length) {
9616 //                Roo.log(" label");
9617             cfg.cn = [
9618                 {
9619                    tag : 'i',
9620                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9621                    tooltip : 'This field is required'
9622                },
9623                {
9624                    tag: 'label',
9625                    //cls : 'input-group-addon',
9626                    html : this.fieldLabel
9627
9628                },
9629
9630                combobox
9631
9632             ];
9633             
9634             if(this.indicatorpos == 'right'){
9635                 
9636                 cfg.cn = [
9637                     {
9638                        tag: 'label',
9639                        //cls : 'input-group-addon',
9640                        html : this.fieldLabel
9641
9642                     },
9643                     {
9644                        tag : 'i',
9645                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9646                        tooltip : 'This field is required'
9647                     },
9648                     
9649                     combobox
9650
9651                 ];
9652
9653             }
9654
9655         } else {
9656             
9657 //                Roo.log(" no label && no align");
9658                 cfg = combobox
9659                      
9660                 
9661         }
9662          
9663         var settings=this;
9664         ['xs','sm','md','lg'].map(function(size){
9665             if (settings[size]) {
9666                 cfg.cls += ' col-' + size + '-' + settings[size];
9667             }
9668         });
9669         
9670         return cfg;
9671         
9672     },
9673     
9674     
9675     
9676     // private
9677     onResize : function(w, h){
9678 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9679 //        if(typeof w == 'number'){
9680 //            var x = w - this.trigger.getWidth();
9681 //            this.inputEl().setWidth(this.adjustWidth('input', x));
9682 //            this.trigger.setStyle('left', x+'px');
9683 //        }
9684     },
9685
9686     // private
9687     adjustSize : Roo.BoxComponent.prototype.adjustSize,
9688
9689     // private
9690     getResizeEl : function(){
9691         return this.inputEl();
9692     },
9693
9694     // private
9695     getPositionEl : function(){
9696         return this.inputEl();
9697     },
9698
9699     // private
9700     alignErrorIcon : function(){
9701         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9702     },
9703
9704     // private
9705     initEvents : function(){
9706         
9707         this.createList();
9708         
9709         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9710         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9711         if(!this.multiple && this.showToggleBtn){
9712             this.trigger = this.el.select('span.dropdown-toggle',true).first();
9713             if(this.hideTrigger){
9714                 this.trigger.setDisplayed(false);
9715             }
9716             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9717         }
9718         
9719         if(this.multiple){
9720             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9721         }
9722         
9723         if(this.removable && !this.editable && !this.tickable){
9724             var close = this.closeTriggerEl();
9725             
9726             if(close){
9727                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9728                 close.on('click', this.removeBtnClick, this, close);
9729             }
9730         }
9731         
9732         //this.trigger.addClassOnOver('x-form-trigger-over');
9733         //this.trigger.addClassOnClick('x-form-trigger-click');
9734         
9735         //if(!this.width){
9736         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9737         //}
9738     },
9739     
9740     closeTriggerEl : function()
9741     {
9742         var close = this.el.select('.roo-combo-removable-btn', true).first();
9743         return close ? close : false;
9744     },
9745     
9746     removeBtnClick : function(e, h, el)
9747     {
9748         e.preventDefault();
9749         
9750         if(this.fireEvent("remove", this) !== false){
9751             this.reset();
9752             this.fireEvent("afterremove", this)
9753         }
9754     },
9755     
9756     createList : function()
9757     {
9758         this.list = Roo.get(document.body).createChild({
9759             tag: 'ul',
9760             cls: 'typeahead typeahead-long dropdown-menu',
9761             style: 'display:none'
9762         });
9763         
9764         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9765         
9766     },
9767
9768     // private
9769     initTrigger : function(){
9770        
9771     },
9772
9773     // private
9774     onDestroy : function(){
9775         if(this.trigger){
9776             this.trigger.removeAllListeners();
9777           //  this.trigger.remove();
9778         }
9779         //if(this.wrap){
9780         //    this.wrap.remove();
9781         //}
9782         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9783     },
9784
9785     // private
9786     onFocus : function(){
9787         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9788         /*
9789         if(!this.mimicing){
9790             this.wrap.addClass('x-trigger-wrap-focus');
9791             this.mimicing = true;
9792             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9793             if(this.monitorTab){
9794                 this.el.on("keydown", this.checkTab, this);
9795             }
9796         }
9797         */
9798     },
9799
9800     // private
9801     checkTab : function(e){
9802         if(e.getKey() == e.TAB){
9803             this.triggerBlur();
9804         }
9805     },
9806
9807     // private
9808     onBlur : function(){
9809         // do nothing
9810     },
9811
9812     // private
9813     mimicBlur : function(e, t){
9814         /*
9815         if(!this.wrap.contains(t) && this.validateBlur()){
9816             this.triggerBlur();
9817         }
9818         */
9819     },
9820
9821     // private
9822     triggerBlur : function(){
9823         this.mimicing = false;
9824         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9825         if(this.monitorTab){
9826             this.el.un("keydown", this.checkTab, this);
9827         }
9828         //this.wrap.removeClass('x-trigger-wrap-focus');
9829         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9830     },
9831
9832     // private
9833     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9834     validateBlur : function(e, t){
9835         return true;
9836     },
9837
9838     // private
9839     onDisable : function(){
9840         this.inputEl().dom.disabled = true;
9841         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9842         //if(this.wrap){
9843         //    this.wrap.addClass('x-item-disabled');
9844         //}
9845     },
9846
9847     // private
9848     onEnable : function(){
9849         this.inputEl().dom.disabled = false;
9850         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9851         //if(this.wrap){
9852         //    this.el.removeClass('x-item-disabled');
9853         //}
9854     },
9855
9856     // private
9857     onShow : function(){
9858         var ae = this.getActionEl();
9859         
9860         if(ae){
9861             ae.dom.style.display = '';
9862             ae.dom.style.visibility = 'visible';
9863         }
9864     },
9865
9866     // private
9867     
9868     onHide : function(){
9869         var ae = this.getActionEl();
9870         ae.dom.style.display = 'none';
9871     },
9872
9873     /**
9874      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9875      * by an implementing function.
9876      * @method
9877      * @param {EventObject} e
9878      */
9879     onTriggerClick : Roo.emptyFn
9880 });
9881  /*
9882  * Based on:
9883  * Ext JS Library 1.1.1
9884  * Copyright(c) 2006-2007, Ext JS, LLC.
9885  *
9886  * Originally Released Under LGPL - original licence link has changed is not relivant.
9887  *
9888  * Fork - LGPL
9889  * <script type="text/javascript">
9890  */
9891
9892
9893 /**
9894  * @class Roo.data.SortTypes
9895  * @singleton
9896  * Defines the default sorting (casting?) comparison functions used when sorting data.
9897  */
9898 Roo.data.SortTypes = {
9899     /**
9900      * Default sort that does nothing
9901      * @param {Mixed} s The value being converted
9902      * @return {Mixed} The comparison value
9903      */
9904     none : function(s){
9905         return s;
9906     },
9907     
9908     /**
9909      * The regular expression used to strip tags
9910      * @type {RegExp}
9911      * @property
9912      */
9913     stripTagsRE : /<\/?[^>]+>/gi,
9914     
9915     /**
9916      * Strips all HTML tags to sort on text only
9917      * @param {Mixed} s The value being converted
9918      * @return {String} The comparison value
9919      */
9920     asText : function(s){
9921         return String(s).replace(this.stripTagsRE, "");
9922     },
9923     
9924     /**
9925      * Strips all HTML tags to sort on text only - Case insensitive
9926      * @param {Mixed} s The value being converted
9927      * @return {String} The comparison value
9928      */
9929     asUCText : function(s){
9930         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9931     },
9932     
9933     /**
9934      * Case insensitive string
9935      * @param {Mixed} s The value being converted
9936      * @return {String} The comparison value
9937      */
9938     asUCString : function(s) {
9939         return String(s).toUpperCase();
9940     },
9941     
9942     /**
9943      * Date sorting
9944      * @param {Mixed} s The value being converted
9945      * @return {Number} The comparison value
9946      */
9947     asDate : function(s) {
9948         if(!s){
9949             return 0;
9950         }
9951         if(s instanceof Date){
9952             return s.getTime();
9953         }
9954         return Date.parse(String(s));
9955     },
9956     
9957     /**
9958      * Float sorting
9959      * @param {Mixed} s The value being converted
9960      * @return {Float} The comparison value
9961      */
9962     asFloat : function(s) {
9963         var val = parseFloat(String(s).replace(/,/g, ""));
9964         if(isNaN(val)) {
9965             val = 0;
9966         }
9967         return val;
9968     },
9969     
9970     /**
9971      * Integer sorting
9972      * @param {Mixed} s The value being converted
9973      * @return {Number} The comparison value
9974      */
9975     asInt : function(s) {
9976         var val = parseInt(String(s).replace(/,/g, ""));
9977         if(isNaN(val)) {
9978             val = 0;
9979         }
9980         return val;
9981     }
9982 };/*
9983  * Based on:
9984  * Ext JS Library 1.1.1
9985  * Copyright(c) 2006-2007, Ext JS, LLC.
9986  *
9987  * Originally Released Under LGPL - original licence link has changed is not relivant.
9988  *
9989  * Fork - LGPL
9990  * <script type="text/javascript">
9991  */
9992
9993 /**
9994 * @class Roo.data.Record
9995  * Instances of this class encapsulate both record <em>definition</em> information, and record
9996  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9997  * to access Records cached in an {@link Roo.data.Store} object.<br>
9998  * <p>
9999  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10000  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10001  * objects.<br>
10002  * <p>
10003  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10004  * @constructor
10005  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10006  * {@link #create}. The parameters are the same.
10007  * @param {Array} data An associative Array of data values keyed by the field name.
10008  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10009  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10010  * not specified an integer id is generated.
10011  */
10012 Roo.data.Record = function(data, id){
10013     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10014     this.data = data;
10015 };
10016
10017 /**
10018  * Generate a constructor for a specific record layout.
10019  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10020  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10021  * Each field definition object may contain the following properties: <ul>
10022  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
10023  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10024  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10025  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10026  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10027  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10028  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10029  * this may be omitted.</p></li>
10030  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10031  * <ul><li>auto (Default, implies no conversion)</li>
10032  * <li>string</li>
10033  * <li>int</li>
10034  * <li>float</li>
10035  * <li>boolean</li>
10036  * <li>date</li></ul></p></li>
10037  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10038  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10039  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10040  * by the Reader into an object that will be stored in the Record. It is passed the
10041  * following parameters:<ul>
10042  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10043  * </ul></p></li>
10044  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10045  * </ul>
10046  * <br>usage:<br><pre><code>
10047 var TopicRecord = Roo.data.Record.create(
10048     {name: 'title', mapping: 'topic_title'},
10049     {name: 'author', mapping: 'username'},
10050     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10051     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10052     {name: 'lastPoster', mapping: 'user2'},
10053     {name: 'excerpt', mapping: 'post_text'}
10054 );
10055
10056 var myNewRecord = new TopicRecord({
10057     title: 'Do my job please',
10058     author: 'noobie',
10059     totalPosts: 1,
10060     lastPost: new Date(),
10061     lastPoster: 'Animal',
10062     excerpt: 'No way dude!'
10063 });
10064 myStore.add(myNewRecord);
10065 </code></pre>
10066  * @method create
10067  * @static
10068  */
10069 Roo.data.Record.create = function(o){
10070     var f = function(){
10071         f.superclass.constructor.apply(this, arguments);
10072     };
10073     Roo.extend(f, Roo.data.Record);
10074     var p = f.prototype;
10075     p.fields = new Roo.util.MixedCollection(false, function(field){
10076         return field.name;
10077     });
10078     for(var i = 0, len = o.length; i < len; i++){
10079         p.fields.add(new Roo.data.Field(o[i]));
10080     }
10081     f.getField = function(name){
10082         return p.fields.get(name);  
10083     };
10084     return f;
10085 };
10086
10087 Roo.data.Record.AUTO_ID = 1000;
10088 Roo.data.Record.EDIT = 'edit';
10089 Roo.data.Record.REJECT = 'reject';
10090 Roo.data.Record.COMMIT = 'commit';
10091
10092 Roo.data.Record.prototype = {
10093     /**
10094      * Readonly flag - true if this record has been modified.
10095      * @type Boolean
10096      */
10097     dirty : false,
10098     editing : false,
10099     error: null,
10100     modified: null,
10101
10102     // private
10103     join : function(store){
10104         this.store = store;
10105     },
10106
10107     /**
10108      * Set the named field to the specified value.
10109      * @param {String} name The name of the field to set.
10110      * @param {Object} value The value to set the field to.
10111      */
10112     set : function(name, value){
10113         if(this.data[name] == value){
10114             return;
10115         }
10116         this.dirty = true;
10117         if(!this.modified){
10118             this.modified = {};
10119         }
10120         if(typeof this.modified[name] == 'undefined'){
10121             this.modified[name] = this.data[name];
10122         }
10123         this.data[name] = value;
10124         if(!this.editing && this.store){
10125             this.store.afterEdit(this);
10126         }       
10127     },
10128
10129     /**
10130      * Get the value of the named field.
10131      * @param {String} name The name of the field to get the value of.
10132      * @return {Object} The value of the field.
10133      */
10134     get : function(name){
10135         return this.data[name]; 
10136     },
10137
10138     // private
10139     beginEdit : function(){
10140         this.editing = true;
10141         this.modified = {}; 
10142     },
10143
10144     // private
10145     cancelEdit : function(){
10146         this.editing = false;
10147         delete this.modified;
10148     },
10149
10150     // private
10151     endEdit : function(){
10152         this.editing = false;
10153         if(this.dirty && this.store){
10154             this.store.afterEdit(this);
10155         }
10156     },
10157
10158     /**
10159      * Usually called by the {@link Roo.data.Store} which owns the Record.
10160      * Rejects all changes made to the Record since either creation, or the last commit operation.
10161      * Modified fields are reverted to their original values.
10162      * <p>
10163      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10164      * of reject operations.
10165      */
10166     reject : function(){
10167         var m = this.modified;
10168         for(var n in m){
10169             if(typeof m[n] != "function"){
10170                 this.data[n] = m[n];
10171             }
10172         }
10173         this.dirty = false;
10174         delete this.modified;
10175         this.editing = false;
10176         if(this.store){
10177             this.store.afterReject(this);
10178         }
10179     },
10180
10181     /**
10182      * Usually called by the {@link Roo.data.Store} which owns the Record.
10183      * Commits all changes made to the Record since either creation, or the last commit operation.
10184      * <p>
10185      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10186      * of commit operations.
10187      */
10188     commit : function(){
10189         this.dirty = false;
10190         delete this.modified;
10191         this.editing = false;
10192         if(this.store){
10193             this.store.afterCommit(this);
10194         }
10195     },
10196
10197     // private
10198     hasError : function(){
10199         return this.error != null;
10200     },
10201
10202     // private
10203     clearError : function(){
10204         this.error = null;
10205     },
10206
10207     /**
10208      * Creates a copy of this record.
10209      * @param {String} id (optional) A new record id if you don't want to use this record's id
10210      * @return {Record}
10211      */
10212     copy : function(newId) {
10213         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10214     }
10215 };/*
10216  * Based on:
10217  * Ext JS Library 1.1.1
10218  * Copyright(c) 2006-2007, Ext JS, LLC.
10219  *
10220  * Originally Released Under LGPL - original licence link has changed is not relivant.
10221  *
10222  * Fork - LGPL
10223  * <script type="text/javascript">
10224  */
10225
10226
10227
10228 /**
10229  * @class Roo.data.Store
10230  * @extends Roo.util.Observable
10231  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10232  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10233  * <p>
10234  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
10235  * has no knowledge of the format of the data returned by the Proxy.<br>
10236  * <p>
10237  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10238  * instances from the data object. These records are cached and made available through accessor functions.
10239  * @constructor
10240  * Creates a new Store.
10241  * @param {Object} config A config object containing the objects needed for the Store to access data,
10242  * and read the data into Records.
10243  */
10244 Roo.data.Store = function(config){
10245     this.data = new Roo.util.MixedCollection(false);
10246     this.data.getKey = function(o){
10247         return o.id;
10248     };
10249     this.baseParams = {};
10250     // private
10251     this.paramNames = {
10252         "start" : "start",
10253         "limit" : "limit",
10254         "sort" : "sort",
10255         "dir" : "dir",
10256         "multisort" : "_multisort"
10257     };
10258
10259     if(config && config.data){
10260         this.inlineData = config.data;
10261         delete config.data;
10262     }
10263
10264     Roo.apply(this, config);
10265     
10266     if(this.reader){ // reader passed
10267         this.reader = Roo.factory(this.reader, Roo.data);
10268         this.reader.xmodule = this.xmodule || false;
10269         if(!this.recordType){
10270             this.recordType = this.reader.recordType;
10271         }
10272         if(this.reader.onMetaChange){
10273             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10274         }
10275     }
10276
10277     if(this.recordType){
10278         this.fields = this.recordType.prototype.fields;
10279     }
10280     this.modified = [];
10281
10282     this.addEvents({
10283         /**
10284          * @event datachanged
10285          * Fires when the data cache has changed, and a widget which is using this Store
10286          * as a Record cache should refresh its view.
10287          * @param {Store} this
10288          */
10289         datachanged : true,
10290         /**
10291          * @event metachange
10292          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10293          * @param {Store} this
10294          * @param {Object} meta The JSON metadata
10295          */
10296         metachange : true,
10297         /**
10298          * @event add
10299          * Fires when Records have been added to the Store
10300          * @param {Store} this
10301          * @param {Roo.data.Record[]} records The array of Records added
10302          * @param {Number} index The index at which the record(s) were added
10303          */
10304         add : true,
10305         /**
10306          * @event remove
10307          * Fires when a Record has been removed from the Store
10308          * @param {Store} this
10309          * @param {Roo.data.Record} record The Record that was removed
10310          * @param {Number} index The index at which the record was removed
10311          */
10312         remove : true,
10313         /**
10314          * @event update
10315          * Fires when a Record has been updated
10316          * @param {Store} this
10317          * @param {Roo.data.Record} record The Record that was updated
10318          * @param {String} operation The update operation being performed.  Value may be one of:
10319          * <pre><code>
10320  Roo.data.Record.EDIT
10321  Roo.data.Record.REJECT
10322  Roo.data.Record.COMMIT
10323          * </code></pre>
10324          */
10325         update : true,
10326         /**
10327          * @event clear
10328          * Fires when the data cache has been cleared.
10329          * @param {Store} this
10330          */
10331         clear : true,
10332         /**
10333          * @event beforeload
10334          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10335          * the load action will be canceled.
10336          * @param {Store} this
10337          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10338          */
10339         beforeload : true,
10340         /**
10341          * @event beforeloadadd
10342          * Fires after a new set of Records has been loaded.
10343          * @param {Store} this
10344          * @param {Roo.data.Record[]} records The Records that were loaded
10345          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10346          */
10347         beforeloadadd : true,
10348         /**
10349          * @event load
10350          * Fires after a new set of Records has been loaded, before they are added to the store.
10351          * @param {Store} this
10352          * @param {Roo.data.Record[]} records The Records that were loaded
10353          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10354          * @params {Object} return from reader
10355          */
10356         load : true,
10357         /**
10358          * @event loadexception
10359          * Fires if an exception occurs in the Proxy during loading.
10360          * Called with the signature of the Proxy's "loadexception" event.
10361          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10362          * 
10363          * @param {Proxy} 
10364          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10365          * @param {Object} load options 
10366          * @param {Object} jsonData from your request (normally this contains the Exception)
10367          */
10368         loadexception : true
10369     });
10370     
10371     if(this.proxy){
10372         this.proxy = Roo.factory(this.proxy, Roo.data);
10373         this.proxy.xmodule = this.xmodule || false;
10374         this.relayEvents(this.proxy,  ["loadexception"]);
10375     }
10376     this.sortToggle = {};
10377     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10378
10379     Roo.data.Store.superclass.constructor.call(this);
10380
10381     if(this.inlineData){
10382         this.loadData(this.inlineData);
10383         delete this.inlineData;
10384     }
10385 };
10386
10387 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10388      /**
10389     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10390     * without a remote query - used by combo/forms at present.
10391     */
10392     
10393     /**
10394     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10395     */
10396     /**
10397     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10398     */
10399     /**
10400     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10401     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10402     */
10403     /**
10404     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10405     * on any HTTP request
10406     */
10407     /**
10408     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10409     */
10410     /**
10411     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10412     */
10413     multiSort: false,
10414     /**
10415     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10416     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10417     */
10418     remoteSort : false,
10419
10420     /**
10421     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10422      * loaded or when a record is removed. (defaults to false).
10423     */
10424     pruneModifiedRecords : false,
10425
10426     // private
10427     lastOptions : null,
10428
10429     /**
10430      * Add Records to the Store and fires the add event.
10431      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10432      */
10433     add : function(records){
10434         records = [].concat(records);
10435         for(var i = 0, len = records.length; i < len; i++){
10436             records[i].join(this);
10437         }
10438         var index = this.data.length;
10439         this.data.addAll(records);
10440         this.fireEvent("add", this, records, index);
10441     },
10442
10443     /**
10444      * Remove a Record from the Store and fires the remove event.
10445      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10446      */
10447     remove : function(record){
10448         var index = this.data.indexOf(record);
10449         this.data.removeAt(index);
10450         if(this.pruneModifiedRecords){
10451             this.modified.remove(record);
10452         }
10453         this.fireEvent("remove", this, record, index);
10454     },
10455
10456     /**
10457      * Remove all Records from the Store and fires the clear event.
10458      */
10459     removeAll : function(){
10460         this.data.clear();
10461         if(this.pruneModifiedRecords){
10462             this.modified = [];
10463         }
10464         this.fireEvent("clear", this);
10465     },
10466
10467     /**
10468      * Inserts Records to the Store at the given index and fires the add event.
10469      * @param {Number} index The start index at which to insert the passed Records.
10470      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10471      */
10472     insert : function(index, records){
10473         records = [].concat(records);
10474         for(var i = 0, len = records.length; i < len; i++){
10475             this.data.insert(index, records[i]);
10476             records[i].join(this);
10477         }
10478         this.fireEvent("add", this, records, index);
10479     },
10480
10481     /**
10482      * Get the index within the cache of the passed Record.
10483      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10484      * @return {Number} The index of the passed Record. Returns -1 if not found.
10485      */
10486     indexOf : function(record){
10487         return this.data.indexOf(record);
10488     },
10489
10490     /**
10491      * Get the index within the cache of the Record with the passed id.
10492      * @param {String} id The id of the Record to find.
10493      * @return {Number} The index of the Record. Returns -1 if not found.
10494      */
10495     indexOfId : function(id){
10496         return this.data.indexOfKey(id);
10497     },
10498
10499     /**
10500      * Get the Record with the specified id.
10501      * @param {String} id The id of the Record to find.
10502      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10503      */
10504     getById : function(id){
10505         return this.data.key(id);
10506     },
10507
10508     /**
10509      * Get the Record at the specified index.
10510      * @param {Number} index The index of the Record to find.
10511      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10512      */
10513     getAt : function(index){
10514         return this.data.itemAt(index);
10515     },
10516
10517     /**
10518      * Returns a range of Records between specified indices.
10519      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10520      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10521      * @return {Roo.data.Record[]} An array of Records
10522      */
10523     getRange : function(start, end){
10524         return this.data.getRange(start, end);
10525     },
10526
10527     // private
10528     storeOptions : function(o){
10529         o = Roo.apply({}, o);
10530         delete o.callback;
10531         delete o.scope;
10532         this.lastOptions = o;
10533     },
10534
10535     /**
10536      * Loads the Record cache from the configured Proxy using the configured Reader.
10537      * <p>
10538      * If using remote paging, then the first load call must specify the <em>start</em>
10539      * and <em>limit</em> properties in the options.params property to establish the initial
10540      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10541      * <p>
10542      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10543      * and this call will return before the new data has been loaded. Perform any post-processing
10544      * in a callback function, or in a "load" event handler.</strong>
10545      * <p>
10546      * @param {Object} options An object containing properties which control loading options:<ul>
10547      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10548      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10549      * passed the following arguments:<ul>
10550      * <li>r : Roo.data.Record[]</li>
10551      * <li>options: Options object from the load call</li>
10552      * <li>success: Boolean success indicator</li></ul></li>
10553      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10554      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10555      * </ul>
10556      */
10557     load : function(options){
10558         options = options || {};
10559         if(this.fireEvent("beforeload", this, options) !== false){
10560             this.storeOptions(options);
10561             var p = Roo.apply(options.params || {}, this.baseParams);
10562             // if meta was not loaded from remote source.. try requesting it.
10563             if (!this.reader.metaFromRemote) {
10564                 p._requestMeta = 1;
10565             }
10566             if(this.sortInfo && this.remoteSort){
10567                 var pn = this.paramNames;
10568                 p[pn["sort"]] = this.sortInfo.field;
10569                 p[pn["dir"]] = this.sortInfo.direction;
10570             }
10571             if (this.multiSort) {
10572                 var pn = this.paramNames;
10573                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10574             }
10575             
10576             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10577         }
10578     },
10579
10580     /**
10581      * Reloads the Record cache from the configured Proxy using the configured Reader and
10582      * the options from the last load operation performed.
10583      * @param {Object} options (optional) An object containing properties which may override the options
10584      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10585      * the most recently used options are reused).
10586      */
10587     reload : function(options){
10588         this.load(Roo.applyIf(options||{}, this.lastOptions));
10589     },
10590
10591     // private
10592     // Called as a callback by the Reader during a load operation.
10593     loadRecords : function(o, options, success){
10594         if(!o || success === false){
10595             if(success !== false){
10596                 this.fireEvent("load", this, [], options, o);
10597             }
10598             if(options.callback){
10599                 options.callback.call(options.scope || this, [], options, false);
10600             }
10601             return;
10602         }
10603         // if data returned failure - throw an exception.
10604         if (o.success === false) {
10605             // show a message if no listener is registered.
10606             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10607                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10608             }
10609             // loadmask wil be hooked into this..
10610             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10611             return;
10612         }
10613         var r = o.records, t = o.totalRecords || r.length;
10614         
10615         this.fireEvent("beforeloadadd", this, r, options, o);
10616         
10617         if(!options || options.add !== true){
10618             if(this.pruneModifiedRecords){
10619                 this.modified = [];
10620             }
10621             for(var i = 0, len = r.length; i < len; i++){
10622                 r[i].join(this);
10623             }
10624             if(this.snapshot){
10625                 this.data = this.snapshot;
10626                 delete this.snapshot;
10627             }
10628             this.data.clear();
10629             this.data.addAll(r);
10630             this.totalLength = t;
10631             this.applySort();
10632             this.fireEvent("datachanged", this);
10633         }else{
10634             this.totalLength = Math.max(t, this.data.length+r.length);
10635             this.add(r);
10636         }
10637         this.fireEvent("load", this, r, options, o);
10638         if(options.callback){
10639             options.callback.call(options.scope || this, r, options, true);
10640         }
10641     },
10642
10643
10644     /**
10645      * Loads data from a passed data block. A Reader which understands the format of the data
10646      * must have been configured in the constructor.
10647      * @param {Object} data The data block from which to read the Records.  The format of the data expected
10648      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10649      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10650      */
10651     loadData : function(o, append){
10652         var r = this.reader.readRecords(o);
10653         this.loadRecords(r, {add: append}, true);
10654     },
10655
10656     /**
10657      * Gets the number of cached records.
10658      * <p>
10659      * <em>If using paging, this may not be the total size of the dataset. If the data object
10660      * used by the Reader contains the dataset size, then the getTotalCount() function returns
10661      * the data set size</em>
10662      */
10663     getCount : function(){
10664         return this.data.length || 0;
10665     },
10666
10667     /**
10668      * Gets the total number of records in the dataset as returned by the server.
10669      * <p>
10670      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10671      * the dataset size</em>
10672      */
10673     getTotalCount : function(){
10674         return this.totalLength || 0;
10675     },
10676
10677     /**
10678      * Returns the sort state of the Store as an object with two properties:
10679      * <pre><code>
10680  field {String} The name of the field by which the Records are sorted
10681  direction {String} The sort order, "ASC" or "DESC"
10682      * </code></pre>
10683      */
10684     getSortState : function(){
10685         return this.sortInfo;
10686     },
10687
10688     // private
10689     applySort : function(){
10690         if(this.sortInfo && !this.remoteSort){
10691             var s = this.sortInfo, f = s.field;
10692             var st = this.fields.get(f).sortType;
10693             var fn = function(r1, r2){
10694                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10695                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10696             };
10697             this.data.sort(s.direction, fn);
10698             if(this.snapshot && this.snapshot != this.data){
10699                 this.snapshot.sort(s.direction, fn);
10700             }
10701         }
10702     },
10703
10704     /**
10705      * Sets the default sort column and order to be used by the next load operation.
10706      * @param {String} fieldName The name of the field to sort by.
10707      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10708      */
10709     setDefaultSort : function(field, dir){
10710         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10711     },
10712
10713     /**
10714      * Sort the Records.
10715      * If remote sorting is used, the sort is performed on the server, and the cache is
10716      * reloaded. If local sorting is used, the cache is sorted internally.
10717      * @param {String} fieldName The name of the field to sort by.
10718      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10719      */
10720     sort : function(fieldName, dir){
10721         var f = this.fields.get(fieldName);
10722         if(!dir){
10723             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10724             
10725             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10726                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10727             }else{
10728                 dir = f.sortDir;
10729             }
10730         }
10731         this.sortToggle[f.name] = dir;
10732         this.sortInfo = {field: f.name, direction: dir};
10733         if(!this.remoteSort){
10734             this.applySort();
10735             this.fireEvent("datachanged", this);
10736         }else{
10737             this.load(this.lastOptions);
10738         }
10739     },
10740
10741     /**
10742      * Calls the specified function for each of the Records in the cache.
10743      * @param {Function} fn The function to call. The Record is passed as the first parameter.
10744      * Returning <em>false</em> aborts and exits the iteration.
10745      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10746      */
10747     each : function(fn, scope){
10748         this.data.each(fn, scope);
10749     },
10750
10751     /**
10752      * Gets all records modified since the last commit.  Modified records are persisted across load operations
10753      * (e.g., during paging).
10754      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10755      */
10756     getModifiedRecords : function(){
10757         return this.modified;
10758     },
10759
10760     // private
10761     createFilterFn : function(property, value, anyMatch){
10762         if(!value.exec){ // not a regex
10763             value = String(value);
10764             if(value.length == 0){
10765                 return false;
10766             }
10767             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10768         }
10769         return function(r){
10770             return value.test(r.data[property]);
10771         };
10772     },
10773
10774     /**
10775      * Sums the value of <i>property</i> for each record between start and end and returns the result.
10776      * @param {String} property A field on your records
10777      * @param {Number} start The record index to start at (defaults to 0)
10778      * @param {Number} end The last record index to include (defaults to length - 1)
10779      * @return {Number} The sum
10780      */
10781     sum : function(property, start, end){
10782         var rs = this.data.items, v = 0;
10783         start = start || 0;
10784         end = (end || end === 0) ? end : rs.length-1;
10785
10786         for(var i = start; i <= end; i++){
10787             v += (rs[i].data[property] || 0);
10788         }
10789         return v;
10790     },
10791
10792     /**
10793      * Filter the records by a specified property.
10794      * @param {String} field A field on your records
10795      * @param {String/RegExp} value Either a string that the field
10796      * should start with or a RegExp to test against the field
10797      * @param {Boolean} anyMatch True to match any part not just the beginning
10798      */
10799     filter : function(property, value, anyMatch){
10800         var fn = this.createFilterFn(property, value, anyMatch);
10801         return fn ? this.filterBy(fn) : this.clearFilter();
10802     },
10803
10804     /**
10805      * Filter by a function. The specified function will be called with each
10806      * record in this data source. If the function returns true the record is included,
10807      * otherwise it is filtered.
10808      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10809      * @param {Object} scope (optional) The scope of the function (defaults to this)
10810      */
10811     filterBy : function(fn, scope){
10812         this.snapshot = this.snapshot || this.data;
10813         this.data = this.queryBy(fn, scope||this);
10814         this.fireEvent("datachanged", this);
10815     },
10816
10817     /**
10818      * Query the records by a specified property.
10819      * @param {String} field A field on your records
10820      * @param {String/RegExp} value Either a string that the field
10821      * should start with or a RegExp to test against the field
10822      * @param {Boolean} anyMatch True to match any part not just the beginning
10823      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10824      */
10825     query : function(property, value, anyMatch){
10826         var fn = this.createFilterFn(property, value, anyMatch);
10827         return fn ? this.queryBy(fn) : this.data.clone();
10828     },
10829
10830     /**
10831      * Query by a function. The specified function will be called with each
10832      * record in this data source. If the function returns true the record is included
10833      * in the results.
10834      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10835      * @param {Object} scope (optional) The scope of the function (defaults to this)
10836       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10837      **/
10838     queryBy : function(fn, scope){
10839         var data = this.snapshot || this.data;
10840         return data.filterBy(fn, scope||this);
10841     },
10842
10843     /**
10844      * Collects unique values for a particular dataIndex from this store.
10845      * @param {String} dataIndex The property to collect
10846      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10847      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10848      * @return {Array} An array of the unique values
10849      **/
10850     collect : function(dataIndex, allowNull, bypassFilter){
10851         var d = (bypassFilter === true && this.snapshot) ?
10852                 this.snapshot.items : this.data.items;
10853         var v, sv, r = [], l = {};
10854         for(var i = 0, len = d.length; i < len; i++){
10855             v = d[i].data[dataIndex];
10856             sv = String(v);
10857             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10858                 l[sv] = true;
10859                 r[r.length] = v;
10860             }
10861         }
10862         return r;
10863     },
10864
10865     /**
10866      * Revert to a view of the Record cache with no filtering applied.
10867      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10868      */
10869     clearFilter : function(suppressEvent){
10870         if(this.snapshot && this.snapshot != this.data){
10871             this.data = this.snapshot;
10872             delete this.snapshot;
10873             if(suppressEvent !== true){
10874                 this.fireEvent("datachanged", this);
10875             }
10876         }
10877     },
10878
10879     // private
10880     afterEdit : function(record){
10881         if(this.modified.indexOf(record) == -1){
10882             this.modified.push(record);
10883         }
10884         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10885     },
10886     
10887     // private
10888     afterReject : function(record){
10889         this.modified.remove(record);
10890         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10891     },
10892
10893     // private
10894     afterCommit : function(record){
10895         this.modified.remove(record);
10896         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10897     },
10898
10899     /**
10900      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10901      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10902      */
10903     commitChanges : function(){
10904         var m = this.modified.slice(0);
10905         this.modified = [];
10906         for(var i = 0, len = m.length; i < len; i++){
10907             m[i].commit();
10908         }
10909     },
10910
10911     /**
10912      * Cancel outstanding changes on all changed records.
10913      */
10914     rejectChanges : function(){
10915         var m = this.modified.slice(0);
10916         this.modified = [];
10917         for(var i = 0, len = m.length; i < len; i++){
10918             m[i].reject();
10919         }
10920     },
10921
10922     onMetaChange : function(meta, rtype, o){
10923         this.recordType = rtype;
10924         this.fields = rtype.prototype.fields;
10925         delete this.snapshot;
10926         this.sortInfo = meta.sortInfo || this.sortInfo;
10927         this.modified = [];
10928         this.fireEvent('metachange', this, this.reader.meta);
10929     },
10930     
10931     moveIndex : function(data, type)
10932     {
10933         var index = this.indexOf(data);
10934         
10935         var newIndex = index + type;
10936         
10937         this.remove(data);
10938         
10939         this.insert(newIndex, data);
10940         
10941     }
10942 });/*
10943  * Based on:
10944  * Ext JS Library 1.1.1
10945  * Copyright(c) 2006-2007, Ext JS, LLC.
10946  *
10947  * Originally Released Under LGPL - original licence link has changed is not relivant.
10948  *
10949  * Fork - LGPL
10950  * <script type="text/javascript">
10951  */
10952
10953 /**
10954  * @class Roo.data.SimpleStore
10955  * @extends Roo.data.Store
10956  * Small helper class to make creating Stores from Array data easier.
10957  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10958  * @cfg {Array} fields An array of field definition objects, or field name strings.
10959  * @cfg {Array} data The multi-dimensional array of data
10960  * @constructor
10961  * @param {Object} config
10962  */
10963 Roo.data.SimpleStore = function(config){
10964     Roo.data.SimpleStore.superclass.constructor.call(this, {
10965         isLocal : true,
10966         reader: new Roo.data.ArrayReader({
10967                 id: config.id
10968             },
10969             Roo.data.Record.create(config.fields)
10970         ),
10971         proxy : new Roo.data.MemoryProxy(config.data)
10972     });
10973     this.load();
10974 };
10975 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10976  * Based on:
10977  * Ext JS Library 1.1.1
10978  * Copyright(c) 2006-2007, Ext JS, LLC.
10979  *
10980  * Originally Released Under LGPL - original licence link has changed is not relivant.
10981  *
10982  * Fork - LGPL
10983  * <script type="text/javascript">
10984  */
10985
10986 /**
10987 /**
10988  * @extends Roo.data.Store
10989  * @class Roo.data.JsonStore
10990  * Small helper class to make creating Stores for JSON data easier. <br/>
10991 <pre><code>
10992 var store = new Roo.data.JsonStore({
10993     url: 'get-images.php',
10994     root: 'images',
10995     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10996 });
10997 </code></pre>
10998  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10999  * JsonReader and HttpProxy (unless inline data is provided).</b>
11000  * @cfg {Array} fields An array of field definition objects, or field name strings.
11001  * @constructor
11002  * @param {Object} config
11003  */
11004 Roo.data.JsonStore = function(c){
11005     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11006         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11007         reader: new Roo.data.JsonReader(c, c.fields)
11008     }));
11009 };
11010 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11011  * Based on:
11012  * Ext JS Library 1.1.1
11013  * Copyright(c) 2006-2007, Ext JS, LLC.
11014  *
11015  * Originally Released Under LGPL - original licence link has changed is not relivant.
11016  *
11017  * Fork - LGPL
11018  * <script type="text/javascript">
11019  */
11020
11021  
11022 Roo.data.Field = function(config){
11023     if(typeof config == "string"){
11024         config = {name: config};
11025     }
11026     Roo.apply(this, config);
11027     
11028     if(!this.type){
11029         this.type = "auto";
11030     }
11031     
11032     var st = Roo.data.SortTypes;
11033     // named sortTypes are supported, here we look them up
11034     if(typeof this.sortType == "string"){
11035         this.sortType = st[this.sortType];
11036     }
11037     
11038     // set default sortType for strings and dates
11039     if(!this.sortType){
11040         switch(this.type){
11041             case "string":
11042                 this.sortType = st.asUCString;
11043                 break;
11044             case "date":
11045                 this.sortType = st.asDate;
11046                 break;
11047             default:
11048                 this.sortType = st.none;
11049         }
11050     }
11051
11052     // define once
11053     var stripRe = /[\$,%]/g;
11054
11055     // prebuilt conversion function for this field, instead of
11056     // switching every time we're reading a value
11057     if(!this.convert){
11058         var cv, dateFormat = this.dateFormat;
11059         switch(this.type){
11060             case "":
11061             case "auto":
11062             case undefined:
11063                 cv = function(v){ return v; };
11064                 break;
11065             case "string":
11066                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11067                 break;
11068             case "int":
11069                 cv = function(v){
11070                     return v !== undefined && v !== null && v !== '' ?
11071                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11072                     };
11073                 break;
11074             case "float":
11075                 cv = function(v){
11076                     return v !== undefined && v !== null && v !== '' ?
11077                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11078                     };
11079                 break;
11080             case "bool":
11081             case "boolean":
11082                 cv = function(v){ return v === true || v === "true" || v == 1; };
11083                 break;
11084             case "date":
11085                 cv = function(v){
11086                     if(!v){
11087                         return '';
11088                     }
11089                     if(v instanceof Date){
11090                         return v;
11091                     }
11092                     if(dateFormat){
11093                         if(dateFormat == "timestamp"){
11094                             return new Date(v*1000);
11095                         }
11096                         return Date.parseDate(v, dateFormat);
11097                     }
11098                     var parsed = Date.parse(v);
11099                     return parsed ? new Date(parsed) : null;
11100                 };
11101              break;
11102             
11103         }
11104         this.convert = cv;
11105     }
11106 };
11107
11108 Roo.data.Field.prototype = {
11109     dateFormat: null,
11110     defaultValue: "",
11111     mapping: null,
11112     sortType : null,
11113     sortDir : "ASC"
11114 };/*
11115  * Based on:
11116  * Ext JS Library 1.1.1
11117  * Copyright(c) 2006-2007, Ext JS, LLC.
11118  *
11119  * Originally Released Under LGPL - original licence link has changed is not relivant.
11120  *
11121  * Fork - LGPL
11122  * <script type="text/javascript">
11123  */
11124  
11125 // Base class for reading structured data from a data source.  This class is intended to be
11126 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11127
11128 /**
11129  * @class Roo.data.DataReader
11130  * Base class for reading structured data from a data source.  This class is intended to be
11131  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11132  */
11133
11134 Roo.data.DataReader = function(meta, recordType){
11135     
11136     this.meta = meta;
11137     
11138     this.recordType = recordType instanceof Array ? 
11139         Roo.data.Record.create(recordType) : recordType;
11140 };
11141
11142 Roo.data.DataReader.prototype = {
11143      /**
11144      * Create an empty record
11145      * @param {Object} data (optional) - overlay some values
11146      * @return {Roo.data.Record} record created.
11147      */
11148     newRow :  function(d) {
11149         var da =  {};
11150         this.recordType.prototype.fields.each(function(c) {
11151             switch( c.type) {
11152                 case 'int' : da[c.name] = 0; break;
11153                 case 'date' : da[c.name] = new Date(); break;
11154                 case 'float' : da[c.name] = 0.0; break;
11155                 case 'boolean' : da[c.name] = false; break;
11156                 default : da[c.name] = ""; break;
11157             }
11158             
11159         });
11160         return new this.recordType(Roo.apply(da, d));
11161     }
11162     
11163 };/*
11164  * Based on:
11165  * Ext JS Library 1.1.1
11166  * Copyright(c) 2006-2007, Ext JS, LLC.
11167  *
11168  * Originally Released Under LGPL - original licence link has changed is not relivant.
11169  *
11170  * Fork - LGPL
11171  * <script type="text/javascript">
11172  */
11173
11174 /**
11175  * @class Roo.data.DataProxy
11176  * @extends Roo.data.Observable
11177  * This class is an abstract base class for implementations which provide retrieval of
11178  * unformatted data objects.<br>
11179  * <p>
11180  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11181  * (of the appropriate type which knows how to parse the data object) to provide a block of
11182  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11183  * <p>
11184  * Custom implementations must implement the load method as described in
11185  * {@link Roo.data.HttpProxy#load}.
11186  */
11187 Roo.data.DataProxy = function(){
11188     this.addEvents({
11189         /**
11190          * @event beforeload
11191          * Fires before a network request is made to retrieve a data object.
11192          * @param {Object} This DataProxy object.
11193          * @param {Object} params The params parameter to the load function.
11194          */
11195         beforeload : true,
11196         /**
11197          * @event load
11198          * Fires before the load method's callback is called.
11199          * @param {Object} This DataProxy object.
11200          * @param {Object} o The data object.
11201          * @param {Object} arg The callback argument object passed to the load function.
11202          */
11203         load : true,
11204         /**
11205          * @event loadexception
11206          * Fires if an Exception occurs during data retrieval.
11207          * @param {Object} This DataProxy object.
11208          * @param {Object} o The data object.
11209          * @param {Object} arg The callback argument object passed to the load function.
11210          * @param {Object} e The Exception.
11211          */
11212         loadexception : true
11213     });
11214     Roo.data.DataProxy.superclass.constructor.call(this);
11215 };
11216
11217 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11218
11219     /**
11220      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11221      */
11222 /*
11223  * Based on:
11224  * Ext JS Library 1.1.1
11225  * Copyright(c) 2006-2007, Ext JS, LLC.
11226  *
11227  * Originally Released Under LGPL - original licence link has changed is not relivant.
11228  *
11229  * Fork - LGPL
11230  * <script type="text/javascript">
11231  */
11232 /**
11233  * @class Roo.data.MemoryProxy
11234  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11235  * to the Reader when its load method is called.
11236  * @constructor
11237  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11238  */
11239 Roo.data.MemoryProxy = function(data){
11240     if (data.data) {
11241         data = data.data;
11242     }
11243     Roo.data.MemoryProxy.superclass.constructor.call(this);
11244     this.data = data;
11245 };
11246
11247 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11248     
11249     /**
11250      * Load data from the requested source (in this case an in-memory
11251      * data object passed to the constructor), read the data object into
11252      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11253      * process that block using the passed callback.
11254      * @param {Object} params This parameter is not used by the MemoryProxy class.
11255      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11256      * object into a block of Roo.data.Records.
11257      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11258      * The function must be passed <ul>
11259      * <li>The Record block object</li>
11260      * <li>The "arg" argument from the load function</li>
11261      * <li>A boolean success indicator</li>
11262      * </ul>
11263      * @param {Object} scope The scope in which to call the callback
11264      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11265      */
11266     load : function(params, reader, callback, scope, arg){
11267         params = params || {};
11268         var result;
11269         try {
11270             result = reader.readRecords(this.data);
11271         }catch(e){
11272             this.fireEvent("loadexception", this, arg, null, e);
11273             callback.call(scope, null, arg, false);
11274             return;
11275         }
11276         callback.call(scope, result, arg, true);
11277     },
11278     
11279     // private
11280     update : function(params, records){
11281         
11282     }
11283 });/*
11284  * Based on:
11285  * Ext JS Library 1.1.1
11286  * Copyright(c) 2006-2007, Ext JS, LLC.
11287  *
11288  * Originally Released Under LGPL - original licence link has changed is not relivant.
11289  *
11290  * Fork - LGPL
11291  * <script type="text/javascript">
11292  */
11293 /**
11294  * @class Roo.data.HttpProxy
11295  * @extends Roo.data.DataProxy
11296  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11297  * configured to reference a certain URL.<br><br>
11298  * <p>
11299  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11300  * from which the running page was served.<br><br>
11301  * <p>
11302  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11303  * <p>
11304  * Be aware that to enable the browser to parse an XML document, the server must set
11305  * the Content-Type header in the HTTP response to "text/xml".
11306  * @constructor
11307  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11308  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11309  * will be used to make the request.
11310  */
11311 Roo.data.HttpProxy = function(conn){
11312     Roo.data.HttpProxy.superclass.constructor.call(this);
11313     // is conn a conn config or a real conn?
11314     this.conn = conn;
11315     this.useAjax = !conn || !conn.events;
11316   
11317 };
11318
11319 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11320     // thse are take from connection...
11321     
11322     /**
11323      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11324      */
11325     /**
11326      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11327      * extra parameters to each request made by this object. (defaults to undefined)
11328      */
11329     /**
11330      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11331      *  to each request made by this object. (defaults to undefined)
11332      */
11333     /**
11334      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11335      */
11336     /**
11337      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11338      */
11339      /**
11340      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11341      * @type Boolean
11342      */
11343   
11344
11345     /**
11346      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11347      * @type Boolean
11348      */
11349     /**
11350      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11351      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11352      * a finer-grained basis than the DataProxy events.
11353      */
11354     getConnection : function(){
11355         return this.useAjax ? Roo.Ajax : this.conn;
11356     },
11357
11358     /**
11359      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11360      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11361      * process that block using the passed callback.
11362      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11363      * for the request to the remote server.
11364      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11365      * object into a block of Roo.data.Records.
11366      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11367      * The function must be passed <ul>
11368      * <li>The Record block object</li>
11369      * <li>The "arg" argument from the load function</li>
11370      * <li>A boolean success indicator</li>
11371      * </ul>
11372      * @param {Object} scope The scope in which to call the callback
11373      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11374      */
11375     load : function(params, reader, callback, scope, arg){
11376         if(this.fireEvent("beforeload", this, params) !== false){
11377             var  o = {
11378                 params : params || {},
11379                 request: {
11380                     callback : callback,
11381                     scope : scope,
11382                     arg : arg
11383                 },
11384                 reader: reader,
11385                 callback : this.loadResponse,
11386                 scope: this
11387             };
11388             if(this.useAjax){
11389                 Roo.applyIf(o, this.conn);
11390                 if(this.activeRequest){
11391                     Roo.Ajax.abort(this.activeRequest);
11392                 }
11393                 this.activeRequest = Roo.Ajax.request(o);
11394             }else{
11395                 this.conn.request(o);
11396             }
11397         }else{
11398             callback.call(scope||this, null, arg, false);
11399         }
11400     },
11401
11402     // private
11403     loadResponse : function(o, success, response){
11404         delete this.activeRequest;
11405         if(!success){
11406             this.fireEvent("loadexception", this, o, response);
11407             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11408             return;
11409         }
11410         var result;
11411         try {
11412             result = o.reader.read(response);
11413         }catch(e){
11414             this.fireEvent("loadexception", this, o, response, e);
11415             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11416             return;
11417         }
11418         
11419         this.fireEvent("load", this, o, o.request.arg);
11420         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11421     },
11422
11423     // private
11424     update : function(dataSet){
11425
11426     },
11427
11428     // private
11429     updateResponse : function(dataSet){
11430
11431     }
11432 });/*
11433  * Based on:
11434  * Ext JS Library 1.1.1
11435  * Copyright(c) 2006-2007, Ext JS, LLC.
11436  *
11437  * Originally Released Under LGPL - original licence link has changed is not relivant.
11438  *
11439  * Fork - LGPL
11440  * <script type="text/javascript">
11441  */
11442
11443 /**
11444  * @class Roo.data.ScriptTagProxy
11445  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11446  * other than the originating domain of the running page.<br><br>
11447  * <p>
11448  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
11449  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11450  * <p>
11451  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11452  * source code that is used as the source inside a &lt;script> tag.<br><br>
11453  * <p>
11454  * In order for the browser to process the returned data, the server must wrap the data object
11455  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11456  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11457  * depending on whether the callback name was passed:
11458  * <p>
11459  * <pre><code>
11460 boolean scriptTag = false;
11461 String cb = request.getParameter("callback");
11462 if (cb != null) {
11463     scriptTag = true;
11464     response.setContentType("text/javascript");
11465 } else {
11466     response.setContentType("application/x-json");
11467 }
11468 Writer out = response.getWriter();
11469 if (scriptTag) {
11470     out.write(cb + "(");
11471 }
11472 out.print(dataBlock.toJsonString());
11473 if (scriptTag) {
11474     out.write(");");
11475 }
11476 </pre></code>
11477  *
11478  * @constructor
11479  * @param {Object} config A configuration object.
11480  */
11481 Roo.data.ScriptTagProxy = function(config){
11482     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11483     Roo.apply(this, config);
11484     this.head = document.getElementsByTagName("head")[0];
11485 };
11486
11487 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11488
11489 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11490     /**
11491      * @cfg {String} url The URL from which to request the data object.
11492      */
11493     /**
11494      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11495      */
11496     timeout : 30000,
11497     /**
11498      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11499      * the server the name of the callback function set up by the load call to process the returned data object.
11500      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11501      * javascript output which calls this named function passing the data object as its only parameter.
11502      */
11503     callbackParam : "callback",
11504     /**
11505      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11506      * name to the request.
11507      */
11508     nocache : true,
11509
11510     /**
11511      * Load data from the configured URL, read the data object into
11512      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11513      * process that block using the passed callback.
11514      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11515      * for the request to the remote server.
11516      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11517      * object into a block of Roo.data.Records.
11518      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11519      * The function must be passed <ul>
11520      * <li>The Record block object</li>
11521      * <li>The "arg" argument from the load function</li>
11522      * <li>A boolean success indicator</li>
11523      * </ul>
11524      * @param {Object} scope The scope in which to call the callback
11525      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11526      */
11527     load : function(params, reader, callback, scope, arg){
11528         if(this.fireEvent("beforeload", this, params) !== false){
11529
11530             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11531
11532             var url = this.url;
11533             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11534             if(this.nocache){
11535                 url += "&_dc=" + (new Date().getTime());
11536             }
11537             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11538             var trans = {
11539                 id : transId,
11540                 cb : "stcCallback"+transId,
11541                 scriptId : "stcScript"+transId,
11542                 params : params,
11543                 arg : arg,
11544                 url : url,
11545                 callback : callback,
11546                 scope : scope,
11547                 reader : reader
11548             };
11549             var conn = this;
11550
11551             window[trans.cb] = function(o){
11552                 conn.handleResponse(o, trans);
11553             };
11554
11555             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11556
11557             if(this.autoAbort !== false){
11558                 this.abort();
11559             }
11560
11561             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11562
11563             var script = document.createElement("script");
11564             script.setAttribute("src", url);
11565             script.setAttribute("type", "text/javascript");
11566             script.setAttribute("id", trans.scriptId);
11567             this.head.appendChild(script);
11568
11569             this.trans = trans;
11570         }else{
11571             callback.call(scope||this, null, arg, false);
11572         }
11573     },
11574
11575     // private
11576     isLoading : function(){
11577         return this.trans ? true : false;
11578     },
11579
11580     /**
11581      * Abort the current server request.
11582      */
11583     abort : function(){
11584         if(this.isLoading()){
11585             this.destroyTrans(this.trans);
11586         }
11587     },
11588
11589     // private
11590     destroyTrans : function(trans, isLoaded){
11591         this.head.removeChild(document.getElementById(trans.scriptId));
11592         clearTimeout(trans.timeoutId);
11593         if(isLoaded){
11594             window[trans.cb] = undefined;
11595             try{
11596                 delete window[trans.cb];
11597             }catch(e){}
11598         }else{
11599             // if hasn't been loaded, wait for load to remove it to prevent script error
11600             window[trans.cb] = function(){
11601                 window[trans.cb] = undefined;
11602                 try{
11603                     delete window[trans.cb];
11604                 }catch(e){}
11605             };
11606         }
11607     },
11608
11609     // private
11610     handleResponse : function(o, trans){
11611         this.trans = false;
11612         this.destroyTrans(trans, true);
11613         var result;
11614         try {
11615             result = trans.reader.readRecords(o);
11616         }catch(e){
11617             this.fireEvent("loadexception", this, o, trans.arg, e);
11618             trans.callback.call(trans.scope||window, null, trans.arg, false);
11619             return;
11620         }
11621         this.fireEvent("load", this, o, trans.arg);
11622         trans.callback.call(trans.scope||window, result, trans.arg, true);
11623     },
11624
11625     // private
11626     handleFailure : function(trans){
11627         this.trans = false;
11628         this.destroyTrans(trans, false);
11629         this.fireEvent("loadexception", this, null, trans.arg);
11630         trans.callback.call(trans.scope||window, null, trans.arg, false);
11631     }
11632 });/*
11633  * Based on:
11634  * Ext JS Library 1.1.1
11635  * Copyright(c) 2006-2007, Ext JS, LLC.
11636  *
11637  * Originally Released Under LGPL - original licence link has changed is not relivant.
11638  *
11639  * Fork - LGPL
11640  * <script type="text/javascript">
11641  */
11642
11643 /**
11644  * @class Roo.data.JsonReader
11645  * @extends Roo.data.DataReader
11646  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11647  * based on mappings in a provided Roo.data.Record constructor.
11648  * 
11649  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11650  * in the reply previously. 
11651  * 
11652  * <p>
11653  * Example code:
11654  * <pre><code>
11655 var RecordDef = Roo.data.Record.create([
11656     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
11657     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
11658 ]);
11659 var myReader = new Roo.data.JsonReader({
11660     totalProperty: "results",    // The property which contains the total dataset size (optional)
11661     root: "rows",                // The property which contains an Array of row objects
11662     id: "id"                     // The property within each row object that provides an ID for the record (optional)
11663 }, RecordDef);
11664 </code></pre>
11665  * <p>
11666  * This would consume a JSON file like this:
11667  * <pre><code>
11668 { 'results': 2, 'rows': [
11669     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11670     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11671 }
11672 </code></pre>
11673  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11674  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11675  * paged from the remote server.
11676  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11677  * @cfg {String} root name of the property which contains the Array of row objects.
11678  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11679  * @cfg {Array} fields Array of field definition objects
11680  * @constructor
11681  * Create a new JsonReader
11682  * @param {Object} meta Metadata configuration options
11683  * @param {Object} recordType Either an Array of field definition objects,
11684  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11685  */
11686 Roo.data.JsonReader = function(meta, recordType){
11687     
11688     meta = meta || {};
11689     // set some defaults:
11690     Roo.applyIf(meta, {
11691         totalProperty: 'total',
11692         successProperty : 'success',
11693         root : 'data',
11694         id : 'id'
11695     });
11696     
11697     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11698 };
11699 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11700     
11701     /**
11702      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
11703      * Used by Store query builder to append _requestMeta to params.
11704      * 
11705      */
11706     metaFromRemote : false,
11707     /**
11708      * This method is only used by a DataProxy which has retrieved data from a remote server.
11709      * @param {Object} response The XHR object which contains the JSON data in its responseText.
11710      * @return {Object} data A data block which is used by an Roo.data.Store object as
11711      * a cache of Roo.data.Records.
11712      */
11713     read : function(response){
11714         var json = response.responseText;
11715        
11716         var o = /* eval:var:o */ eval("("+json+")");
11717         if(!o) {
11718             throw {message: "JsonReader.read: Json object not found"};
11719         }
11720         
11721         if(o.metaData){
11722             
11723             delete this.ef;
11724             this.metaFromRemote = true;
11725             this.meta = o.metaData;
11726             this.recordType = Roo.data.Record.create(o.metaData.fields);
11727             this.onMetaChange(this.meta, this.recordType, o);
11728         }
11729         return this.readRecords(o);
11730     },
11731
11732     // private function a store will implement
11733     onMetaChange : function(meta, recordType, o){
11734
11735     },
11736
11737     /**
11738          * @ignore
11739          */
11740     simpleAccess: function(obj, subsc) {
11741         return obj[subsc];
11742     },
11743
11744         /**
11745          * @ignore
11746          */
11747     getJsonAccessor: function(){
11748         var re = /[\[\.]/;
11749         return function(expr) {
11750             try {
11751                 return(re.test(expr))
11752                     ? new Function("obj", "return obj." + expr)
11753                     : function(obj){
11754                         return obj[expr];
11755                     };
11756             } catch(e){}
11757             return Roo.emptyFn;
11758         };
11759     }(),
11760
11761     /**
11762      * Create a data block containing Roo.data.Records from an XML document.
11763      * @param {Object} o An object which contains an Array of row objects in the property specified
11764      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11765      * which contains the total size of the dataset.
11766      * @return {Object} data A data block which is used by an Roo.data.Store object as
11767      * a cache of Roo.data.Records.
11768      */
11769     readRecords : function(o){
11770         /**
11771          * After any data loads, the raw JSON data is available for further custom processing.
11772          * @type Object
11773          */
11774         this.o = o;
11775         var s = this.meta, Record = this.recordType,
11776             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11777
11778 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
11779         if (!this.ef) {
11780             if(s.totalProperty) {
11781                     this.getTotal = this.getJsonAccessor(s.totalProperty);
11782                 }
11783                 if(s.successProperty) {
11784                     this.getSuccess = this.getJsonAccessor(s.successProperty);
11785                 }
11786                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11787                 if (s.id) {
11788                         var g = this.getJsonAccessor(s.id);
11789                         this.getId = function(rec) {
11790                                 var r = g(rec);  
11791                                 return (r === undefined || r === "") ? null : r;
11792                         };
11793                 } else {
11794                         this.getId = function(){return null;};
11795                 }
11796             this.ef = [];
11797             for(var jj = 0; jj < fl; jj++){
11798                 f = fi[jj];
11799                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11800                 this.ef[jj] = this.getJsonAccessor(map);
11801             }
11802         }
11803
11804         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11805         if(s.totalProperty){
11806             var vt = parseInt(this.getTotal(o), 10);
11807             if(!isNaN(vt)){
11808                 totalRecords = vt;
11809             }
11810         }
11811         if(s.successProperty){
11812             var vs = this.getSuccess(o);
11813             if(vs === false || vs === 'false'){
11814                 success = false;
11815             }
11816         }
11817         var records = [];
11818         for(var i = 0; i < c; i++){
11819                 var n = root[i];
11820             var values = {};
11821             var id = this.getId(n);
11822             for(var j = 0; j < fl; j++){
11823                 f = fi[j];
11824             var v = this.ef[j](n);
11825             if (!f.convert) {
11826                 Roo.log('missing convert for ' + f.name);
11827                 Roo.log(f);
11828                 continue;
11829             }
11830             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11831             }
11832             var record = new Record(values, id);
11833             record.json = n;
11834             records[i] = record;
11835         }
11836         return {
11837             raw : o,
11838             success : success,
11839             records : records,
11840             totalRecords : totalRecords
11841         };
11842     }
11843 });/*
11844  * Based on:
11845  * Ext JS Library 1.1.1
11846  * Copyright(c) 2006-2007, Ext JS, LLC.
11847  *
11848  * Originally Released Under LGPL - original licence link has changed is not relivant.
11849  *
11850  * Fork - LGPL
11851  * <script type="text/javascript">
11852  */
11853
11854 /**
11855  * @class Roo.data.ArrayReader
11856  * @extends Roo.data.DataReader
11857  * Data reader class to create an Array of Roo.data.Record objects from an Array.
11858  * Each element of that Array represents a row of data fields. The
11859  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11860  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11861  * <p>
11862  * Example code:.
11863  * <pre><code>
11864 var RecordDef = Roo.data.Record.create([
11865     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11866     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11867 ]);
11868 var myReader = new Roo.data.ArrayReader({
11869     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11870 }, RecordDef);
11871 </code></pre>
11872  * <p>
11873  * This would consume an Array like this:
11874  * <pre><code>
11875 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11876   </code></pre>
11877  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11878  * @constructor
11879  * Create a new JsonReader
11880  * @param {Object} meta Metadata configuration options.
11881  * @param {Object} recordType Either an Array of field definition objects
11882  * as specified to {@link Roo.data.Record#create},
11883  * or an {@link Roo.data.Record} object
11884  * created using {@link Roo.data.Record#create}.
11885  */
11886 Roo.data.ArrayReader = function(meta, recordType){
11887     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11888 };
11889
11890 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11891     /**
11892      * Create a data block containing Roo.data.Records from an XML document.
11893      * @param {Object} o An Array of row objects which represents the dataset.
11894      * @return {Object} data A data block which is used by an Roo.data.Store object as
11895      * a cache of Roo.data.Records.
11896      */
11897     readRecords : function(o){
11898         var sid = this.meta ? this.meta.id : null;
11899         var recordType = this.recordType, fields = recordType.prototype.fields;
11900         var records = [];
11901         var root = o;
11902             for(var i = 0; i < root.length; i++){
11903                     var n = root[i];
11904                 var values = {};
11905                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11906                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11907                 var f = fields.items[j];
11908                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11909                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11910                 v = f.convert(v);
11911                 values[f.name] = v;
11912             }
11913                 var record = new recordType(values, id);
11914                 record.json = n;
11915                 records[records.length] = record;
11916             }
11917             return {
11918                 records : records,
11919                 totalRecords : records.length
11920             };
11921     }
11922 });/*
11923  * - LGPL
11924  * * 
11925  */
11926
11927 /**
11928  * @class Roo.bootstrap.ComboBox
11929  * @extends Roo.bootstrap.TriggerField
11930  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11931  * @cfg {Boolean} append (true|false) default false
11932  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11933  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11934  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11935  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11936  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11937  * @cfg {Boolean} animate default true
11938  * @cfg {Boolean} emptyResultText only for touch device
11939  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11940  * @constructor
11941  * Create a new ComboBox.
11942  * @param {Object} config Configuration options
11943  */
11944 Roo.bootstrap.ComboBox = function(config){
11945     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11946     this.addEvents({
11947         /**
11948          * @event expand
11949          * Fires when the dropdown list is expanded
11950              * @param {Roo.bootstrap.ComboBox} combo This combo box
11951              */
11952         'expand' : true,
11953         /**
11954          * @event collapse
11955          * Fires when the dropdown list is collapsed
11956              * @param {Roo.bootstrap.ComboBox} combo This combo box
11957              */
11958         'collapse' : true,
11959         /**
11960          * @event beforeselect
11961          * Fires before a list item is selected. Return false to cancel the selection.
11962              * @param {Roo.bootstrap.ComboBox} combo This combo box
11963              * @param {Roo.data.Record} record The data record returned from the underlying store
11964              * @param {Number} index The index of the selected item in the dropdown list
11965              */
11966         'beforeselect' : true,
11967         /**
11968          * @event select
11969          * Fires when a list item is selected
11970              * @param {Roo.bootstrap.ComboBox} combo This combo box
11971              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11972              * @param {Number} index The index of the selected item in the dropdown list
11973              */
11974         'select' : true,
11975         /**
11976          * @event beforequery
11977          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11978          * The event object passed has these properties:
11979              * @param {Roo.bootstrap.ComboBox} combo This combo box
11980              * @param {String} query The query
11981              * @param {Boolean} forceAll true to force "all" query
11982              * @param {Boolean} cancel true to cancel the query
11983              * @param {Object} e The query event object
11984              */
11985         'beforequery': true,
11986          /**
11987          * @event add
11988          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11989              * @param {Roo.bootstrap.ComboBox} combo This combo box
11990              */
11991         'add' : true,
11992         /**
11993          * @event edit
11994          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11995              * @param {Roo.bootstrap.ComboBox} combo This combo box
11996              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11997              */
11998         'edit' : true,
11999         /**
12000          * @event remove
12001          * Fires when the remove value from the combobox array
12002              * @param {Roo.bootstrap.ComboBox} combo This combo box
12003              */
12004         'remove' : true,
12005         /**
12006          * @event afterremove
12007          * Fires when the remove value from the combobox array
12008              * @param {Roo.bootstrap.ComboBox} combo This combo box
12009              */
12010         'afterremove' : true,
12011         /**
12012          * @event specialfilter
12013          * Fires when specialfilter
12014             * @param {Roo.bootstrap.ComboBox} combo This combo box
12015             */
12016         'specialfilter' : true,
12017         /**
12018          * @event tick
12019          * Fires when tick the element
12020             * @param {Roo.bootstrap.ComboBox} combo This combo box
12021             */
12022         'tick' : true,
12023         /**
12024          * @event touchviewdisplay
12025          * Fires when touch view require special display (default is using displayField)
12026             * @param {Roo.bootstrap.ComboBox} combo This combo box
12027             * @param {Object} cfg set html .
12028             */
12029         'touchviewdisplay' : true
12030         
12031     });
12032     
12033     this.item = [];
12034     this.tickItems = [];
12035     
12036     this.selectedIndex = -1;
12037     if(this.mode == 'local'){
12038         if(config.queryDelay === undefined){
12039             this.queryDelay = 10;
12040         }
12041         if(config.minChars === undefined){
12042             this.minChars = 0;
12043         }
12044     }
12045 };
12046
12047 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12048      
12049     /**
12050      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12051      * rendering into an Roo.Editor, defaults to false)
12052      */
12053     /**
12054      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12055      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12056      */
12057     /**
12058      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12059      */
12060     /**
12061      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12062      * the dropdown list (defaults to undefined, with no header element)
12063      */
12064
12065      /**
12066      * @cfg {String/Roo.Template} tpl The template to use to render the output
12067      */
12068      
12069      /**
12070      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12071      */
12072     listWidth: undefined,
12073     /**
12074      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12075      * mode = 'remote' or 'text' if mode = 'local')
12076      */
12077     displayField: undefined,
12078     
12079     /**
12080      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12081      * mode = 'remote' or 'value' if mode = 'local'). 
12082      * Note: use of a valueField requires the user make a selection
12083      * in order for a value to be mapped.
12084      */
12085     valueField: undefined,
12086     /**
12087      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12088      */
12089     modalTitle : '',
12090     
12091     /**
12092      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12093      * field's data value (defaults to the underlying DOM element's name)
12094      */
12095     hiddenName: undefined,
12096     /**
12097      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12098      */
12099     listClass: '',
12100     /**
12101      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12102      */
12103     selectedClass: 'active',
12104     
12105     /**
12106      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12107      */
12108     shadow:'sides',
12109     /**
12110      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12111      * anchor positions (defaults to 'tl-bl')
12112      */
12113     listAlign: 'tl-bl?',
12114     /**
12115      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12116      */
12117     maxHeight: 300,
12118     /**
12119      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12120      * query specified by the allQuery config option (defaults to 'query')
12121      */
12122     triggerAction: 'query',
12123     /**
12124      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12125      * (defaults to 4, does not apply if editable = false)
12126      */
12127     minChars : 4,
12128     /**
12129      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12130      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12131      */
12132     typeAhead: false,
12133     /**
12134      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12135      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12136      */
12137     queryDelay: 500,
12138     /**
12139      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12140      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12141      */
12142     pageSize: 0,
12143     /**
12144      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12145      * when editable = true (defaults to false)
12146      */
12147     selectOnFocus:false,
12148     /**
12149      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12150      */
12151     queryParam: 'query',
12152     /**
12153      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12154      * when mode = 'remote' (defaults to 'Loading...')
12155      */
12156     loadingText: 'Loading...',
12157     /**
12158      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12159      */
12160     resizable: false,
12161     /**
12162      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12163      */
12164     handleHeight : 8,
12165     /**
12166      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12167      * traditional select (defaults to true)
12168      */
12169     editable: true,
12170     /**
12171      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12172      */
12173     allQuery: '',
12174     /**
12175      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12176      */
12177     mode: 'remote',
12178     /**
12179      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12180      * listWidth has a higher value)
12181      */
12182     minListWidth : 70,
12183     /**
12184      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12185      * allow the user to set arbitrary text into the field (defaults to false)
12186      */
12187     forceSelection:false,
12188     /**
12189      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12190      * if typeAhead = true (defaults to 250)
12191      */
12192     typeAheadDelay : 250,
12193     /**
12194      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12195      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12196      */
12197     valueNotFoundText : undefined,
12198     /**
12199      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12200      */
12201     blockFocus : false,
12202     
12203     /**
12204      * @cfg {Boolean} disableClear Disable showing of clear button.
12205      */
12206     disableClear : false,
12207     /**
12208      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12209      */
12210     alwaysQuery : false,
12211     
12212     /**
12213      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12214      */
12215     multiple : false,
12216     
12217     /**
12218      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12219      */
12220     invalidClass : "has-warning",
12221     
12222     /**
12223      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12224      */
12225     validClass : "has-success",
12226     
12227     /**
12228      * @cfg {Boolean} specialFilter (true|false) special filter default false
12229      */
12230     specialFilter : false,
12231     
12232     /**
12233      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12234      */
12235     mobileTouchView : true,
12236     
12237     /**
12238      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12239      */
12240     useNativeIOS : false,
12241     
12242     ios_options : false,
12243     
12244     //private
12245     addicon : false,
12246     editicon: false,
12247     
12248     page: 0,
12249     hasQuery: false,
12250     append: false,
12251     loadNext: false,
12252     autoFocus : true,
12253     tickable : false,
12254     btnPosition : 'right',
12255     triggerList : true,
12256     showToggleBtn : true,
12257     animate : true,
12258     emptyResultText: 'Empty',
12259     triggerText : 'Select',
12260     
12261     // element that contains real text value.. (when hidden is used..)
12262     
12263     getAutoCreate : function()
12264     {
12265         var cfg = false;
12266         
12267         /*
12268          * Render classic select for iso
12269          */
12270         
12271         if(Roo.isIOS && this.useNativeIOS){
12272             cfg = this.getAutoCreateNativeIOS();
12273             return cfg;
12274         }
12275         
12276         /*
12277          * Touch Devices
12278          */
12279         
12280         if(Roo.isTouch && this.mobileTouchView){
12281             cfg = this.getAutoCreateTouchView();
12282             return cfg;;
12283         }
12284         
12285         /*
12286          *  Normal ComboBox
12287          */
12288         if(!this.tickable){
12289             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12290             return cfg;
12291         }
12292         
12293         /*
12294          *  ComboBox with tickable selections
12295          */
12296              
12297         var align = this.labelAlign || this.parentLabelAlign();
12298         
12299         cfg = {
12300             cls : 'form-group roo-combobox-tickable' //input-group
12301         };
12302         
12303         var buttons = {
12304             tag : 'div',
12305             cls : 'tickable-buttons',
12306             cn : [
12307                 {
12308                     tag : 'button',
12309                     type : 'button',
12310                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12311                     html : this.triggerText
12312                 },
12313                 {
12314                     tag : 'button',
12315                     type : 'button',
12316                     name : 'ok',
12317                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12318                     html : 'Done'
12319                 },
12320                 {
12321                     tag : 'button',
12322                     type : 'button',
12323                     name : 'cancel',
12324                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12325                     html : 'Cancel'
12326                 }
12327             ]
12328         };
12329         
12330         if(this.editable){
12331             buttons.cn.unshift({
12332                 tag: 'input',
12333                 cls: 'roo-select2-search-field-input'
12334             });
12335         }
12336         
12337         var _this = this;
12338         
12339         Roo.each(buttons.cn, function(c){
12340             if (_this.size) {
12341                 c.cls += ' btn-' + _this.size;
12342             }
12343
12344             if (_this.disabled) {
12345                 c.disabled = true;
12346             }
12347         });
12348         
12349         var box = {
12350             tag: 'div',
12351             cn: [
12352                 {
12353                     tag: 'input',
12354                     type : 'hidden',
12355                     cls: 'form-hidden-field'
12356                 },
12357                 {
12358                     tag: 'ul',
12359                     cls: 'roo-select2-choices',
12360                     cn:[
12361                         {
12362                             tag: 'li',
12363                             cls: 'roo-select2-search-field',
12364                             cn: [
12365
12366                                 buttons
12367                             ]
12368                         }
12369                     ]
12370                 }
12371             ]
12372         };
12373         
12374         var combobox = {
12375             cls: 'roo-select2-container input-group roo-select2-container-multi',
12376             cn: [
12377                 box
12378 //                {
12379 //                    tag: 'ul',
12380 //                    cls: 'typeahead typeahead-long dropdown-menu',
12381 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12382 //                }
12383             ]
12384         };
12385         
12386         if(this.hasFeedback && !this.allowBlank){
12387             
12388             var feedback = {
12389                 tag: 'span',
12390                 cls: 'glyphicon form-control-feedback'
12391             };
12392
12393             combobox.cn.push(feedback);
12394         }
12395         
12396         if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
12397             
12398 //                Roo.log("left and has label");
12399             cfg.cn = [
12400                 {
12401                     tag : 'i',
12402                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12403                     tooltip : 'This field is required'
12404                 },
12405                 {
12406                     tag: 'label',
12407                     'for' :  id,
12408                     cls : 'control-label col-sm-' + this.labelWidth,
12409                     html : this.fieldLabel
12410
12411                 },
12412                 {
12413                     cls : "col-sm-" + (12 - this.labelWidth), 
12414                     cn: [
12415                         combobox
12416                     ]
12417                 }
12418
12419             ];
12420
12421             if(this.indicatorpos == 'right'){
12422                 
12423                 cfg.cn = [
12424                     {
12425                         tag: 'label',
12426                         'for' :  id,
12427                         cls : 'control-label col-sm-' + this.labelWidth,
12428                         html : this.fieldLabel
12429
12430                     },
12431                     {
12432                         tag : 'i',
12433                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12434                         tooltip : 'This field is required'
12435                     },
12436                     {
12437                         cls : "col-sm-" + (12 - this.labelWidth), 
12438                         cn: [
12439                             combobox
12440                         ]
12441                     }
12442
12443                 ];
12444             
12445             }
12446                 
12447                 
12448         } else if ( this.fieldLabel.length) {
12449 //                Roo.log(" label");
12450                  cfg.cn = [
12451                     {
12452                         tag : 'i',
12453                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12454                         tooltip : 'This field is required'
12455                     },
12456                     {
12457                         tag: 'label',
12458                         //cls : 'input-group-addon',
12459                         html : this.fieldLabel
12460                         
12461                     },
12462                     
12463                     combobox
12464                     
12465                 ];
12466                 
12467                 if(this.indicatorpos == 'right'){
12468                     
12469                     cfg.cn = [
12470                         {
12471                             tag: 'label',
12472                             //cls : 'input-group-addon',
12473                             html : this.fieldLabel
12474
12475                         },
12476                         
12477                         {
12478                             tag : 'i',
12479                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12480                             tooltip : 'This field is required'
12481                         },
12482                         
12483                         combobox
12484
12485                     ];
12486                 
12487                 }
12488
12489         } else {
12490             
12491 //                Roo.log(" no label && no align");
12492                 cfg = combobox
12493                      
12494                 
12495         }
12496          
12497         var settings=this;
12498         ['xs','sm','md','lg'].map(function(size){
12499             if (settings[size]) {
12500                 cfg.cls += ' col-' + size + '-' + settings[size];
12501             }
12502         });
12503         
12504         return cfg;
12505         
12506     },
12507     
12508     _initEventsCalled : false,
12509     
12510     // private
12511     initEvents: function()
12512     {   
12513         if (this._initEventsCalled) { // as we call render... prevent looping...
12514             return;
12515         }
12516         this._initEventsCalled = true;
12517         
12518         if (!this.store) {
12519             throw "can not find store for combo";
12520         }
12521         
12522         this.store = Roo.factory(this.store, Roo.data);
12523         
12524         // if we are building from html. then this element is so complex, that we can not really
12525         // use the rendered HTML.
12526         // so we have to trash and replace the previous code.
12527         if (Roo.XComponent.build_from_html) {
12528             
12529             // remove this element....
12530             var e = this.el.dom, k=0;
12531             while (e ) { e = e.previousSibling;  ++k;}
12532
12533             this.el.remove();
12534             
12535             this.el=false;
12536             this.rendered = false;
12537             
12538             this.render(this.parent().getChildContainer(true), k);
12539             
12540             
12541             
12542         }
12543         
12544         if(Roo.isIOS && this.useNativeIOS){
12545             this.initIOSView();
12546             return;
12547         }
12548         
12549         /*
12550          * Touch Devices
12551          */
12552         
12553         if(Roo.isTouch && this.mobileTouchView){
12554             this.initTouchView();
12555             return;
12556         }
12557         
12558         if(this.tickable){
12559             this.initTickableEvents();
12560             return;
12561         }
12562         
12563         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12564         
12565         if(this.hiddenName){
12566             
12567             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12568             
12569             this.hiddenField.dom.value =
12570                 this.hiddenValue !== undefined ? this.hiddenValue :
12571                 this.value !== undefined ? this.value : '';
12572
12573             // prevent input submission
12574             this.el.dom.removeAttribute('name');
12575             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12576              
12577              
12578         }
12579         //if(Roo.isGecko){
12580         //    this.el.dom.setAttribute('autocomplete', 'off');
12581         //}
12582         
12583         var cls = 'x-combo-list';
12584         
12585         //this.list = new Roo.Layer({
12586         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12587         //});
12588         
12589         var _this = this;
12590         
12591         (function(){
12592             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12593             _this.list.setWidth(lw);
12594         }).defer(100);
12595         
12596         this.list.on('mouseover', this.onViewOver, this);
12597         this.list.on('mousemove', this.onViewMove, this);
12598         
12599         this.list.on('scroll', this.onViewScroll, this);
12600         
12601         /*
12602         this.list.swallowEvent('mousewheel');
12603         this.assetHeight = 0;
12604
12605         if(this.title){
12606             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12607             this.assetHeight += this.header.getHeight();
12608         }
12609
12610         this.innerList = this.list.createChild({cls:cls+'-inner'});
12611         this.innerList.on('mouseover', this.onViewOver, this);
12612         this.innerList.on('mousemove', this.onViewMove, this);
12613         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12614         
12615         if(this.allowBlank && !this.pageSize && !this.disableClear){
12616             this.footer = this.list.createChild({cls:cls+'-ft'});
12617             this.pageTb = new Roo.Toolbar(this.footer);
12618            
12619         }
12620         if(this.pageSize){
12621             this.footer = this.list.createChild({cls:cls+'-ft'});
12622             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12623                     {pageSize: this.pageSize});
12624             
12625         }
12626         
12627         if (this.pageTb && this.allowBlank && !this.disableClear) {
12628             var _this = this;
12629             this.pageTb.add(new Roo.Toolbar.Fill(), {
12630                 cls: 'x-btn-icon x-btn-clear',
12631                 text: '&#160;',
12632                 handler: function()
12633                 {
12634                     _this.collapse();
12635                     _this.clearValue();
12636                     _this.onSelect(false, -1);
12637                 }
12638             });
12639         }
12640         if (this.footer) {
12641             this.assetHeight += this.footer.getHeight();
12642         }
12643         */
12644             
12645         if(!this.tpl){
12646             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12647         }
12648
12649         this.view = new Roo.View(this.list, this.tpl, {
12650             singleSelect:true, store: this.store, selectedClass: this.selectedClass
12651         });
12652         //this.view.wrapEl.setDisplayed(false);
12653         this.view.on('click', this.onViewClick, this);
12654         
12655         
12656         
12657         this.store.on('beforeload', this.onBeforeLoad, this);
12658         this.store.on('load', this.onLoad, this);
12659         this.store.on('loadexception', this.onLoadException, this);
12660         /*
12661         if(this.resizable){
12662             this.resizer = new Roo.Resizable(this.list,  {
12663                pinned:true, handles:'se'
12664             });
12665             this.resizer.on('resize', function(r, w, h){
12666                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12667                 this.listWidth = w;
12668                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12669                 this.restrictHeight();
12670             }, this);
12671             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12672         }
12673         */
12674         if(!this.editable){
12675             this.editable = true;
12676             this.setEditable(false);
12677         }
12678         
12679         /*
12680         
12681         if (typeof(this.events.add.listeners) != 'undefined') {
12682             
12683             this.addicon = this.wrap.createChild(
12684                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
12685        
12686             this.addicon.on('click', function(e) {
12687                 this.fireEvent('add', this);
12688             }, this);
12689         }
12690         if (typeof(this.events.edit.listeners) != 'undefined') {
12691             
12692             this.editicon = this.wrap.createChild(
12693                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
12694             if (this.addicon) {
12695                 this.editicon.setStyle('margin-left', '40px');
12696             }
12697             this.editicon.on('click', function(e) {
12698                 
12699                 // we fire even  if inothing is selected..
12700                 this.fireEvent('edit', this, this.lastData );
12701                 
12702             }, this);
12703         }
12704         */
12705         
12706         this.keyNav = new Roo.KeyNav(this.inputEl(), {
12707             "up" : function(e){
12708                 this.inKeyMode = true;
12709                 this.selectPrev();
12710             },
12711
12712             "down" : function(e){
12713                 if(!this.isExpanded()){
12714                     this.onTriggerClick();
12715                 }else{
12716                     this.inKeyMode = true;
12717                     this.selectNext();
12718                 }
12719             },
12720
12721             "enter" : function(e){
12722 //                this.onViewClick();
12723                 //return true;
12724                 this.collapse();
12725                 
12726                 if(this.fireEvent("specialkey", this, e)){
12727                     this.onViewClick(false);
12728                 }
12729                 
12730                 return true;
12731             },
12732
12733             "esc" : function(e){
12734                 this.collapse();
12735             },
12736
12737             "tab" : function(e){
12738                 this.collapse();
12739                 
12740                 if(this.fireEvent("specialkey", this, e)){
12741                     this.onViewClick(false);
12742                 }
12743                 
12744                 return true;
12745             },
12746
12747             scope : this,
12748
12749             doRelay : function(foo, bar, hname){
12750                 if(hname == 'down' || this.scope.isExpanded()){
12751                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12752                 }
12753                 return true;
12754             },
12755
12756             forceKeyDown: true
12757         });
12758         
12759         
12760         this.queryDelay = Math.max(this.queryDelay || 10,
12761                 this.mode == 'local' ? 10 : 250);
12762         
12763         
12764         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12765         
12766         if(this.typeAhead){
12767             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12768         }
12769         if(this.editable !== false){
12770             this.inputEl().on("keyup", this.onKeyUp, this);
12771         }
12772         if(this.forceSelection){
12773             this.inputEl().on('blur', this.doForce, this);
12774         }
12775         
12776         if(this.multiple){
12777             this.choices = this.el.select('ul.roo-select2-choices', true).first();
12778             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12779         }
12780     },
12781     
12782     initTickableEvents: function()
12783     {   
12784         this.createList();
12785         
12786         if(this.hiddenName){
12787             
12788             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12789             
12790             this.hiddenField.dom.value =
12791                 this.hiddenValue !== undefined ? this.hiddenValue :
12792                 this.value !== undefined ? this.value : '';
12793
12794             // prevent input submission
12795             this.el.dom.removeAttribute('name');
12796             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12797              
12798              
12799         }
12800         
12801 //        this.list = this.el.select('ul.dropdown-menu',true).first();
12802         
12803         this.choices = this.el.select('ul.roo-select2-choices', true).first();
12804         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12805         if(this.triggerList){
12806             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12807         }
12808          
12809         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12810         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12811         
12812         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12813         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12814         
12815         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12816         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12817         
12818         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12819         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12820         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12821         
12822         this.okBtn.hide();
12823         this.cancelBtn.hide();
12824         
12825         var _this = this;
12826         
12827         (function(){
12828             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12829             _this.list.setWidth(lw);
12830         }).defer(100);
12831         
12832         this.list.on('mouseover', this.onViewOver, this);
12833         this.list.on('mousemove', this.onViewMove, this);
12834         
12835         this.list.on('scroll', this.onViewScroll, this);
12836         
12837         if(!this.tpl){
12838             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}" type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></li>';
12839         }
12840
12841         this.view = new Roo.View(this.list, this.tpl, {
12842             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12843         });
12844         
12845         //this.view.wrapEl.setDisplayed(false);
12846         this.view.on('click', this.onViewClick, this);
12847         
12848         
12849         
12850         this.store.on('beforeload', this.onBeforeLoad, this);
12851         this.store.on('load', this.onLoad, this);
12852         this.store.on('loadexception', this.onLoadException, this);
12853         
12854         if(this.editable){
12855             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12856                 "up" : function(e){
12857                     this.inKeyMode = true;
12858                     this.selectPrev();
12859                 },
12860
12861                 "down" : function(e){
12862                     this.inKeyMode = true;
12863                     this.selectNext();
12864                 },
12865
12866                 "enter" : function(e){
12867                     if(this.fireEvent("specialkey", this, e)){
12868                         this.onViewClick(false);
12869                     }
12870                     
12871                     return true;
12872                 },
12873
12874                 "esc" : function(e){
12875                     this.onTickableFooterButtonClick(e, false, false);
12876                 },
12877
12878                 "tab" : function(e){
12879                     this.fireEvent("specialkey", this, e);
12880                     
12881                     this.onTickableFooterButtonClick(e, false, false);
12882                     
12883                     return true;
12884                 },
12885
12886                 scope : this,
12887
12888                 doRelay : function(e, fn, key){
12889                     if(this.scope.isExpanded()){
12890                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12891                     }
12892                     return true;
12893                 },
12894
12895                 forceKeyDown: true
12896             });
12897         }
12898         
12899         this.queryDelay = Math.max(this.queryDelay || 10,
12900                 this.mode == 'local' ? 10 : 250);
12901         
12902         
12903         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12904         
12905         if(this.typeAhead){
12906             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12907         }
12908         
12909         if(this.editable !== false){
12910             this.tickableInputEl().on("keyup", this.onKeyUp, this);
12911         }
12912         
12913     },
12914
12915     onDestroy : function(){
12916         if(this.view){
12917             this.view.setStore(null);
12918             this.view.el.removeAllListeners();
12919             this.view.el.remove();
12920             this.view.purgeListeners();
12921         }
12922         if(this.list){
12923             this.list.dom.innerHTML  = '';
12924         }
12925         
12926         if(this.store){
12927             this.store.un('beforeload', this.onBeforeLoad, this);
12928             this.store.un('load', this.onLoad, this);
12929             this.store.un('loadexception', this.onLoadException, this);
12930         }
12931         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12932     },
12933
12934     // private
12935     fireKey : function(e){
12936         if(e.isNavKeyPress() && !this.list.isVisible()){
12937             this.fireEvent("specialkey", this, e);
12938         }
12939     },
12940
12941     // private
12942     onResize: function(w, h){
12943 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12944 //        
12945 //        if(typeof w != 'number'){
12946 //            // we do not handle it!?!?
12947 //            return;
12948 //        }
12949 //        var tw = this.trigger.getWidth();
12950 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
12951 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
12952 //        var x = w - tw;
12953 //        this.inputEl().setWidth( this.adjustWidth('input', x));
12954 //            
12955 //        //this.trigger.setStyle('left', x+'px');
12956 //        
12957 //        if(this.list && this.listWidth === undefined){
12958 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12959 //            this.list.setWidth(lw);
12960 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12961 //        }
12962         
12963     
12964         
12965     },
12966
12967     /**
12968      * Allow or prevent the user from directly editing the field text.  If false is passed,
12969      * the user will only be able to select from the items defined in the dropdown list.  This method
12970      * is the runtime equivalent of setting the 'editable' config option at config time.
12971      * @param {Boolean} value True to allow the user to directly edit the field text
12972      */
12973     setEditable : function(value){
12974         if(value == this.editable){
12975             return;
12976         }
12977         this.editable = value;
12978         if(!value){
12979             this.inputEl().dom.setAttribute('readOnly', true);
12980             this.inputEl().on('mousedown', this.onTriggerClick,  this);
12981             this.inputEl().addClass('x-combo-noedit');
12982         }else{
12983             this.inputEl().dom.setAttribute('readOnly', false);
12984             this.inputEl().un('mousedown', this.onTriggerClick,  this);
12985             this.inputEl().removeClass('x-combo-noedit');
12986         }
12987     },
12988
12989     // private
12990     
12991     onBeforeLoad : function(combo,opts){
12992         if(!this.hasFocus){
12993             return;
12994         }
12995          if (!opts.add) {
12996             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12997          }
12998         this.restrictHeight();
12999         this.selectedIndex = -1;
13000     },
13001
13002     // private
13003     onLoad : function(){
13004         
13005         this.hasQuery = false;
13006         
13007         if(!this.hasFocus){
13008             return;
13009         }
13010         
13011         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13012             this.loading.hide();
13013         }
13014              
13015         if(this.store.getCount() > 0){
13016             this.expand();
13017             this.restrictHeight();
13018             if(this.lastQuery == this.allQuery){
13019                 if(this.editable && !this.tickable){
13020                     this.inputEl().dom.select();
13021                 }
13022                 
13023                 if(
13024                     !this.selectByValue(this.value, true) &&
13025                     this.autoFocus && 
13026                     (
13027                         !this.store.lastOptions ||
13028                         typeof(this.store.lastOptions.add) == 'undefined' || 
13029                         this.store.lastOptions.add != true
13030                     )
13031                 ){
13032                     this.select(0, true);
13033                 }
13034             }else{
13035                 if(this.autoFocus){
13036                     this.selectNext();
13037                 }
13038                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13039                     this.taTask.delay(this.typeAheadDelay);
13040                 }
13041             }
13042         }else{
13043             this.onEmptyResults();
13044         }
13045         
13046         //this.el.focus();
13047     },
13048     // private
13049     onLoadException : function()
13050     {
13051         this.hasQuery = false;
13052         
13053         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13054             this.loading.hide();
13055         }
13056         
13057         if(this.tickable && this.editable){
13058             return;
13059         }
13060         
13061         this.collapse();
13062         // only causes errors at present
13063         //Roo.log(this.store.reader.jsonData);
13064         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13065             // fixme
13066             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13067         //}
13068         
13069         
13070     },
13071     // private
13072     onTypeAhead : function(){
13073         if(this.store.getCount() > 0){
13074             var r = this.store.getAt(0);
13075             var newValue = r.data[this.displayField];
13076             var len = newValue.length;
13077             var selStart = this.getRawValue().length;
13078             
13079             if(selStart != len){
13080                 this.setRawValue(newValue);
13081                 this.selectText(selStart, newValue.length);
13082             }
13083         }
13084     },
13085
13086     // private
13087     onSelect : function(record, index){
13088         
13089         if(this.fireEvent('beforeselect', this, record, index) !== false){
13090         
13091             this.setFromData(index > -1 ? record.data : false);
13092             
13093             this.collapse();
13094             this.fireEvent('select', this, record, index);
13095         }
13096     },
13097
13098     /**
13099      * Returns the currently selected field value or empty string if no value is set.
13100      * @return {String} value The selected value
13101      */
13102     getValue : function()
13103     {
13104         if(Roo.isIOS && this.useNativeIOS){
13105             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13106         }
13107         
13108         if(this.multiple){
13109             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13110         }
13111         
13112         if(this.valueField){
13113             return typeof this.value != 'undefined' ? this.value : '';
13114         }else{
13115             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13116         }
13117     },
13118     
13119     getRawValue : function()
13120     {
13121         if(Roo.isIOS && this.useNativeIOS){
13122             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13123         }
13124         
13125         var v = this.inputEl().getValue();
13126         
13127         return v;
13128     },
13129
13130     /**
13131      * Clears any text/value currently set in the field
13132      */
13133     clearValue : function(){
13134         
13135         if(this.hiddenField){
13136             this.hiddenField.dom.value = '';
13137         }
13138         this.value = '';
13139         this.setRawValue('');
13140         this.lastSelectionText = '';
13141         this.lastData = false;
13142         
13143         var close = this.closeTriggerEl();
13144         
13145         if(close){
13146             close.hide();
13147         }
13148         
13149         this.validate();
13150         
13151     },
13152
13153     /**
13154      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13155      * will be displayed in the field.  If the value does not match the data value of an existing item,
13156      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13157      * Otherwise the field will be blank (although the value will still be set).
13158      * @param {String} value The value to match
13159      */
13160     setValue : function(v)
13161     {
13162         if(Roo.isIOS && this.useNativeIOS){
13163             this.setIOSValue(v);
13164             return;
13165         }
13166         
13167         if(this.multiple){
13168             this.syncValue();
13169             return;
13170         }
13171         
13172         var text = v;
13173         if(this.valueField){
13174             var r = this.findRecord(this.valueField, v);
13175             if(r){
13176                 text = r.data[this.displayField];
13177             }else if(this.valueNotFoundText !== undefined){
13178                 text = this.valueNotFoundText;
13179             }
13180         }
13181         this.lastSelectionText = text;
13182         if(this.hiddenField){
13183             this.hiddenField.dom.value = v;
13184         }
13185         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13186         this.value = v;
13187         
13188         var close = this.closeTriggerEl();
13189         
13190         if(close){
13191             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13192         }
13193         
13194         this.validate();
13195     },
13196     /**
13197      * @property {Object} the last set data for the element
13198      */
13199     
13200     lastData : false,
13201     /**
13202      * Sets the value of the field based on a object which is related to the record format for the store.
13203      * @param {Object} value the value to set as. or false on reset?
13204      */
13205     setFromData : function(o){
13206         
13207         if(this.multiple){
13208             this.addItem(o);
13209             return;
13210         }
13211             
13212         var dv = ''; // display value
13213         var vv = ''; // value value..
13214         this.lastData = o;
13215         if (this.displayField) {
13216             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13217         } else {
13218             // this is an error condition!!!
13219             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13220         }
13221         
13222         if(this.valueField){
13223             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13224         }
13225         
13226         var close = this.closeTriggerEl();
13227         
13228         if(close){
13229             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13230         }
13231         
13232         if(this.hiddenField){
13233             this.hiddenField.dom.value = vv;
13234             
13235             this.lastSelectionText = dv;
13236             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13237             this.value = vv;
13238             return;
13239         }
13240         // no hidden field.. - we store the value in 'value', but still display
13241         // display field!!!!
13242         this.lastSelectionText = dv;
13243         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13244         this.value = vv;
13245         
13246         
13247         
13248     },
13249     // private
13250     reset : function(){
13251         // overridden so that last data is reset..
13252         
13253         if(this.multiple){
13254             this.clearItem();
13255             return;
13256         }
13257         
13258         this.setValue(this.originalValue);
13259         //this.clearInvalid();
13260         this.lastData = false;
13261         if (this.view) {
13262             this.view.clearSelections();
13263         }
13264         
13265         this.validate();
13266     },
13267     // private
13268     findRecord : function(prop, value){
13269         var record;
13270         if(this.store.getCount() > 0){
13271             this.store.each(function(r){
13272                 if(r.data[prop] == value){
13273                     record = r;
13274                     return false;
13275                 }
13276                 return true;
13277             });
13278         }
13279         return record;
13280     },
13281     
13282     getName: function()
13283     {
13284         // returns hidden if it's set..
13285         if (!this.rendered) {return ''};
13286         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13287         
13288     },
13289     // private
13290     onViewMove : function(e, t){
13291         this.inKeyMode = false;
13292     },
13293
13294     // private
13295     onViewOver : function(e, t){
13296         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13297             return;
13298         }
13299         var item = this.view.findItemFromChild(t);
13300         
13301         if(item){
13302             var index = this.view.indexOf(item);
13303             this.select(index, false);
13304         }
13305     },
13306
13307     // private
13308     onViewClick : function(view, doFocus, el, e)
13309     {
13310         var index = this.view.getSelectedIndexes()[0];
13311         
13312         var r = this.store.getAt(index);
13313         
13314         if(this.tickable){
13315             
13316             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13317                 return;
13318             }
13319             
13320             var rm = false;
13321             var _this = this;
13322             
13323             Roo.each(this.tickItems, function(v,k){
13324                 
13325                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13326                     Roo.log(v);
13327                     _this.tickItems.splice(k, 1);
13328                     
13329                     if(typeof(e) == 'undefined' && view == false){
13330                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13331                     }
13332                     
13333                     rm = true;
13334                     return;
13335                 }
13336             });
13337             
13338             if(rm){
13339                 return;
13340             }
13341             
13342             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13343                 this.tickItems.push(r.data);
13344             }
13345             
13346             if(typeof(e) == 'undefined' && view == false){
13347                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13348             }
13349                     
13350             return;
13351         }
13352         
13353         if(r){
13354             this.onSelect(r, index);
13355         }
13356         if(doFocus !== false && !this.blockFocus){
13357             this.inputEl().focus();
13358         }
13359     },
13360
13361     // private
13362     restrictHeight : function(){
13363         //this.innerList.dom.style.height = '';
13364         //var inner = this.innerList.dom;
13365         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13366         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13367         //this.list.beginUpdate();
13368         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13369         this.list.alignTo(this.inputEl(), this.listAlign);
13370         this.list.alignTo(this.inputEl(), this.listAlign);
13371         //this.list.endUpdate();
13372     },
13373
13374     // private
13375     onEmptyResults : function(){
13376         
13377         if(this.tickable && this.editable){
13378             this.restrictHeight();
13379             return;
13380         }
13381         
13382         this.collapse();
13383     },
13384
13385     /**
13386      * Returns true if the dropdown list is expanded, else false.
13387      */
13388     isExpanded : function(){
13389         return this.list.isVisible();
13390     },
13391
13392     /**
13393      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13394      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13395      * @param {String} value The data value of the item to select
13396      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13397      * selected item if it is not currently in view (defaults to true)
13398      * @return {Boolean} True if the value matched an item in the list, else false
13399      */
13400     selectByValue : function(v, scrollIntoView){
13401         if(v !== undefined && v !== null){
13402             var r = this.findRecord(this.valueField || this.displayField, v);
13403             if(r){
13404                 this.select(this.store.indexOf(r), scrollIntoView);
13405                 return true;
13406             }
13407         }
13408         return false;
13409     },
13410
13411     /**
13412      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13413      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13414      * @param {Number} index The zero-based index of the list item to select
13415      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13416      * selected item if it is not currently in view (defaults to true)
13417      */
13418     select : function(index, scrollIntoView){
13419         this.selectedIndex = index;
13420         this.view.select(index);
13421         if(scrollIntoView !== false){
13422             var el = this.view.getNode(index);
13423             /*
13424              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13425              */
13426             if(el){
13427                 this.list.scrollChildIntoView(el, false);
13428             }
13429         }
13430     },
13431
13432     // private
13433     selectNext : function(){
13434         var ct = this.store.getCount();
13435         if(ct > 0){
13436             if(this.selectedIndex == -1){
13437                 this.select(0);
13438             }else if(this.selectedIndex < ct-1){
13439                 this.select(this.selectedIndex+1);
13440             }
13441         }
13442     },
13443
13444     // private
13445     selectPrev : function(){
13446         var ct = this.store.getCount();
13447         if(ct > 0){
13448             if(this.selectedIndex == -1){
13449                 this.select(0);
13450             }else if(this.selectedIndex != 0){
13451                 this.select(this.selectedIndex-1);
13452             }
13453         }
13454     },
13455
13456     // private
13457     onKeyUp : function(e){
13458         if(this.editable !== false && !e.isSpecialKey()){
13459             this.lastKey = e.getKey();
13460             this.dqTask.delay(this.queryDelay);
13461         }
13462     },
13463
13464     // private
13465     validateBlur : function(){
13466         return !this.list || !this.list.isVisible();   
13467     },
13468
13469     // private
13470     initQuery : function(){
13471         
13472         var v = this.getRawValue();
13473         
13474         if(this.tickable && this.editable){
13475             v = this.tickableInputEl().getValue();
13476         }
13477         
13478         this.doQuery(v);
13479     },
13480
13481     // private
13482     doForce : function(){
13483         if(this.inputEl().dom.value.length > 0){
13484             this.inputEl().dom.value =
13485                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13486              
13487         }
13488     },
13489
13490     /**
13491      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
13492      * query allowing the query action to be canceled if needed.
13493      * @param {String} query The SQL query to execute
13494      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13495      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
13496      * saved in the current store (defaults to false)
13497      */
13498     doQuery : function(q, forceAll){
13499         
13500         if(q === undefined || q === null){
13501             q = '';
13502         }
13503         var qe = {
13504             query: q,
13505             forceAll: forceAll,
13506             combo: this,
13507             cancel:false
13508         };
13509         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13510             return false;
13511         }
13512         q = qe.query;
13513         
13514         forceAll = qe.forceAll;
13515         if(forceAll === true || (q.length >= this.minChars)){
13516             
13517             this.hasQuery = true;
13518             
13519             if(this.lastQuery != q || this.alwaysQuery){
13520                 this.lastQuery = q;
13521                 if(this.mode == 'local'){
13522                     this.selectedIndex = -1;
13523                     if(forceAll){
13524                         this.store.clearFilter();
13525                     }else{
13526                         
13527                         if(this.specialFilter){
13528                             this.fireEvent('specialfilter', this);
13529                             this.onLoad();
13530                             return;
13531                         }
13532                         
13533                         this.store.filter(this.displayField, q);
13534                     }
13535                     
13536                     this.store.fireEvent("datachanged", this.store);
13537                     
13538                     this.onLoad();
13539                     
13540                     
13541                 }else{
13542                     
13543                     this.store.baseParams[this.queryParam] = q;
13544                     
13545                     var options = {params : this.getParams(q)};
13546                     
13547                     if(this.loadNext){
13548                         options.add = true;
13549                         options.params.start = this.page * this.pageSize;
13550                     }
13551                     
13552                     this.store.load(options);
13553                     
13554                     /*
13555                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
13556                      *  we should expand the list on onLoad
13557                      *  so command out it
13558                      */
13559 //                    this.expand();
13560                 }
13561             }else{
13562                 this.selectedIndex = -1;
13563                 this.onLoad();   
13564             }
13565         }
13566         
13567         this.loadNext = false;
13568     },
13569     
13570     // private
13571     getParams : function(q){
13572         var p = {};
13573         //p[this.queryParam] = q;
13574         
13575         if(this.pageSize){
13576             p.start = 0;
13577             p.limit = this.pageSize;
13578         }
13579         return p;
13580     },
13581
13582     /**
13583      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13584      */
13585     collapse : function(){
13586         if(!this.isExpanded()){
13587             return;
13588         }
13589         
13590         this.list.hide();
13591         
13592         if(this.tickable){
13593             this.hasFocus = false;
13594             this.okBtn.hide();
13595             this.cancelBtn.hide();
13596             this.trigger.show();
13597             
13598             if(this.editable){
13599                 this.tickableInputEl().dom.value = '';
13600                 this.tickableInputEl().blur();
13601             }
13602             
13603         }
13604         
13605         Roo.get(document).un('mousedown', this.collapseIf, this);
13606         Roo.get(document).un('mousewheel', this.collapseIf, this);
13607         if (!this.editable) {
13608             Roo.get(document).un('keydown', this.listKeyPress, this);
13609         }
13610         this.fireEvent('collapse', this);
13611         
13612         this.validate();
13613     },
13614
13615     // private
13616     collapseIf : function(e){
13617         var in_combo  = e.within(this.el);
13618         var in_list =  e.within(this.list);
13619         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13620         
13621         if (in_combo || in_list || is_list) {
13622             //e.stopPropagation();
13623             return;
13624         }
13625         
13626         if(this.tickable){
13627             this.onTickableFooterButtonClick(e, false, false);
13628         }
13629
13630         this.collapse();
13631         
13632     },
13633
13634     /**
13635      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13636      */
13637     expand : function(){
13638        
13639         if(this.isExpanded() || !this.hasFocus){
13640             return;
13641         }
13642         
13643         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13644         this.list.setWidth(lw);
13645         
13646         
13647          Roo.log('expand');
13648         
13649         this.list.show();
13650         
13651         this.restrictHeight();
13652         
13653         if(this.tickable){
13654             
13655             this.tickItems = Roo.apply([], this.item);
13656             
13657             this.okBtn.show();
13658             this.cancelBtn.show();
13659             this.trigger.hide();
13660             
13661             if(this.editable){
13662                 this.tickableInputEl().focus();
13663             }
13664             
13665         }
13666         
13667         Roo.get(document).on('mousedown', this.collapseIf, this);
13668         Roo.get(document).on('mousewheel', this.collapseIf, this);
13669         if (!this.editable) {
13670             Roo.get(document).on('keydown', this.listKeyPress, this);
13671         }
13672         
13673         this.fireEvent('expand', this);
13674     },
13675
13676     // private
13677     // Implements the default empty TriggerField.onTriggerClick function
13678     onTriggerClick : function(e)
13679     {
13680         Roo.log('trigger click');
13681         
13682         if(this.disabled || !this.triggerList){
13683             return;
13684         }
13685         
13686         this.page = 0;
13687         this.loadNext = false;
13688         
13689         if(this.isExpanded()){
13690             this.collapse();
13691             if (!this.blockFocus) {
13692                 this.inputEl().focus();
13693             }
13694             
13695         }else {
13696             this.hasFocus = true;
13697             if(this.triggerAction == 'all') {
13698                 this.doQuery(this.allQuery, true);
13699             } else {
13700                 this.doQuery(this.getRawValue());
13701             }
13702             if (!this.blockFocus) {
13703                 this.inputEl().focus();
13704             }
13705         }
13706     },
13707     
13708     onTickableTriggerClick : function(e)
13709     {
13710         if(this.disabled){
13711             return;
13712         }
13713         
13714         this.page = 0;
13715         this.loadNext = false;
13716         this.hasFocus = true;
13717         
13718         if(this.triggerAction == 'all') {
13719             this.doQuery(this.allQuery, true);
13720         } else {
13721             this.doQuery(this.getRawValue());
13722         }
13723     },
13724     
13725     onSearchFieldClick : function(e)
13726     {
13727         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13728             this.onTickableFooterButtonClick(e, false, false);
13729             return;
13730         }
13731         
13732         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13733             return;
13734         }
13735         
13736         this.page = 0;
13737         this.loadNext = false;
13738         this.hasFocus = true;
13739         
13740         if(this.triggerAction == 'all') {
13741             this.doQuery(this.allQuery, true);
13742         } else {
13743             this.doQuery(this.getRawValue());
13744         }
13745     },
13746     
13747     listKeyPress : function(e)
13748     {
13749         //Roo.log('listkeypress');
13750         // scroll to first matching element based on key pres..
13751         if (e.isSpecialKey()) {
13752             return false;
13753         }
13754         var k = String.fromCharCode(e.getKey()).toUpperCase();
13755         //Roo.log(k);
13756         var match  = false;
13757         var csel = this.view.getSelectedNodes();
13758         var cselitem = false;
13759         if (csel.length) {
13760             var ix = this.view.indexOf(csel[0]);
13761             cselitem  = this.store.getAt(ix);
13762             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13763                 cselitem = false;
13764             }
13765             
13766         }
13767         
13768         this.store.each(function(v) { 
13769             if (cselitem) {
13770                 // start at existing selection.
13771                 if (cselitem.id == v.id) {
13772                     cselitem = false;
13773                 }
13774                 return true;
13775             }
13776                 
13777             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13778                 match = this.store.indexOf(v);
13779                 return false;
13780             }
13781             return true;
13782         }, this);
13783         
13784         if (match === false) {
13785             return true; // no more action?
13786         }
13787         // scroll to?
13788         this.view.select(match);
13789         var sn = Roo.get(this.view.getSelectedNodes()[0]);
13790         sn.scrollIntoView(sn.dom.parentNode, false);
13791     },
13792     
13793     onViewScroll : function(e, t){
13794         
13795         if(this.view.el.getScroll().top == 0 ||this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
13796             return;
13797         }
13798         
13799         this.hasQuery = true;
13800         
13801         this.loading = this.list.select('.loading', true).first();
13802         
13803         if(this.loading === null){
13804             this.list.createChild({
13805                 tag: 'div',
13806                 cls: 'loading roo-select2-more-results roo-select2-active',
13807                 html: 'Loading more results...'
13808             });
13809             
13810             this.loading = this.list.select('.loading', true).first();
13811             
13812             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13813             
13814             this.loading.hide();
13815         }
13816         
13817         this.loading.show();
13818         
13819         var _combo = this;
13820         
13821         this.page++;
13822         this.loadNext = true;
13823         
13824         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13825         
13826         return;
13827     },
13828     
13829     addItem : function(o)
13830     {   
13831         var dv = ''; // display value
13832         
13833         if (this.displayField) {
13834             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13835         } else {
13836             // this is an error condition!!!
13837             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13838         }
13839         
13840         if(!dv.length){
13841             return;
13842         }
13843         
13844         var choice = this.choices.createChild({
13845             tag: 'li',
13846             cls: 'roo-select2-search-choice',
13847             cn: [
13848                 {
13849                     tag: 'div',
13850                     html: dv
13851                 },
13852                 {
13853                     tag: 'a',
13854                     href: '#',
13855                     cls: 'roo-select2-search-choice-close',
13856                     tabindex: '-1'
13857                 }
13858             ]
13859             
13860         }, this.searchField);
13861         
13862         var close = choice.select('a.roo-select2-search-choice-close', true).first();
13863         
13864         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13865         
13866         this.item.push(o);
13867         
13868         this.lastData = o;
13869         
13870         this.syncValue();
13871         
13872         this.inputEl().dom.value = '';
13873         
13874         this.validate();
13875     },
13876     
13877     onRemoveItem : function(e, _self, o)
13878     {
13879         e.preventDefault();
13880         
13881         this.lastItem = Roo.apply([], this.item);
13882         
13883         var index = this.item.indexOf(o.data) * 1;
13884         
13885         if( index < 0){
13886             Roo.log('not this item?!');
13887             return;
13888         }
13889         
13890         this.item.splice(index, 1);
13891         o.item.remove();
13892         
13893         this.syncValue();
13894         
13895         this.fireEvent('remove', this, e);
13896         
13897         this.validate();
13898         
13899     },
13900     
13901     syncValue : function()
13902     {
13903         if(!this.item.length){
13904             this.clearValue();
13905             return;
13906         }
13907             
13908         var value = [];
13909         var _this = this;
13910         Roo.each(this.item, function(i){
13911             if(_this.valueField){
13912                 value.push(i[_this.valueField]);
13913                 return;
13914             }
13915
13916             value.push(i);
13917         });
13918
13919         this.value = value.join(',');
13920
13921         if(this.hiddenField){
13922             this.hiddenField.dom.value = this.value;
13923         }
13924         
13925         this.store.fireEvent("datachanged", this.store);
13926         
13927         this.validate();
13928     },
13929     
13930     clearItem : function()
13931     {
13932         if(!this.multiple){
13933             return;
13934         }
13935         
13936         this.item = [];
13937         
13938         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
13939            c.remove();
13940         });
13941         
13942         this.syncValue();
13943         
13944         this.validate();
13945         
13946         if(this.tickable && !Roo.isTouch){
13947             this.view.refresh();
13948         }
13949     },
13950     
13951     inputEl: function ()
13952     {
13953         if(Roo.isIOS && this.useNativeIOS){
13954             return this.el.select('select.roo-ios-select', true).first();
13955         }
13956         
13957         if(Roo.isTouch && this.mobileTouchView){
13958             return this.el.select('input.form-control',true).first();
13959         }
13960         
13961         if(this.tickable){
13962             return this.searchField;
13963         }
13964         
13965         return this.el.select('input.form-control',true).first();
13966     },
13967     
13968     onTickableFooterButtonClick : function(e, btn, el)
13969     {
13970         e.preventDefault();
13971         
13972         this.lastItem = Roo.apply([], this.item);
13973         
13974         if(btn && btn.name == 'cancel'){
13975             this.tickItems = Roo.apply([], this.item);
13976             this.collapse();
13977             return;
13978         }
13979         
13980         this.clearItem();
13981         
13982         var _this = this;
13983         
13984         Roo.each(this.tickItems, function(o){
13985             _this.addItem(o);
13986         });
13987         
13988         this.collapse();
13989         
13990     },
13991     
13992     validate : function()
13993     {
13994         var v = this.getRawValue();
13995         
13996         if(this.multiple){
13997             v = this.getValue();
13998         }
13999         
14000         if(this.disabled || this.allowBlank || v.length){
14001             this.markValid();
14002             return true;
14003         }
14004         
14005         this.markInvalid();
14006         return false;
14007     },
14008     
14009     tickableInputEl : function()
14010     {
14011         if(!this.tickable || !this.editable){
14012             return this.inputEl();
14013         }
14014         
14015         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14016     },
14017     
14018     
14019     getAutoCreateTouchView : function()
14020     {
14021         var id = Roo.id();
14022         
14023         var cfg = {
14024             cls: 'form-group' //input-group
14025         };
14026         
14027         var input =  {
14028             tag: 'input',
14029             id : id,
14030             type : this.inputType,
14031             cls : 'form-control x-combo-noedit',
14032             autocomplete: 'new-password',
14033             placeholder : this.placeholder || '',
14034             readonly : true
14035         };
14036         
14037         if (this.name) {
14038             input.name = this.name;
14039         }
14040         
14041         if (this.size) {
14042             input.cls += ' input-' + this.size;
14043         }
14044         
14045         if (this.disabled) {
14046             input.disabled = true;
14047         }
14048         
14049         var inputblock = {
14050             cls : '',
14051             cn : [
14052                 input
14053             ]
14054         };
14055         
14056         if(this.before){
14057             inputblock.cls += ' input-group';
14058             
14059             inputblock.cn.unshift({
14060                 tag :'span',
14061                 cls : 'input-group-addon',
14062                 html : this.before
14063             });
14064         }
14065         
14066         if(this.removable && !this.multiple){
14067             inputblock.cls += ' roo-removable';
14068             
14069             inputblock.cn.push({
14070                 tag: 'button',
14071                 html : 'x',
14072                 cls : 'roo-combo-removable-btn close'
14073             });
14074         }
14075
14076         if(this.hasFeedback && !this.allowBlank){
14077             
14078             inputblock.cls += ' has-feedback';
14079             
14080             inputblock.cn.push({
14081                 tag: 'span',
14082                 cls: 'glyphicon form-control-feedback'
14083             });
14084             
14085         }
14086         
14087         if (this.after) {
14088             
14089             inputblock.cls += (this.before) ? '' : ' input-group';
14090             
14091             inputblock.cn.push({
14092                 tag :'span',
14093                 cls : 'input-group-addon',
14094                 html : this.after
14095             });
14096         }
14097
14098         var box = {
14099             tag: 'div',
14100             cn: [
14101                 {
14102                     tag: 'input',
14103                     type : 'hidden',
14104                     cls: 'form-hidden-field'
14105                 },
14106                 inputblock
14107             ]
14108             
14109         };
14110         
14111         if(this.multiple){
14112             box = {
14113                 tag: 'div',
14114                 cn: [
14115                     {
14116                         tag: 'input',
14117                         type : 'hidden',
14118                         cls: 'form-hidden-field'
14119                     },
14120                     {
14121                         tag: 'ul',
14122                         cls: 'roo-select2-choices',
14123                         cn:[
14124                             {
14125                                 tag: 'li',
14126                                 cls: 'roo-select2-search-field',
14127                                 cn: [
14128
14129                                     inputblock
14130                                 ]
14131                             }
14132                         ]
14133                     }
14134                 ]
14135             }
14136         };
14137         
14138         var combobox = {
14139             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14140             cn: [
14141                 box
14142             ]
14143         };
14144         
14145         if(!this.multiple && this.showToggleBtn){
14146             
14147             var caret = {
14148                         tag: 'span',
14149                         cls: 'caret'
14150             };
14151             
14152             if (this.caret != false) {
14153                 caret = {
14154                      tag: 'i',
14155                      cls: 'fa fa-' + this.caret
14156                 };
14157                 
14158             }
14159             
14160             combobox.cn.push({
14161                 tag :'span',
14162                 cls : 'input-group-addon btn dropdown-toggle',
14163                 cn : [
14164                     caret,
14165                     {
14166                         tag: 'span',
14167                         cls: 'combobox-clear',
14168                         cn  : [
14169                             {
14170                                 tag : 'i',
14171                                 cls: 'icon-remove'
14172                             }
14173                         ]
14174                     }
14175                 ]
14176
14177             })
14178         }
14179         
14180         if(this.multiple){
14181             combobox.cls += ' roo-select2-container-multi';
14182         }
14183         
14184         var align = this.labelAlign || this.parentLabelAlign();
14185         
14186         cfg.cn = combobox;
14187         
14188         if(this.fieldLabel.length && this.labelWidth){
14189             
14190             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
14191             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
14192             
14193             cfg.cn = [
14194                 {
14195                    tag : 'i',
14196                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14197                    tooltip : 'This field is required'
14198                 },
14199                 {
14200                     tag: 'label',
14201                     cls : 'control-label ' + lw,
14202                     html : this.fieldLabel
14203
14204                 },
14205                 {
14206                     cls : cw, 
14207                     cn: [
14208                         combobox
14209                     ]
14210                 }
14211             ];
14212             
14213             if(this.indicatorpos == 'right'){
14214                 cfg.cn = [
14215                     {
14216                         tag: 'label',
14217                         cls : 'control-label ' + lw,
14218                         html : this.fieldLabel
14219
14220                     },
14221                     {
14222                        tag : 'i',
14223                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14224                        tooltip : 'This field is required'
14225                     },
14226                     {
14227                         cls : cw, 
14228                         cn: [
14229                             combobox
14230                         ]
14231                     }
14232                 ];
14233             }
14234         }
14235         
14236         var settings = this;
14237         
14238         ['xs','sm','md','lg'].map(function(size){
14239             if (settings[size]) {
14240                 cfg.cls += ' col-' + size + '-' + settings[size];
14241             }
14242         });
14243         
14244         return cfg;
14245     },
14246     
14247     initTouchView : function()
14248     {
14249         this.renderTouchView();
14250         
14251         this.touchViewEl.on('scroll', function(){
14252             this.el.dom.scrollTop = 0;
14253         }, this);
14254         
14255         this.originalValue = this.getValue();
14256         
14257         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14258         
14259         this.inputEl().on("click", this.showTouchView, this);
14260         if (this.triggerEl) {
14261             this.triggerEl.on("click", this.showTouchView, this);
14262         }
14263         
14264         
14265         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14266         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14267         
14268         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14269         
14270         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14271         this.store.on('load', this.onTouchViewLoad, this);
14272         this.store.on('loadexception', this.onTouchViewLoadException, this);
14273         
14274         if(this.hiddenName){
14275             
14276             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14277             
14278             this.hiddenField.dom.value =
14279                 this.hiddenValue !== undefined ? this.hiddenValue :
14280                 this.value !== undefined ? this.value : '';
14281         
14282             this.el.dom.removeAttribute('name');
14283             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14284         }
14285         
14286         if(this.multiple){
14287             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14288             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14289         }
14290         
14291         if(this.removable && !this.multiple){
14292             var close = this.closeTriggerEl();
14293             if(close){
14294                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14295                 close.on('click', this.removeBtnClick, this, close);
14296             }
14297         }
14298         /*
14299          * fix the bug in Safari iOS8
14300          */
14301         this.inputEl().on("focus", function(e){
14302             document.activeElement.blur();
14303         }, this);
14304         
14305         return;
14306         
14307         
14308     },
14309     
14310     renderTouchView : function()
14311     {
14312         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14313         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14314         
14315         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14316         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14317         
14318         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14319         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14320         this.touchViewBodyEl.setStyle('overflow', 'auto');
14321         
14322         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14323         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14324         
14325         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14326         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14327         
14328     },
14329     
14330     showTouchView : function()
14331     {
14332         if(this.disabled){
14333             return;
14334         }
14335         
14336         this.touchViewHeaderEl.hide();
14337
14338         if(this.modalTitle.length){
14339             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14340             this.touchViewHeaderEl.show();
14341         }
14342
14343         this.touchViewEl.show();
14344
14345         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14346         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14347                 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14348
14349         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14350
14351         if(this.modalTitle.length){
14352             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14353         }
14354         
14355         this.touchViewBodyEl.setHeight(bodyHeight);
14356
14357         if(this.animate){
14358             var _this = this;
14359             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14360         }else{
14361             this.touchViewEl.addClass('in');
14362         }
14363
14364         this.doTouchViewQuery();
14365         
14366     },
14367     
14368     hideTouchView : function()
14369     {
14370         this.touchViewEl.removeClass('in');
14371
14372         if(this.animate){
14373             var _this = this;
14374             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14375         }else{
14376             this.touchViewEl.setStyle('display', 'none');
14377         }
14378         
14379     },
14380     
14381     setTouchViewValue : function()
14382     {
14383         if(this.multiple){
14384             this.clearItem();
14385         
14386             var _this = this;
14387
14388             Roo.each(this.tickItems, function(o){
14389                 this.addItem(o);
14390             }, this);
14391         }
14392         
14393         this.hideTouchView();
14394     },
14395     
14396     doTouchViewQuery : function()
14397     {
14398         var qe = {
14399             query: '',
14400             forceAll: true,
14401             combo: this,
14402             cancel:false
14403         };
14404         
14405         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14406             return false;
14407         }
14408         
14409         if(!this.alwaysQuery || this.mode == 'local'){
14410             this.onTouchViewLoad();
14411             return;
14412         }
14413         
14414         this.store.load();
14415     },
14416     
14417     onTouchViewBeforeLoad : function(combo,opts)
14418     {
14419         return;
14420     },
14421
14422     // private
14423     onTouchViewLoad : function()
14424     {
14425         if(this.store.getCount() < 1){
14426             this.onTouchViewEmptyResults();
14427             return;
14428         }
14429         
14430         this.clearTouchView();
14431         
14432         var rawValue = this.getRawValue();
14433         
14434         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14435         
14436         this.tickItems = [];
14437         
14438         this.store.data.each(function(d, rowIndex){
14439             var row = this.touchViewListGroup.createChild(template);
14440             
14441             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14442                 row.addClass(d.data.cls);
14443             }
14444             
14445             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14446                 var cfg = {
14447                     data : d.data,
14448                     html : d.data[this.displayField]
14449                 };
14450                 
14451                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14452                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14453                 }
14454             }
14455             row.removeClass('selected');
14456             if(!this.multiple && this.valueField &&
14457                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14458             {
14459                 // radio buttons..
14460                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14461                 row.addClass('selected');
14462             }
14463             
14464             if(this.multiple && this.valueField &&
14465                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14466             {
14467                 
14468                 // checkboxes...
14469                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14470                 this.tickItems.push(d.data);
14471             }
14472             
14473             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14474             
14475         }, this);
14476         
14477         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14478         
14479         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14480
14481         if(this.modalTitle.length){
14482             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14483         }
14484
14485         var listHeight = this.touchViewListGroup.getHeight();
14486         
14487         var _this = this;
14488         
14489         if(firstChecked && listHeight > bodyHeight){
14490             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14491         }
14492         
14493     },
14494     
14495     onTouchViewLoadException : function()
14496     {
14497         this.hideTouchView();
14498     },
14499     
14500     onTouchViewEmptyResults : function()
14501     {
14502         this.clearTouchView();
14503         
14504         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14505         
14506         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14507         
14508     },
14509     
14510     clearTouchView : function()
14511     {
14512         this.touchViewListGroup.dom.innerHTML = '';
14513     },
14514     
14515     onTouchViewClick : function(e, el, o)
14516     {
14517         e.preventDefault();
14518         
14519         var row = o.row;
14520         var rowIndex = o.rowIndex;
14521         
14522         var r = this.store.getAt(rowIndex);
14523         
14524         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14525             
14526             if(!this.multiple){
14527                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14528                     c.dom.removeAttribute('checked');
14529                 }, this);
14530
14531                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14532
14533                 this.setFromData(r.data);
14534
14535                 var close = this.closeTriggerEl();
14536
14537                 if(close){
14538                     close.show();
14539                 }
14540
14541                 this.hideTouchView();
14542
14543                 this.fireEvent('select', this, r, rowIndex);
14544
14545                 return;
14546             }
14547
14548             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14549                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14550                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14551                 return;
14552             }
14553
14554             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14555             this.addItem(r.data);
14556             this.tickItems.push(r.data);
14557         }
14558     },
14559     
14560     getAutoCreateNativeIOS : function()
14561     {
14562         var cfg = {
14563             cls: 'form-group' //input-group,
14564         };
14565         
14566         var combobox =  {
14567             tag: 'select',
14568             cls : 'roo-ios-select'
14569         };
14570         
14571         if (this.name) {
14572             combobox.name = this.name;
14573         }
14574         
14575         if (this.disabled) {
14576             combobox.disabled = true;
14577         }
14578         
14579         var settings = this;
14580         
14581         ['xs','sm','md','lg'].map(function(size){
14582             if (settings[size]) {
14583                 cfg.cls += ' col-' + size + '-' + settings[size];
14584             }
14585         });
14586         
14587         cfg.cn = combobox;
14588         
14589         return cfg;
14590         
14591     },
14592     
14593     initIOSView : function()
14594     {
14595         this.store.on('load', this.onIOSViewLoad, this);
14596         
14597         return;
14598     },
14599     
14600     onIOSViewLoad : function()
14601     {
14602         if(this.store.getCount() < 1){
14603             return;
14604         }
14605         
14606         this.clearIOSView();
14607         
14608         if(this.allowBlank) {
14609             
14610             var default_text = '-- SELECT --';
14611             
14612             var opt = this.inputEl().createChild({
14613                 tag: 'option',
14614                 value : 0,
14615                 html : default_text
14616             });
14617             
14618             var o = {};
14619             o[this.valueField] = 0;
14620             o[this.displayField] = default_text;
14621             
14622             this.ios_options.push({
14623                 data : o,
14624                 el : opt
14625             });
14626             
14627         }
14628         
14629         this.store.data.each(function(d, rowIndex){
14630             
14631             var html = '';
14632             
14633             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14634                 html = d.data[this.displayField];
14635             }
14636             
14637             var value = '';
14638             
14639             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
14640                 value = d.data[this.valueField];
14641             }
14642             
14643             var option = {
14644                 tag: 'option',
14645                 value : value,
14646                 html : html
14647             };
14648             
14649             if(this.value == d.data[this.valueField]){
14650                 option['selected'] = true;
14651             }
14652             
14653             var opt = this.inputEl().createChild(option);
14654             
14655             this.ios_options.push({
14656                 data : d.data,
14657                 el : opt
14658             });
14659             
14660         }, this);
14661         
14662         this.inputEl().on('change', function(){
14663            this.fireEvent('select', this);
14664         }, this);
14665         
14666     },
14667     
14668     clearIOSView: function()
14669     {
14670         this.inputEl().dom.innerHTML = '';
14671         
14672         this.ios_options = [];
14673     },
14674     
14675     setIOSValue: function(v)
14676     {
14677         this.value = v;
14678         
14679         if(!this.ios_options){
14680             return;
14681         }
14682         
14683         Roo.each(this.ios_options, function(opts){
14684            
14685            opts.el.dom.removeAttribute('selected');
14686            
14687            if(opts.data[this.valueField] != v){
14688                return;
14689            }
14690            
14691            opts.el.dom.setAttribute('selected', true);
14692            
14693         }, this);
14694     }
14695
14696     /** 
14697     * @cfg {Boolean} grow 
14698     * @hide 
14699     */
14700     /** 
14701     * @cfg {Number} growMin 
14702     * @hide 
14703     */
14704     /** 
14705     * @cfg {Number} growMax 
14706     * @hide 
14707     */
14708     /**
14709      * @hide
14710      * @method autoSize
14711      */
14712 });
14713
14714 Roo.apply(Roo.bootstrap.ComboBox,  {
14715     
14716     header : {
14717         tag: 'div',
14718         cls: 'modal-header',
14719         cn: [
14720             {
14721                 tag: 'h4',
14722                 cls: 'modal-title'
14723             }
14724         ]
14725     },
14726     
14727     body : {
14728         tag: 'div',
14729         cls: 'modal-body',
14730         cn: [
14731             {
14732                 tag: 'ul',
14733                 cls: 'list-group'
14734             }
14735         ]
14736     },
14737     
14738     listItemRadio : {
14739         tag: 'li',
14740         cls: 'list-group-item',
14741         cn: [
14742             {
14743                 tag: 'span',
14744                 cls: 'roo-combobox-list-group-item-value'
14745             },
14746             {
14747                 tag: 'div',
14748                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14749                 cn: [
14750                     {
14751                         tag: 'input',
14752                         type: 'radio'
14753                     },
14754                     {
14755                         tag: 'label'
14756                     }
14757                 ]
14758             }
14759         ]
14760     },
14761     
14762     listItemCheckbox : {
14763         tag: 'li',
14764         cls: 'list-group-item',
14765         cn: [
14766             {
14767                 tag: 'span',
14768                 cls: 'roo-combobox-list-group-item-value'
14769             },
14770             {
14771                 tag: 'div',
14772                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14773                 cn: [
14774                     {
14775                         tag: 'input',
14776                         type: 'checkbox'
14777                     },
14778                     {
14779                         tag: 'label'
14780                     }
14781                 ]
14782             }
14783         ]
14784     },
14785     
14786     emptyResult : {
14787         tag: 'div',
14788         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14789     },
14790     
14791     footer : {
14792         tag: 'div',
14793         cls: 'modal-footer',
14794         cn: [
14795             {
14796                 tag: 'div',
14797                 cls: 'row',
14798                 cn: [
14799                     {
14800                         tag: 'div',
14801                         cls: 'col-xs-6 text-left',
14802                         cn: {
14803                             tag: 'button',
14804                             cls: 'btn btn-danger roo-touch-view-cancel',
14805                             html: 'Cancel'
14806                         }
14807                     },
14808                     {
14809                         tag: 'div',
14810                         cls: 'col-xs-6 text-right',
14811                         cn: {
14812                             tag: 'button',
14813                             cls: 'btn btn-success roo-touch-view-ok',
14814                             html: 'OK'
14815                         }
14816                     }
14817                 ]
14818             }
14819         ]
14820         
14821     }
14822 });
14823
14824 Roo.apply(Roo.bootstrap.ComboBox,  {
14825     
14826     touchViewTemplate : {
14827         tag: 'div',
14828         cls: 'modal fade roo-combobox-touch-view',
14829         cn: [
14830             {
14831                 tag: 'div',
14832                 cls: 'modal-dialog',
14833                 style : 'position:fixed', // we have to fix position....
14834                 cn: [
14835                     {
14836                         tag: 'div',
14837                         cls: 'modal-content',
14838                         cn: [
14839                             Roo.bootstrap.ComboBox.header,
14840                             Roo.bootstrap.ComboBox.body,
14841                             Roo.bootstrap.ComboBox.footer
14842                         ]
14843                     }
14844                 ]
14845             }
14846         ]
14847     }
14848 });/*
14849  * Based on:
14850  * Ext JS Library 1.1.1
14851  * Copyright(c) 2006-2007, Ext JS, LLC.
14852  *
14853  * Originally Released Under LGPL - original licence link has changed is not relivant.
14854  *
14855  * Fork - LGPL
14856  * <script type="text/javascript">
14857  */
14858
14859 /**
14860  * @class Roo.View
14861  * @extends Roo.util.Observable
14862  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
14863  * This class also supports single and multi selection modes. <br>
14864  * Create a data model bound view:
14865  <pre><code>
14866  var store = new Roo.data.Store(...);
14867
14868  var view = new Roo.View({
14869     el : "my-element",
14870     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
14871  
14872     singleSelect: true,
14873     selectedClass: "ydataview-selected",
14874     store: store
14875  });
14876
14877  // listen for node click?
14878  view.on("click", function(vw, index, node, e){
14879  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14880  });
14881
14882  // load XML data
14883  dataModel.load("foobar.xml");
14884  </code></pre>
14885  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14886  * <br><br>
14887  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14888  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14889  * 
14890  * Note: old style constructor is still suported (container, template, config)
14891  * 
14892  * @constructor
14893  * Create a new View
14894  * @param {Object} config The config object
14895  * 
14896  */
14897 Roo.View = function(config, depreciated_tpl, depreciated_config){
14898     
14899     this.parent = false;
14900     
14901     if (typeof(depreciated_tpl) == 'undefined') {
14902         // new way.. - universal constructor.
14903         Roo.apply(this, config);
14904         this.el  = Roo.get(this.el);
14905     } else {
14906         // old format..
14907         this.el  = Roo.get(config);
14908         this.tpl = depreciated_tpl;
14909         Roo.apply(this, depreciated_config);
14910     }
14911     this.wrapEl  = this.el.wrap().wrap();
14912     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14913     
14914     
14915     if(typeof(this.tpl) == "string"){
14916         this.tpl = new Roo.Template(this.tpl);
14917     } else {
14918         // support xtype ctors..
14919         this.tpl = new Roo.factory(this.tpl, Roo);
14920     }
14921     
14922     
14923     this.tpl.compile();
14924     
14925     /** @private */
14926     this.addEvents({
14927         /**
14928          * @event beforeclick
14929          * Fires before a click is processed. Returns false to cancel the default action.
14930          * @param {Roo.View} this
14931          * @param {Number} index The index of the target node
14932          * @param {HTMLElement} node The target node
14933          * @param {Roo.EventObject} e The raw event object
14934          */
14935             "beforeclick" : true,
14936         /**
14937          * @event click
14938          * Fires when a template node is clicked.
14939          * @param {Roo.View} this
14940          * @param {Number} index The index of the target node
14941          * @param {HTMLElement} node The target node
14942          * @param {Roo.EventObject} e The raw event object
14943          */
14944             "click" : true,
14945         /**
14946          * @event dblclick
14947          * Fires when a template node is double clicked.
14948          * @param {Roo.View} this
14949          * @param {Number} index The index of the target node
14950          * @param {HTMLElement} node The target node
14951          * @param {Roo.EventObject} e The raw event object
14952          */
14953             "dblclick" : true,
14954         /**
14955          * @event contextmenu
14956          * Fires when a template node is right clicked.
14957          * @param {Roo.View} this
14958          * @param {Number} index The index of the target node
14959          * @param {HTMLElement} node The target node
14960          * @param {Roo.EventObject} e The raw event object
14961          */
14962             "contextmenu" : true,
14963         /**
14964          * @event selectionchange
14965          * Fires when the selected nodes change.
14966          * @param {Roo.View} this
14967          * @param {Array} selections Array of the selected nodes
14968          */
14969             "selectionchange" : true,
14970     
14971         /**
14972          * @event beforeselect
14973          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14974          * @param {Roo.View} this
14975          * @param {HTMLElement} node The node to be selected
14976          * @param {Array} selections Array of currently selected nodes
14977          */
14978             "beforeselect" : true,
14979         /**
14980          * @event preparedata
14981          * Fires on every row to render, to allow you to change the data.
14982          * @param {Roo.View} this
14983          * @param {Object} data to be rendered (change this)
14984          */
14985           "preparedata" : true
14986           
14987           
14988         });
14989
14990
14991
14992     this.el.on({
14993         "click": this.onClick,
14994         "dblclick": this.onDblClick,
14995         "contextmenu": this.onContextMenu,
14996         scope:this
14997     });
14998
14999     this.selections = [];
15000     this.nodes = [];
15001     this.cmp = new Roo.CompositeElementLite([]);
15002     if(this.store){
15003         this.store = Roo.factory(this.store, Roo.data);
15004         this.setStore(this.store, true);
15005     }
15006     
15007     if ( this.footer && this.footer.xtype) {
15008            
15009          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15010         
15011         this.footer.dataSource = this.store;
15012         this.footer.container = fctr;
15013         this.footer = Roo.factory(this.footer, Roo);
15014         fctr.insertFirst(this.el);
15015         
15016         // this is a bit insane - as the paging toolbar seems to detach the el..
15017 //        dom.parentNode.parentNode.parentNode
15018          // they get detached?
15019     }
15020     
15021     
15022     Roo.View.superclass.constructor.call(this);
15023     
15024     
15025 };
15026
15027 Roo.extend(Roo.View, Roo.util.Observable, {
15028     
15029      /**
15030      * @cfg {Roo.data.Store} store Data store to load data from.
15031      */
15032     store : false,
15033     
15034     /**
15035      * @cfg {String|Roo.Element} el The container element.
15036      */
15037     el : '',
15038     
15039     /**
15040      * @cfg {String|Roo.Template} tpl The template used by this View 
15041      */
15042     tpl : false,
15043     /**
15044      * @cfg {String} dataName the named area of the template to use as the data area
15045      *                          Works with domtemplates roo-name="name"
15046      */
15047     dataName: false,
15048     /**
15049      * @cfg {String} selectedClass The css class to add to selected nodes
15050      */
15051     selectedClass : "x-view-selected",
15052      /**
15053      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15054      */
15055     emptyText : "",
15056     
15057     /**
15058      * @cfg {String} text to display on mask (default Loading)
15059      */
15060     mask : false,
15061     /**
15062      * @cfg {Boolean} multiSelect Allow multiple selection
15063      */
15064     multiSelect : false,
15065     /**
15066      * @cfg {Boolean} singleSelect Allow single selection
15067      */
15068     singleSelect:  false,
15069     
15070     /**
15071      * @cfg {Boolean} toggleSelect - selecting 
15072      */
15073     toggleSelect : false,
15074     
15075     /**
15076      * @cfg {Boolean} tickable - selecting 
15077      */
15078     tickable : false,
15079     
15080     /**
15081      * Returns the element this view is bound to.
15082      * @return {Roo.Element}
15083      */
15084     getEl : function(){
15085         return this.wrapEl;
15086     },
15087     
15088     
15089
15090     /**
15091      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15092      */
15093     refresh : function(){
15094         //Roo.log('refresh');
15095         var t = this.tpl;
15096         
15097         // if we are using something like 'domtemplate', then
15098         // the what gets used is:
15099         // t.applySubtemplate(NAME, data, wrapping data..)
15100         // the outer template then get' applied with
15101         //     the store 'extra data'
15102         // and the body get's added to the
15103         //      roo-name="data" node?
15104         //      <span class='roo-tpl-{name}'></span> ?????
15105         
15106         
15107         
15108         this.clearSelections();
15109         this.el.update("");
15110         var html = [];
15111         var records = this.store.getRange();
15112         if(records.length < 1) {
15113             
15114             // is this valid??  = should it render a template??
15115             
15116             this.el.update(this.emptyText);
15117             return;
15118         }
15119         var el = this.el;
15120         if (this.dataName) {
15121             this.el.update(t.apply(this.store.meta)); //????
15122             el = this.el.child('.roo-tpl-' + this.dataName);
15123         }
15124         
15125         for(var i = 0, len = records.length; i < len; i++){
15126             var data = this.prepareData(records[i].data, i, records[i]);
15127             this.fireEvent("preparedata", this, data, i, records[i]);
15128             
15129             var d = Roo.apply({}, data);
15130             
15131             if(this.tickable){
15132                 Roo.apply(d, {'roo-id' : Roo.id()});
15133                 
15134                 var _this = this;
15135             
15136                 Roo.each(this.parent.item, function(item){
15137                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15138                         return;
15139                     }
15140                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15141                 });
15142             }
15143             
15144             html[html.length] = Roo.util.Format.trim(
15145                 this.dataName ?
15146                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15147                     t.apply(d)
15148             );
15149         }
15150         
15151         
15152         
15153         el.update(html.join(""));
15154         this.nodes = el.dom.childNodes;
15155         this.updateIndexes(0);
15156     },
15157     
15158
15159     /**
15160      * Function to override to reformat the data that is sent to
15161      * the template for each node.
15162      * DEPRICATED - use the preparedata event handler.
15163      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15164      * a JSON object for an UpdateManager bound view).
15165      */
15166     prepareData : function(data, index, record)
15167     {
15168         this.fireEvent("preparedata", this, data, index, record);
15169         return data;
15170     },
15171
15172     onUpdate : function(ds, record){
15173         // Roo.log('on update');   
15174         this.clearSelections();
15175         var index = this.store.indexOf(record);
15176         var n = this.nodes[index];
15177         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15178         n.parentNode.removeChild(n);
15179         this.updateIndexes(index, index);
15180     },
15181
15182     
15183     
15184 // --------- FIXME     
15185     onAdd : function(ds, records, index)
15186     {
15187         //Roo.log(['on Add', ds, records, index] );        
15188         this.clearSelections();
15189         if(this.nodes.length == 0){
15190             this.refresh();
15191             return;
15192         }
15193         var n = this.nodes[index];
15194         for(var i = 0, len = records.length; i < len; i++){
15195             var d = this.prepareData(records[i].data, i, records[i]);
15196             if(n){
15197                 this.tpl.insertBefore(n, d);
15198             }else{
15199                 
15200                 this.tpl.append(this.el, d);
15201             }
15202         }
15203         this.updateIndexes(index);
15204     },
15205
15206     onRemove : function(ds, record, index){
15207        // Roo.log('onRemove');
15208         this.clearSelections();
15209         var el = this.dataName  ?
15210             this.el.child('.roo-tpl-' + this.dataName) :
15211             this.el; 
15212         
15213         el.dom.removeChild(this.nodes[index]);
15214         this.updateIndexes(index);
15215     },
15216
15217     /**
15218      * Refresh an individual node.
15219      * @param {Number} index
15220      */
15221     refreshNode : function(index){
15222         this.onUpdate(this.store, this.store.getAt(index));
15223     },
15224
15225     updateIndexes : function(startIndex, endIndex){
15226         var ns = this.nodes;
15227         startIndex = startIndex || 0;
15228         endIndex = endIndex || ns.length - 1;
15229         for(var i = startIndex; i <= endIndex; i++){
15230             ns[i].nodeIndex = i;
15231         }
15232     },
15233
15234     /**
15235      * Changes the data store this view uses and refresh the view.
15236      * @param {Store} store
15237      */
15238     setStore : function(store, initial){
15239         if(!initial && this.store){
15240             this.store.un("datachanged", this.refresh);
15241             this.store.un("add", this.onAdd);
15242             this.store.un("remove", this.onRemove);
15243             this.store.un("update", this.onUpdate);
15244             this.store.un("clear", this.refresh);
15245             this.store.un("beforeload", this.onBeforeLoad);
15246             this.store.un("load", this.onLoad);
15247             this.store.un("loadexception", this.onLoad);
15248         }
15249         if(store){
15250           
15251             store.on("datachanged", this.refresh, this);
15252             store.on("add", this.onAdd, this);
15253             store.on("remove", this.onRemove, this);
15254             store.on("update", this.onUpdate, this);
15255             store.on("clear", this.refresh, this);
15256             store.on("beforeload", this.onBeforeLoad, this);
15257             store.on("load", this.onLoad, this);
15258             store.on("loadexception", this.onLoad, this);
15259         }
15260         
15261         if(store){
15262             this.refresh();
15263         }
15264     },
15265     /**
15266      * onbeforeLoad - masks the loading area.
15267      *
15268      */
15269     onBeforeLoad : function(store,opts)
15270     {
15271          //Roo.log('onBeforeLoad');   
15272         if (!opts.add) {
15273             this.el.update("");
15274         }
15275         this.el.mask(this.mask ? this.mask : "Loading" ); 
15276     },
15277     onLoad : function ()
15278     {
15279         this.el.unmask();
15280     },
15281     
15282
15283     /**
15284      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15285      * @param {HTMLElement} node
15286      * @return {HTMLElement} The template node
15287      */
15288     findItemFromChild : function(node){
15289         var el = this.dataName  ?
15290             this.el.child('.roo-tpl-' + this.dataName,true) :
15291             this.el.dom; 
15292         
15293         if(!node || node.parentNode == el){
15294                     return node;
15295             }
15296             var p = node.parentNode;
15297             while(p && p != el){
15298             if(p.parentNode == el){
15299                 return p;
15300             }
15301             p = p.parentNode;
15302         }
15303             return null;
15304     },
15305
15306     /** @ignore */
15307     onClick : function(e){
15308         var item = this.findItemFromChild(e.getTarget());
15309         if(item){
15310             var index = this.indexOf(item);
15311             if(this.onItemClick(item, index, e) !== false){
15312                 this.fireEvent("click", this, index, item, e);
15313             }
15314         }else{
15315             this.clearSelections();
15316         }
15317     },
15318
15319     /** @ignore */
15320     onContextMenu : function(e){
15321         var item = this.findItemFromChild(e.getTarget());
15322         if(item){
15323             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15324         }
15325     },
15326
15327     /** @ignore */
15328     onDblClick : function(e){
15329         var item = this.findItemFromChild(e.getTarget());
15330         if(item){
15331             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15332         }
15333     },
15334
15335     onItemClick : function(item, index, e)
15336     {
15337         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15338             return false;
15339         }
15340         if (this.toggleSelect) {
15341             var m = this.isSelected(item) ? 'unselect' : 'select';
15342             //Roo.log(m);
15343             var _t = this;
15344             _t[m](item, true, false);
15345             return true;
15346         }
15347         if(this.multiSelect || this.singleSelect){
15348             if(this.multiSelect && e.shiftKey && this.lastSelection){
15349                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15350             }else{
15351                 this.select(item, this.multiSelect && e.ctrlKey);
15352                 this.lastSelection = item;
15353             }
15354             
15355             if(!this.tickable){
15356                 e.preventDefault();
15357             }
15358             
15359         }
15360         return true;
15361     },
15362
15363     /**
15364      * Get the number of selected nodes.
15365      * @return {Number}
15366      */
15367     getSelectionCount : function(){
15368         return this.selections.length;
15369     },
15370
15371     /**
15372      * Get the currently selected nodes.
15373      * @return {Array} An array of HTMLElements
15374      */
15375     getSelectedNodes : function(){
15376         return this.selections;
15377     },
15378
15379     /**
15380      * Get the indexes of the selected nodes.
15381      * @return {Array}
15382      */
15383     getSelectedIndexes : function(){
15384         var indexes = [], s = this.selections;
15385         for(var i = 0, len = s.length; i < len; i++){
15386             indexes.push(s[i].nodeIndex);
15387         }
15388         return indexes;
15389     },
15390
15391     /**
15392      * Clear all selections
15393      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15394      */
15395     clearSelections : function(suppressEvent){
15396         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15397             this.cmp.elements = this.selections;
15398             this.cmp.removeClass(this.selectedClass);
15399             this.selections = [];
15400             if(!suppressEvent){
15401                 this.fireEvent("selectionchange", this, this.selections);
15402             }
15403         }
15404     },
15405
15406     /**
15407      * Returns true if the passed node is selected
15408      * @param {HTMLElement/Number} node The node or node index
15409      * @return {Boolean}
15410      */
15411     isSelected : function(node){
15412         var s = this.selections;
15413         if(s.length < 1){
15414             return false;
15415         }
15416         node = this.getNode(node);
15417         return s.indexOf(node) !== -1;
15418     },
15419
15420     /**
15421      * Selects nodes.
15422      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
15423      * @param {Boolean} keepExisting (optional) true to keep existing selections
15424      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15425      */
15426     select : function(nodeInfo, keepExisting, suppressEvent){
15427         if(nodeInfo instanceof Array){
15428             if(!keepExisting){
15429                 this.clearSelections(true);
15430             }
15431             for(var i = 0, len = nodeInfo.length; i < len; i++){
15432                 this.select(nodeInfo[i], true, true);
15433             }
15434             return;
15435         } 
15436         var node = this.getNode(nodeInfo);
15437         if(!node || this.isSelected(node)){
15438             return; // already selected.
15439         }
15440         if(!keepExisting){
15441             this.clearSelections(true);
15442         }
15443         
15444         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15445             Roo.fly(node).addClass(this.selectedClass);
15446             this.selections.push(node);
15447             if(!suppressEvent){
15448                 this.fireEvent("selectionchange", this, this.selections);
15449             }
15450         }
15451         
15452         
15453     },
15454       /**
15455      * Unselects nodes.
15456      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
15457      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15458      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15459      */
15460     unselect : function(nodeInfo, keepExisting, suppressEvent)
15461     {
15462         if(nodeInfo instanceof Array){
15463             Roo.each(this.selections, function(s) {
15464                 this.unselect(s, nodeInfo);
15465             }, this);
15466             return;
15467         }
15468         var node = this.getNode(nodeInfo);
15469         if(!node || !this.isSelected(node)){
15470             //Roo.log("not selected");
15471             return; // not selected.
15472         }
15473         // fireevent???
15474         var ns = [];
15475         Roo.each(this.selections, function(s) {
15476             if (s == node ) {
15477                 Roo.fly(node).removeClass(this.selectedClass);
15478
15479                 return;
15480             }
15481             ns.push(s);
15482         },this);
15483         
15484         this.selections= ns;
15485         this.fireEvent("selectionchange", this, this.selections);
15486     },
15487
15488     /**
15489      * Gets a template node.
15490      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15491      * @return {HTMLElement} The node or null if it wasn't found
15492      */
15493     getNode : function(nodeInfo){
15494         if(typeof nodeInfo == "string"){
15495             return document.getElementById(nodeInfo);
15496         }else if(typeof nodeInfo == "number"){
15497             return this.nodes[nodeInfo];
15498         }
15499         return nodeInfo;
15500     },
15501
15502     /**
15503      * Gets a range template nodes.
15504      * @param {Number} startIndex
15505      * @param {Number} endIndex
15506      * @return {Array} An array of nodes
15507      */
15508     getNodes : function(start, end){
15509         var ns = this.nodes;
15510         start = start || 0;
15511         end = typeof end == "undefined" ? ns.length - 1 : end;
15512         var nodes = [];
15513         if(start <= end){
15514             for(var i = start; i <= end; i++){
15515                 nodes.push(ns[i]);
15516             }
15517         } else{
15518             for(var i = start; i >= end; i--){
15519                 nodes.push(ns[i]);
15520             }
15521         }
15522         return nodes;
15523     },
15524
15525     /**
15526      * Finds the index of the passed node
15527      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15528      * @return {Number} The index of the node or -1
15529      */
15530     indexOf : function(node){
15531         node = this.getNode(node);
15532         if(typeof node.nodeIndex == "number"){
15533             return node.nodeIndex;
15534         }
15535         var ns = this.nodes;
15536         for(var i = 0, len = ns.length; i < len; i++){
15537             if(ns[i] == node){
15538                 return i;
15539             }
15540         }
15541         return -1;
15542     }
15543 });
15544 /*
15545  * - LGPL
15546  *
15547  * based on jquery fullcalendar
15548  * 
15549  */
15550
15551 Roo.bootstrap = Roo.bootstrap || {};
15552 /**
15553  * @class Roo.bootstrap.Calendar
15554  * @extends Roo.bootstrap.Component
15555  * Bootstrap Calendar class
15556  * @cfg {Boolean} loadMask (true|false) default false
15557  * @cfg {Object} header generate the user specific header of the calendar, default false
15558
15559  * @constructor
15560  * Create a new Container
15561  * @param {Object} config The config object
15562  */
15563
15564
15565
15566 Roo.bootstrap.Calendar = function(config){
15567     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15568      this.addEvents({
15569         /**
15570              * @event select
15571              * Fires when a date is selected
15572              * @param {DatePicker} this
15573              * @param {Date} date The selected date
15574              */
15575         'select': true,
15576         /**
15577              * @event monthchange
15578              * Fires when the displayed month changes 
15579              * @param {DatePicker} this
15580              * @param {Date} date The selected month
15581              */
15582         'monthchange': true,
15583         /**
15584              * @event evententer
15585              * Fires when mouse over an event
15586              * @param {Calendar} this
15587              * @param {event} Event
15588              */
15589         'evententer': true,
15590         /**
15591              * @event eventleave
15592              * Fires when the mouse leaves an
15593              * @param {Calendar} this
15594              * @param {event}
15595              */
15596         'eventleave': true,
15597         /**
15598              * @event eventclick
15599              * Fires when the mouse click an
15600              * @param {Calendar} this
15601              * @param {event}
15602              */
15603         'eventclick': true
15604         
15605     });
15606
15607 };
15608
15609 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
15610     
15611      /**
15612      * @cfg {Number} startDay
15613      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15614      */
15615     startDay : 0,
15616     
15617     loadMask : false,
15618     
15619     header : false,
15620       
15621     getAutoCreate : function(){
15622         
15623         
15624         var fc_button = function(name, corner, style, content ) {
15625             return Roo.apply({},{
15626                 tag : 'span',
15627                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
15628                          (corner.length ?
15629                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15630                             ''
15631                         ),
15632                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15633                 unselectable: 'on'
15634             });
15635         };
15636         
15637         var header = {};
15638         
15639         if(!this.header){
15640             header = {
15641                 tag : 'table',
15642                 cls : 'fc-header',
15643                 style : 'width:100%',
15644                 cn : [
15645                     {
15646                         tag: 'tr',
15647                         cn : [
15648                             {
15649                                 tag : 'td',
15650                                 cls : 'fc-header-left',
15651                                 cn : [
15652                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
15653                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
15654                                     { tag: 'span', cls: 'fc-header-space' },
15655                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
15656
15657
15658                                 ]
15659                             },
15660
15661                             {
15662                                 tag : 'td',
15663                                 cls : 'fc-header-center',
15664                                 cn : [
15665                                     {
15666                                         tag: 'span',
15667                                         cls: 'fc-header-title',
15668                                         cn : {
15669                                             tag: 'H2',
15670                                             html : 'month / year'
15671                                         }
15672                                     }
15673
15674                                 ]
15675                             },
15676                             {
15677                                 tag : 'td',
15678                                 cls : 'fc-header-right',
15679                                 cn : [
15680                               /*      fc_button('month', 'left', '', 'month' ),
15681                                     fc_button('week', '', '', 'week' ),
15682                                     fc_button('day', 'right', '', 'day' )
15683                                 */    
15684
15685                                 ]
15686                             }
15687
15688                         ]
15689                     }
15690                 ]
15691             };
15692         }
15693         
15694         header = this.header;
15695         
15696        
15697         var cal_heads = function() {
15698             var ret = [];
15699             // fixme - handle this.
15700             
15701             for (var i =0; i < Date.dayNames.length; i++) {
15702                 var d = Date.dayNames[i];
15703                 ret.push({
15704                     tag: 'th',
15705                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15706                     html : d.substring(0,3)
15707                 });
15708                 
15709             }
15710             ret[0].cls += ' fc-first';
15711             ret[6].cls += ' fc-last';
15712             return ret;
15713         };
15714         var cal_cell = function(n) {
15715             return  {
15716                 tag: 'td',
15717                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15718                 cn : [
15719                     {
15720                         cn : [
15721                             {
15722                                 cls: 'fc-day-number',
15723                                 html: 'D'
15724                             },
15725                             {
15726                                 cls: 'fc-day-content',
15727                              
15728                                 cn : [
15729                                      {
15730                                         style: 'position: relative;' // height: 17px;
15731                                     }
15732                                 ]
15733                             }
15734                             
15735                             
15736                         ]
15737                     }
15738                 ]
15739                 
15740             }
15741         };
15742         var cal_rows = function() {
15743             
15744             var ret = [];
15745             for (var r = 0; r < 6; r++) {
15746                 var row= {
15747                     tag : 'tr',
15748                     cls : 'fc-week',
15749                     cn : []
15750                 };
15751                 
15752                 for (var i =0; i < Date.dayNames.length; i++) {
15753                     var d = Date.dayNames[i];
15754                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15755
15756                 }
15757                 row.cn[0].cls+=' fc-first';
15758                 row.cn[0].cn[0].style = 'min-height:90px';
15759                 row.cn[6].cls+=' fc-last';
15760                 ret.push(row);
15761                 
15762             }
15763             ret[0].cls += ' fc-first';
15764             ret[4].cls += ' fc-prev-last';
15765             ret[5].cls += ' fc-last';
15766             return ret;
15767             
15768         };
15769         
15770         var cal_table = {
15771             tag: 'table',
15772             cls: 'fc-border-separate',
15773             style : 'width:100%',
15774             cellspacing  : 0,
15775             cn : [
15776                 { 
15777                     tag: 'thead',
15778                     cn : [
15779                         { 
15780                             tag: 'tr',
15781                             cls : 'fc-first fc-last',
15782                             cn : cal_heads()
15783                         }
15784                     ]
15785                 },
15786                 { 
15787                     tag: 'tbody',
15788                     cn : cal_rows()
15789                 }
15790                   
15791             ]
15792         };
15793          
15794          var cfg = {
15795             cls : 'fc fc-ltr',
15796             cn : [
15797                 header,
15798                 {
15799                     cls : 'fc-content',
15800                     style : "position: relative;",
15801                     cn : [
15802                         {
15803                             cls : 'fc-view fc-view-month fc-grid',
15804                             style : 'position: relative',
15805                             unselectable : 'on',
15806                             cn : [
15807                                 {
15808                                     cls : 'fc-event-container',
15809                                     style : 'position:absolute;z-index:8;top:0;left:0;'
15810                                 },
15811                                 cal_table
15812                             ]
15813                         }
15814                     ]
15815     
15816                 }
15817            ] 
15818             
15819         };
15820         
15821          
15822         
15823         return cfg;
15824     },
15825     
15826     
15827     initEvents : function()
15828     {
15829         if(!this.store){
15830             throw "can not find store for calendar";
15831         }
15832         
15833         var mark = {
15834             tag: "div",
15835             cls:"x-dlg-mask",
15836             style: "text-align:center",
15837             cn: [
15838                 {
15839                     tag: "div",
15840                     style: "background-color:white;width:50%;margin:250 auto",
15841                     cn: [
15842                         {
15843                             tag: "img",
15844                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
15845                         },
15846                         {
15847                             tag: "span",
15848                             html: "Loading"
15849                         }
15850                         
15851                     ]
15852                 }
15853             ]
15854         };
15855         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15856         
15857         var size = this.el.select('.fc-content', true).first().getSize();
15858         this.maskEl.setSize(size.width, size.height);
15859         this.maskEl.enableDisplayMode("block");
15860         if(!this.loadMask){
15861             this.maskEl.hide();
15862         }
15863         
15864         this.store = Roo.factory(this.store, Roo.data);
15865         this.store.on('load', this.onLoad, this);
15866         this.store.on('beforeload', this.onBeforeLoad, this);
15867         
15868         this.resize();
15869         
15870         this.cells = this.el.select('.fc-day',true);
15871         //Roo.log(this.cells);
15872         this.textNodes = this.el.query('.fc-day-number');
15873         this.cells.addClassOnOver('fc-state-hover');
15874         
15875         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15876         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15877         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15878         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15879         
15880         this.on('monthchange', this.onMonthChange, this);
15881         
15882         this.update(new Date().clearTime());
15883     },
15884     
15885     resize : function() {
15886         var sz  = this.el.getSize();
15887         
15888         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15889         this.el.select('.fc-day-content div',true).setHeight(34);
15890     },
15891     
15892     
15893     // private
15894     showPrevMonth : function(e){
15895         this.update(this.activeDate.add("mo", -1));
15896     },
15897     showToday : function(e){
15898         this.update(new Date().clearTime());
15899     },
15900     // private
15901     showNextMonth : function(e){
15902         this.update(this.activeDate.add("mo", 1));
15903     },
15904
15905     // private
15906     showPrevYear : function(){
15907         this.update(this.activeDate.add("y", -1));
15908     },
15909
15910     // private
15911     showNextYear : function(){
15912         this.update(this.activeDate.add("y", 1));
15913     },
15914
15915     
15916    // private
15917     update : function(date)
15918     {
15919         var vd = this.activeDate;
15920         this.activeDate = date;
15921 //        if(vd && this.el){
15922 //            var t = date.getTime();
15923 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15924 //                Roo.log('using add remove');
15925 //                
15926 //                this.fireEvent('monthchange', this, date);
15927 //                
15928 //                this.cells.removeClass("fc-state-highlight");
15929 //                this.cells.each(function(c){
15930 //                   if(c.dateValue == t){
15931 //                       c.addClass("fc-state-highlight");
15932 //                       setTimeout(function(){
15933 //                            try{c.dom.firstChild.focus();}catch(e){}
15934 //                       }, 50);
15935 //                       return false;
15936 //                   }
15937 //                   return true;
15938 //                });
15939 //                return;
15940 //            }
15941 //        }
15942         
15943         var days = date.getDaysInMonth();
15944         
15945         var firstOfMonth = date.getFirstDateOfMonth();
15946         var startingPos = firstOfMonth.getDay()-this.startDay;
15947         
15948         if(startingPos < this.startDay){
15949             startingPos += 7;
15950         }
15951         
15952         var pm = date.add(Date.MONTH, -1);
15953         var prevStart = pm.getDaysInMonth()-startingPos;
15954 //        
15955         this.cells = this.el.select('.fc-day',true);
15956         this.textNodes = this.el.query('.fc-day-number');
15957         this.cells.addClassOnOver('fc-state-hover');
15958         
15959         var cells = this.cells.elements;
15960         var textEls = this.textNodes;
15961         
15962         Roo.each(cells, function(cell){
15963             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15964         });
15965         
15966         days += startingPos;
15967
15968         // convert everything to numbers so it's fast
15969         var day = 86400000;
15970         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15971         //Roo.log(d);
15972         //Roo.log(pm);
15973         //Roo.log(prevStart);
15974         
15975         var today = new Date().clearTime().getTime();
15976         var sel = date.clearTime().getTime();
15977         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15978         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15979         var ddMatch = this.disabledDatesRE;
15980         var ddText = this.disabledDatesText;
15981         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15982         var ddaysText = this.disabledDaysText;
15983         var format = this.format;
15984         
15985         var setCellClass = function(cal, cell){
15986             cell.row = 0;
15987             cell.events = [];
15988             cell.more = [];
15989             //Roo.log('set Cell Class');
15990             cell.title = "";
15991             var t = d.getTime();
15992             
15993             //Roo.log(d);
15994             
15995             cell.dateValue = t;
15996             if(t == today){
15997                 cell.className += " fc-today";
15998                 cell.className += " fc-state-highlight";
15999                 cell.title = cal.todayText;
16000             }
16001             if(t == sel){
16002                 // disable highlight in other month..
16003                 //cell.className += " fc-state-highlight";
16004                 
16005             }
16006             // disabling
16007             if(t < min) {
16008                 cell.className = " fc-state-disabled";
16009                 cell.title = cal.minText;
16010                 return;
16011             }
16012             if(t > max) {
16013                 cell.className = " fc-state-disabled";
16014                 cell.title = cal.maxText;
16015                 return;
16016             }
16017             if(ddays){
16018                 if(ddays.indexOf(d.getDay()) != -1){
16019                     cell.title = ddaysText;
16020                     cell.className = " fc-state-disabled";
16021                 }
16022             }
16023             if(ddMatch && format){
16024                 var fvalue = d.dateFormat(format);
16025                 if(ddMatch.test(fvalue)){
16026                     cell.title = ddText.replace("%0", fvalue);
16027                     cell.className = " fc-state-disabled";
16028                 }
16029             }
16030             
16031             if (!cell.initialClassName) {
16032                 cell.initialClassName = cell.dom.className;
16033             }
16034             
16035             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16036         };
16037
16038         var i = 0;
16039         
16040         for(; i < startingPos; i++) {
16041             textEls[i].innerHTML = (++prevStart);
16042             d.setDate(d.getDate()+1);
16043             
16044             cells[i].className = "fc-past fc-other-month";
16045             setCellClass(this, cells[i]);
16046         }
16047         
16048         var intDay = 0;
16049         
16050         for(; i < days; i++){
16051             intDay = i - startingPos + 1;
16052             textEls[i].innerHTML = (intDay);
16053             d.setDate(d.getDate()+1);
16054             
16055             cells[i].className = ''; // "x-date-active";
16056             setCellClass(this, cells[i]);
16057         }
16058         var extraDays = 0;
16059         
16060         for(; i < 42; i++) {
16061             textEls[i].innerHTML = (++extraDays);
16062             d.setDate(d.getDate()+1);
16063             
16064             cells[i].className = "fc-future fc-other-month";
16065             setCellClass(this, cells[i]);
16066         }
16067         
16068         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16069         
16070         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16071         
16072         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16073         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16074         
16075         if(totalRows != 6){
16076             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16077             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16078         }
16079         
16080         this.fireEvent('monthchange', this, date);
16081         
16082         
16083         /*
16084         if(!this.internalRender){
16085             var main = this.el.dom.firstChild;
16086             var w = main.offsetWidth;
16087             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16088             Roo.fly(main).setWidth(w);
16089             this.internalRender = true;
16090             // opera does not respect the auto grow header center column
16091             // then, after it gets a width opera refuses to recalculate
16092             // without a second pass
16093             if(Roo.isOpera && !this.secondPass){
16094                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16095                 this.secondPass = true;
16096                 this.update.defer(10, this, [date]);
16097             }
16098         }
16099         */
16100         
16101     },
16102     
16103     findCell : function(dt) {
16104         dt = dt.clearTime().getTime();
16105         var ret = false;
16106         this.cells.each(function(c){
16107             //Roo.log("check " +c.dateValue + '?=' + dt);
16108             if(c.dateValue == dt){
16109                 ret = c;
16110                 return false;
16111             }
16112             return true;
16113         });
16114         
16115         return ret;
16116     },
16117     
16118     findCells : function(ev) {
16119         var s = ev.start.clone().clearTime().getTime();
16120        // Roo.log(s);
16121         var e= ev.end.clone().clearTime().getTime();
16122        // Roo.log(e);
16123         var ret = [];
16124         this.cells.each(function(c){
16125              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16126             
16127             if(c.dateValue > e){
16128                 return ;
16129             }
16130             if(c.dateValue < s){
16131                 return ;
16132             }
16133             ret.push(c);
16134         });
16135         
16136         return ret;    
16137     },
16138     
16139 //    findBestRow: function(cells)
16140 //    {
16141 //        var ret = 0;
16142 //        
16143 //        for (var i =0 ; i < cells.length;i++) {
16144 //            ret  = Math.max(cells[i].rows || 0,ret);
16145 //        }
16146 //        return ret;
16147 //        
16148 //    },
16149     
16150     
16151     addItem : function(ev)
16152     {
16153         // look for vertical location slot in
16154         var cells = this.findCells(ev);
16155         
16156 //        ev.row = this.findBestRow(cells);
16157         
16158         // work out the location.
16159         
16160         var crow = false;
16161         var rows = [];
16162         for(var i =0; i < cells.length; i++) {
16163             
16164             cells[i].row = cells[0].row;
16165             
16166             if(i == 0){
16167                 cells[i].row = cells[i].row + 1;
16168             }
16169             
16170             if (!crow) {
16171                 crow = {
16172                     start : cells[i],
16173                     end :  cells[i]
16174                 };
16175                 continue;
16176             }
16177             if (crow.start.getY() == cells[i].getY()) {
16178                 // on same row.
16179                 crow.end = cells[i];
16180                 continue;
16181             }
16182             // different row.
16183             rows.push(crow);
16184             crow = {
16185                 start: cells[i],
16186                 end : cells[i]
16187             };
16188             
16189         }
16190         
16191         rows.push(crow);
16192         ev.els = [];
16193         ev.rows = rows;
16194         ev.cells = cells;
16195         
16196         cells[0].events.push(ev);
16197         
16198         this.calevents.push(ev);
16199     },
16200     
16201     clearEvents: function() {
16202         
16203         if(!this.calevents){
16204             return;
16205         }
16206         
16207         Roo.each(this.cells.elements, function(c){
16208             c.row = 0;
16209             c.events = [];
16210             c.more = [];
16211         });
16212         
16213         Roo.each(this.calevents, function(e) {
16214             Roo.each(e.els, function(el) {
16215                 el.un('mouseenter' ,this.onEventEnter, this);
16216                 el.un('mouseleave' ,this.onEventLeave, this);
16217                 el.remove();
16218             },this);
16219         },this);
16220         
16221         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16222             e.remove();
16223         });
16224         
16225     },
16226     
16227     renderEvents: function()
16228     {   
16229         var _this = this;
16230         
16231         this.cells.each(function(c) {
16232             
16233             if(c.row < 5){
16234                 return;
16235             }
16236             
16237             var ev = c.events;
16238             
16239             var r = 4;
16240             if(c.row != c.events.length){
16241                 r = 4 - (4 - (c.row - c.events.length));
16242             }
16243             
16244             c.events = ev.slice(0, r);
16245             c.more = ev.slice(r);
16246             
16247             if(c.more.length && c.more.length == 1){
16248                 c.events.push(c.more.pop());
16249             }
16250             
16251             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16252             
16253         });
16254             
16255         this.cells.each(function(c) {
16256             
16257             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16258             
16259             
16260             for (var e = 0; e < c.events.length; e++){
16261                 var ev = c.events[e];
16262                 var rows = ev.rows;
16263                 
16264                 for(var i = 0; i < rows.length; i++) {
16265                 
16266                     // how many rows should it span..
16267
16268                     var  cfg = {
16269                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16270                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16271
16272                         unselectable : "on",
16273                         cn : [
16274                             {
16275                                 cls: 'fc-event-inner',
16276                                 cn : [
16277     //                                {
16278     //                                  tag:'span',
16279     //                                  cls: 'fc-event-time',
16280     //                                  html : cells.length > 1 ? '' : ev.time
16281     //                                },
16282                                     {
16283                                       tag:'span',
16284                                       cls: 'fc-event-title',
16285                                       html : String.format('{0}', ev.title)
16286                                     }
16287
16288
16289                                 ]
16290                             },
16291                             {
16292                                 cls: 'ui-resizable-handle ui-resizable-e',
16293                                 html : '&nbsp;&nbsp;&nbsp'
16294                             }
16295
16296                         ]
16297                     };
16298
16299                     if (i == 0) {
16300                         cfg.cls += ' fc-event-start';
16301                     }
16302                     if ((i+1) == rows.length) {
16303                         cfg.cls += ' fc-event-end';
16304                     }
16305
16306                     var ctr = _this.el.select('.fc-event-container',true).first();
16307                     var cg = ctr.createChild(cfg);
16308
16309                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16310                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16311
16312                     var r = (c.more.length) ? 1 : 0;
16313                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16314                     cg.setWidth(ebox.right - sbox.x -2);
16315
16316                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16317                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16318                     cg.on('click', _this.onEventClick, _this, ev);
16319
16320                     ev.els.push(cg);
16321                     
16322                 }
16323                 
16324             }
16325             
16326             
16327             if(c.more.length){
16328                 var  cfg = {
16329                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16330                     style : 'position: absolute',
16331                     unselectable : "on",
16332                     cn : [
16333                         {
16334                             cls: 'fc-event-inner',
16335                             cn : [
16336                                 {
16337                                   tag:'span',
16338                                   cls: 'fc-event-title',
16339                                   html : 'More'
16340                                 }
16341
16342
16343                             ]
16344                         },
16345                         {
16346                             cls: 'ui-resizable-handle ui-resizable-e',
16347                             html : '&nbsp;&nbsp;&nbsp'
16348                         }
16349
16350                     ]
16351                 };
16352
16353                 var ctr = _this.el.select('.fc-event-container',true).first();
16354                 var cg = ctr.createChild(cfg);
16355
16356                 var sbox = c.select('.fc-day-content',true).first().getBox();
16357                 var ebox = c.select('.fc-day-content',true).first().getBox();
16358                 //Roo.log(cg);
16359                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
16360                 cg.setWidth(ebox.right - sbox.x -2);
16361
16362                 cg.on('click', _this.onMoreEventClick, _this, c.more);
16363                 
16364             }
16365             
16366         });
16367         
16368         
16369         
16370     },
16371     
16372     onEventEnter: function (e, el,event,d) {
16373         this.fireEvent('evententer', this, el, event);
16374     },
16375     
16376     onEventLeave: function (e, el,event,d) {
16377         this.fireEvent('eventleave', this, el, event);
16378     },
16379     
16380     onEventClick: function (e, el,event,d) {
16381         this.fireEvent('eventclick', this, el, event);
16382     },
16383     
16384     onMonthChange: function () {
16385         this.store.load();
16386     },
16387     
16388     onMoreEventClick: function(e, el, more)
16389     {
16390         var _this = this;
16391         
16392         this.calpopover.placement = 'right';
16393         this.calpopover.setTitle('More');
16394         
16395         this.calpopover.setContent('');
16396         
16397         var ctr = this.calpopover.el.select('.popover-content', true).first();
16398         
16399         Roo.each(more, function(m){
16400             var cfg = {
16401                 cls : 'fc-event-hori fc-event-draggable',
16402                 html : m.title
16403             };
16404             var cg = ctr.createChild(cfg);
16405             
16406             cg.on('click', _this.onEventClick, _this, m);
16407         });
16408         
16409         this.calpopover.show(el);
16410         
16411         
16412     },
16413     
16414     onLoad: function () 
16415     {   
16416         this.calevents = [];
16417         var cal = this;
16418         
16419         if(this.store.getCount() > 0){
16420             this.store.data.each(function(d){
16421                cal.addItem({
16422                     id : d.data.id,
16423                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16424                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16425                     time : d.data.start_time,
16426                     title : d.data.title,
16427                     description : d.data.description,
16428                     venue : d.data.venue
16429                 });
16430             });
16431         }
16432         
16433         this.renderEvents();
16434         
16435         if(this.calevents.length && this.loadMask){
16436             this.maskEl.hide();
16437         }
16438     },
16439     
16440     onBeforeLoad: function()
16441     {
16442         this.clearEvents();
16443         if(this.loadMask){
16444             this.maskEl.show();
16445         }
16446     }
16447 });
16448
16449  
16450  /*
16451  * - LGPL
16452  *
16453  * element
16454  * 
16455  */
16456
16457 /**
16458  * @class Roo.bootstrap.Popover
16459  * @extends Roo.bootstrap.Component
16460  * Bootstrap Popover class
16461  * @cfg {String} html contents of the popover   (or false to use children..)
16462  * @cfg {String} title of popover (or false to hide)
16463  * @cfg {String} placement how it is placed
16464  * @cfg {String} trigger click || hover (or false to trigger manually)
16465  * @cfg {String} over what (parent or false to trigger manually.)
16466  * @cfg {Number} delay - delay before showing
16467  
16468  * @constructor
16469  * Create a new Popover
16470  * @param {Object} config The config object
16471  */
16472
16473 Roo.bootstrap.Popover = function(config){
16474     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16475     
16476     this.addEvents({
16477         // raw events
16478          /**
16479          * @event show
16480          * After the popover show
16481          * 
16482          * @param {Roo.bootstrap.Popover} this
16483          */
16484         "show" : true,
16485         /**
16486          * @event hide
16487          * After the popover hide
16488          * 
16489          * @param {Roo.bootstrap.Popover} this
16490          */
16491         "hide" : true
16492     });
16493 };
16494
16495 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
16496     
16497     title: 'Fill in a title',
16498     html: false,
16499     
16500     placement : 'right',
16501     trigger : 'hover', // hover
16502     
16503     delay : 0,
16504     
16505     over: 'parent',
16506     
16507     can_build_overlaid : false,
16508     
16509     getChildContainer : function()
16510     {
16511         return this.el.select('.popover-content',true).first();
16512     },
16513     
16514     getAutoCreate : function(){
16515          
16516         var cfg = {
16517            cls : 'popover roo-dynamic',
16518            style: 'display:block',
16519            cn : [
16520                 {
16521                     cls : 'arrow'
16522                 },
16523                 {
16524                     cls : 'popover-inner',
16525                     cn : [
16526                         {
16527                             tag: 'h3',
16528                             cls: 'popover-title',
16529                             html : this.title
16530                         },
16531                         {
16532                             cls : 'popover-content',
16533                             html : this.html
16534                         }
16535                     ]
16536                     
16537                 }
16538            ]
16539         };
16540         
16541         return cfg;
16542     },
16543     setTitle: function(str)
16544     {
16545         this.title = str;
16546         this.el.select('.popover-title',true).first().dom.innerHTML = str;
16547     },
16548     setContent: function(str)
16549     {
16550         this.html = str;
16551         this.el.select('.popover-content',true).first().dom.innerHTML = str;
16552     },
16553     // as it get's added to the bottom of the page.
16554     onRender : function(ct, position)
16555     {
16556         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16557         if(!this.el){
16558             var cfg = Roo.apply({},  this.getAutoCreate());
16559             cfg.id = Roo.id();
16560             
16561             if (this.cls) {
16562                 cfg.cls += ' ' + this.cls;
16563             }
16564             if (this.style) {
16565                 cfg.style = this.style;
16566             }
16567             //Roo.log("adding to ");
16568             this.el = Roo.get(document.body).createChild(cfg, position);
16569 //            Roo.log(this.el);
16570         }
16571         this.initEvents();
16572     },
16573     
16574     initEvents : function()
16575     {
16576         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16577         this.el.enableDisplayMode('block');
16578         this.el.hide();
16579         if (this.over === false) {
16580             return; 
16581         }
16582         if (this.triggers === false) {
16583             return;
16584         }
16585         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16586         var triggers = this.trigger ? this.trigger.split(' ') : [];
16587         Roo.each(triggers, function(trigger) {
16588         
16589             if (trigger == 'click') {
16590                 on_el.on('click', this.toggle, this);
16591             } else if (trigger != 'manual') {
16592                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
16593                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16594       
16595                 on_el.on(eventIn  ,this.enter, this);
16596                 on_el.on(eventOut, this.leave, this);
16597             }
16598         }, this);
16599         
16600     },
16601     
16602     
16603     // private
16604     timeout : null,
16605     hoverState : null,
16606     
16607     toggle : function () {
16608         this.hoverState == 'in' ? this.leave() : this.enter();
16609     },
16610     
16611     enter : function () {
16612         
16613         clearTimeout(this.timeout);
16614     
16615         this.hoverState = 'in';
16616     
16617         if (!this.delay || !this.delay.show) {
16618             this.show();
16619             return;
16620         }
16621         var _t = this;
16622         this.timeout = setTimeout(function () {
16623             if (_t.hoverState == 'in') {
16624                 _t.show();
16625             }
16626         }, this.delay.show)
16627     },
16628     
16629     leave : function() {
16630         clearTimeout(this.timeout);
16631     
16632         this.hoverState = 'out';
16633     
16634         if (!this.delay || !this.delay.hide) {
16635             this.hide();
16636             return;
16637         }
16638         var _t = this;
16639         this.timeout = setTimeout(function () {
16640             if (_t.hoverState == 'out') {
16641                 _t.hide();
16642             }
16643         }, this.delay.hide)
16644     },
16645     
16646     show : function (on_el)
16647     {
16648         if (!on_el) {
16649             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16650         }
16651         
16652         // set content.
16653         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16654         if (this.html !== false) {
16655             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16656         }
16657         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16658         if (!this.title.length) {
16659             this.el.select('.popover-title',true).hide();
16660         }
16661         
16662         var placement = typeof this.placement == 'function' ?
16663             this.placement.call(this, this.el, on_el) :
16664             this.placement;
16665             
16666         var autoToken = /\s?auto?\s?/i;
16667         var autoPlace = autoToken.test(placement);
16668         if (autoPlace) {
16669             placement = placement.replace(autoToken, '') || 'top';
16670         }
16671         
16672         //this.el.detach()
16673         //this.el.setXY([0,0]);
16674         this.el.show();
16675         this.el.dom.style.display='block';
16676         this.el.addClass(placement);
16677         
16678         //this.el.appendTo(on_el);
16679         
16680         var p = this.getPosition();
16681         var box = this.el.getBox();
16682         
16683         if (autoPlace) {
16684             // fixme..
16685         }
16686         var align = Roo.bootstrap.Popover.alignment[placement];
16687         this.el.alignTo(on_el, align[0],align[1]);
16688         //var arrow = this.el.select('.arrow',true).first();
16689         //arrow.set(align[2], 
16690         
16691         this.el.addClass('in');
16692         
16693         
16694         if (this.el.hasClass('fade')) {
16695             // fade it?
16696         }
16697         
16698         this.hoverState = 'in';
16699         
16700         this.fireEvent('show', this);
16701         
16702     },
16703     hide : function()
16704     {
16705         this.el.setXY([0,0]);
16706         this.el.removeClass('in');
16707         this.el.hide();
16708         this.hoverState = null;
16709         
16710         this.fireEvent('hide', this);
16711     }
16712     
16713 });
16714
16715 Roo.bootstrap.Popover.alignment = {
16716     'left' : ['r-l', [-10,0], 'right'],
16717     'right' : ['l-r', [10,0], 'left'],
16718     'bottom' : ['t-b', [0,10], 'top'],
16719     'top' : [ 'b-t', [0,-10], 'bottom']
16720 };
16721
16722  /*
16723  * - LGPL
16724  *
16725  * Progress
16726  * 
16727  */
16728
16729 /**
16730  * @class Roo.bootstrap.Progress
16731  * @extends Roo.bootstrap.Component
16732  * Bootstrap Progress class
16733  * @cfg {Boolean} striped striped of the progress bar
16734  * @cfg {Boolean} active animated of the progress bar
16735  * 
16736  * 
16737  * @constructor
16738  * Create a new Progress
16739  * @param {Object} config The config object
16740  */
16741
16742 Roo.bootstrap.Progress = function(config){
16743     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16744 };
16745
16746 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
16747     
16748     striped : false,
16749     active: false,
16750     
16751     getAutoCreate : function(){
16752         var cfg = {
16753             tag: 'div',
16754             cls: 'progress'
16755         };
16756         
16757         
16758         if(this.striped){
16759             cfg.cls += ' progress-striped';
16760         }
16761       
16762         if(this.active){
16763             cfg.cls += ' active';
16764         }
16765         
16766         
16767         return cfg;
16768     }
16769    
16770 });
16771
16772  
16773
16774  /*
16775  * - LGPL
16776  *
16777  * ProgressBar
16778  * 
16779  */
16780
16781 /**
16782  * @class Roo.bootstrap.ProgressBar
16783  * @extends Roo.bootstrap.Component
16784  * Bootstrap ProgressBar class
16785  * @cfg {Number} aria_valuenow aria-value now
16786  * @cfg {Number} aria_valuemin aria-value min
16787  * @cfg {Number} aria_valuemax aria-value max
16788  * @cfg {String} label label for the progress bar
16789  * @cfg {String} panel (success | info | warning | danger )
16790  * @cfg {String} role role of the progress bar
16791  * @cfg {String} sr_only text
16792  * 
16793  * 
16794  * @constructor
16795  * Create a new ProgressBar
16796  * @param {Object} config The config object
16797  */
16798
16799 Roo.bootstrap.ProgressBar = function(config){
16800     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16801 };
16802
16803 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
16804     
16805     aria_valuenow : 0,
16806     aria_valuemin : 0,
16807     aria_valuemax : 100,
16808     label : false,
16809     panel : false,
16810     role : false,
16811     sr_only: false,
16812     
16813     getAutoCreate : function()
16814     {
16815         
16816         var cfg = {
16817             tag: 'div',
16818             cls: 'progress-bar',
16819             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16820         };
16821         
16822         if(this.sr_only){
16823             cfg.cn = {
16824                 tag: 'span',
16825                 cls: 'sr-only',
16826                 html: this.sr_only
16827             }
16828         }
16829         
16830         if(this.role){
16831             cfg.role = this.role;
16832         }
16833         
16834         if(this.aria_valuenow){
16835             cfg['aria-valuenow'] = this.aria_valuenow;
16836         }
16837         
16838         if(this.aria_valuemin){
16839             cfg['aria-valuemin'] = this.aria_valuemin;
16840         }
16841         
16842         if(this.aria_valuemax){
16843             cfg['aria-valuemax'] = this.aria_valuemax;
16844         }
16845         
16846         if(this.label && !this.sr_only){
16847             cfg.html = this.label;
16848         }
16849         
16850         if(this.panel){
16851             cfg.cls += ' progress-bar-' + this.panel;
16852         }
16853         
16854         return cfg;
16855     },
16856     
16857     update : function(aria_valuenow)
16858     {
16859         this.aria_valuenow = aria_valuenow;
16860         
16861         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16862     }
16863    
16864 });
16865
16866  
16867
16868  /*
16869  * - LGPL
16870  *
16871  * column
16872  * 
16873  */
16874
16875 /**
16876  * @class Roo.bootstrap.TabGroup
16877  * @extends Roo.bootstrap.Column
16878  * Bootstrap Column class
16879  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16880  * @cfg {Boolean} carousel true to make the group behave like a carousel
16881  * @cfg {Boolean} bullets show bullets for the panels
16882  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16883  * @cfg {Number} timer auto slide timer .. default 0 millisecond
16884  * @cfg {Boolean} showarrow (true|false) show arrow default true
16885  * 
16886  * @constructor
16887  * Create a new TabGroup
16888  * @param {Object} config The config object
16889  */
16890
16891 Roo.bootstrap.TabGroup = function(config){
16892     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16893     if (!this.navId) {
16894         this.navId = Roo.id();
16895     }
16896     this.tabs = [];
16897     Roo.bootstrap.TabGroup.register(this);
16898     
16899 };
16900
16901 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
16902     
16903     carousel : false,
16904     transition : false,
16905     bullets : 0,
16906     timer : 0,
16907     autoslide : false,
16908     slideFn : false,
16909     slideOnTouch : false,
16910     showarrow : true,
16911     
16912     getAutoCreate : function()
16913     {
16914         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16915         
16916         cfg.cls += ' tab-content';
16917         
16918         if (this.carousel) {
16919             cfg.cls += ' carousel slide';
16920             
16921             cfg.cn = [{
16922                cls : 'carousel-inner',
16923                cn : []
16924             }];
16925         
16926             if(this.bullets  && !Roo.isTouch){
16927                 
16928                 var bullets = {
16929                     cls : 'carousel-bullets',
16930                     cn : []
16931                 };
16932                
16933                 if(this.bullets_cls){
16934                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16935                 }
16936                 
16937                 bullets.cn.push({
16938                     cls : 'clear'
16939                 });
16940                 
16941                 cfg.cn[0].cn.push(bullets);
16942             }
16943             
16944             if(this.showarrow){
16945                 cfg.cn[0].cn.push({
16946                     tag : 'div',
16947                     class : 'carousel-arrow',
16948                     cn : [
16949                         {
16950                             tag : 'div',
16951                             class : 'carousel-prev',
16952                             cn : [
16953                                 {
16954                                     tag : 'i',
16955                                     class : 'fa fa-chevron-left'
16956                                 }
16957                             ]
16958                         },
16959                         {
16960                             tag : 'div',
16961                             class : 'carousel-next',
16962                             cn : [
16963                                 {
16964                                     tag : 'i',
16965                                     class : 'fa fa-chevron-right'
16966                                 }
16967                             ]
16968                         }
16969                     ]
16970                 });
16971             }
16972             
16973         }
16974         
16975         return cfg;
16976     },
16977     
16978     initEvents:  function()
16979     {
16980 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
16981 //            this.el.on("touchstart", this.onTouchStart, this);
16982 //        }
16983         
16984         if(this.autoslide){
16985             var _this = this;
16986             
16987             this.slideFn = window.setInterval(function() {
16988                 _this.showPanelNext();
16989             }, this.timer);
16990         }
16991         
16992         if(this.showarrow){
16993             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
16994             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
16995         }
16996         
16997         
16998     },
16999     
17000 //    onTouchStart : function(e, el, o)
17001 //    {
17002 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17003 //            return;
17004 //        }
17005 //        
17006 //        this.showPanelNext();
17007 //    },
17008     
17009     
17010     getChildContainer : function()
17011     {
17012         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17013     },
17014     
17015     /**
17016     * register a Navigation item
17017     * @param {Roo.bootstrap.NavItem} the navitem to add
17018     */
17019     register : function(item)
17020     {
17021         this.tabs.push( item);
17022         item.navId = this.navId; // not really needed..
17023         this.addBullet();
17024     
17025     },
17026     
17027     getActivePanel : function()
17028     {
17029         var r = false;
17030         Roo.each(this.tabs, function(t) {
17031             if (t.active) {
17032                 r = t;
17033                 return false;
17034             }
17035             return null;
17036         });
17037         return r;
17038         
17039     },
17040     getPanelByName : function(n)
17041     {
17042         var r = false;
17043         Roo.each(this.tabs, function(t) {
17044             if (t.tabId == n) {
17045                 r = t;
17046                 return false;
17047             }
17048             return null;
17049         });
17050         return r;
17051     },
17052     indexOfPanel : function(p)
17053     {
17054         var r = false;
17055         Roo.each(this.tabs, function(t,i) {
17056             if (t.tabId == p.tabId) {
17057                 r = i;
17058                 return false;
17059             }
17060             return null;
17061         });
17062         return r;
17063     },
17064     /**
17065      * show a specific panel
17066      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17067      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17068      */
17069     showPanel : function (pan)
17070     {
17071         if(this.transition || typeof(pan) == 'undefined'){
17072             Roo.log("waiting for the transitionend");
17073             return;
17074         }
17075         
17076         if (typeof(pan) == 'number') {
17077             pan = this.tabs[pan];
17078         }
17079         
17080         if (typeof(pan) == 'string') {
17081             pan = this.getPanelByName(pan);
17082         }
17083         
17084         var cur = this.getActivePanel();
17085         
17086         if(!pan || !cur){
17087             Roo.log('pan or acitve pan is undefined');
17088             return false;
17089         }
17090         
17091         if (pan.tabId == this.getActivePanel().tabId) {
17092             return true;
17093         }
17094         
17095         if (false === cur.fireEvent('beforedeactivate')) {
17096             return false;
17097         }
17098         
17099         if(this.bullets > 0 && !Roo.isTouch){
17100             this.setActiveBullet(this.indexOfPanel(pan));
17101         }
17102         
17103         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17104             
17105             this.transition = true;
17106             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17107             var lr = dir == 'next' ? 'left' : 'right';
17108             pan.el.addClass(dir); // or prev
17109             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17110             cur.el.addClass(lr); // or right
17111             pan.el.addClass(lr);
17112             
17113             var _this = this;
17114             cur.el.on('transitionend', function() {
17115                 Roo.log("trans end?");
17116                 
17117                 pan.el.removeClass([lr,dir]);
17118                 pan.setActive(true);
17119                 
17120                 cur.el.removeClass([lr]);
17121                 cur.setActive(false);
17122                 
17123                 _this.transition = false;
17124                 
17125             }, this, { single:  true } );
17126             
17127             return true;
17128         }
17129         
17130         cur.setActive(false);
17131         pan.setActive(true);
17132         
17133         return true;
17134         
17135     },
17136     showPanelNext : function()
17137     {
17138         var i = this.indexOfPanel(this.getActivePanel());
17139         
17140         if (i >= this.tabs.length - 1 && !this.autoslide) {
17141             return;
17142         }
17143         
17144         if (i >= this.tabs.length - 1 && this.autoslide) {
17145             i = -1;
17146         }
17147         
17148         this.showPanel(this.tabs[i+1]);
17149     },
17150     
17151     showPanelPrev : function()
17152     {
17153         var i = this.indexOfPanel(this.getActivePanel());
17154         
17155         if (i  < 1 && !this.autoslide) {
17156             return;
17157         }
17158         
17159         if (i < 1 && this.autoslide) {
17160             i = this.tabs.length;
17161         }
17162         
17163         this.showPanel(this.tabs[i-1]);
17164     },
17165     
17166     
17167     addBullet: function()
17168     {
17169         if(!this.bullets || Roo.isTouch){
17170             return;
17171         }
17172         var ctr = this.el.select('.carousel-bullets',true).first();
17173         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17174         var bullet = ctr.createChild({
17175             cls : 'bullet bullet-' + i
17176         },ctr.dom.lastChild);
17177         
17178         
17179         var _this = this;
17180         
17181         bullet.on('click', (function(e, el, o, ii, t){
17182
17183             e.preventDefault();
17184
17185             this.showPanel(ii);
17186
17187             if(this.autoslide && this.slideFn){
17188                 clearInterval(this.slideFn);
17189                 this.slideFn = window.setInterval(function() {
17190                     _this.showPanelNext();
17191                 }, this.timer);
17192             }
17193
17194         }).createDelegate(this, [i, bullet], true));
17195                 
17196         
17197     },
17198      
17199     setActiveBullet : function(i)
17200     {
17201         if(Roo.isTouch){
17202             return;
17203         }
17204         
17205         Roo.each(this.el.select('.bullet', true).elements, function(el){
17206             el.removeClass('selected');
17207         });
17208
17209         var bullet = this.el.select('.bullet-' + i, true).first();
17210         
17211         if(!bullet){
17212             return;
17213         }
17214         
17215         bullet.addClass('selected');
17216     }
17217     
17218     
17219   
17220 });
17221
17222  
17223
17224  
17225  
17226 Roo.apply(Roo.bootstrap.TabGroup, {
17227     
17228     groups: {},
17229      /**
17230     * register a Navigation Group
17231     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17232     */
17233     register : function(navgrp)
17234     {
17235         this.groups[navgrp.navId] = navgrp;
17236         
17237     },
17238     /**
17239     * fetch a Navigation Group based on the navigation ID
17240     * if one does not exist , it will get created.
17241     * @param {string} the navgroup to add
17242     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17243     */
17244     get: function(navId) {
17245         if (typeof(this.groups[navId]) == 'undefined') {
17246             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17247         }
17248         return this.groups[navId] ;
17249     }
17250     
17251     
17252     
17253 });
17254
17255  /*
17256  * - LGPL
17257  *
17258  * TabPanel
17259  * 
17260  */
17261
17262 /**
17263  * @class Roo.bootstrap.TabPanel
17264  * @extends Roo.bootstrap.Component
17265  * Bootstrap TabPanel class
17266  * @cfg {Boolean} active panel active
17267  * @cfg {String} html panel content
17268  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17269  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17270  * @cfg {String} href click to link..
17271  * 
17272  * 
17273  * @constructor
17274  * Create a new TabPanel
17275  * @param {Object} config The config object
17276  */
17277
17278 Roo.bootstrap.TabPanel = function(config){
17279     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17280     this.addEvents({
17281         /**
17282              * @event changed
17283              * Fires when the active status changes
17284              * @param {Roo.bootstrap.TabPanel} this
17285              * @param {Boolean} state the new state
17286             
17287          */
17288         'changed': true,
17289         /**
17290              * @event beforedeactivate
17291              * Fires before a tab is de-activated - can be used to do validation on a form.
17292              * @param {Roo.bootstrap.TabPanel} this
17293              * @return {Boolean} false if there is an error
17294             
17295          */
17296         'beforedeactivate': true
17297      });
17298     
17299     this.tabId = this.tabId || Roo.id();
17300   
17301 };
17302
17303 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17304     
17305     active: false,
17306     html: false,
17307     tabId: false,
17308     navId : false,
17309     href : '',
17310     
17311     getAutoCreate : function(){
17312         var cfg = {
17313             tag: 'div',
17314             // item is needed for carousel - not sure if it has any effect otherwise
17315             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17316             html: this.html || ''
17317         };
17318         
17319         if(this.active){
17320             cfg.cls += ' active';
17321         }
17322         
17323         if(this.tabId){
17324             cfg.tabId = this.tabId;
17325         }
17326         
17327         
17328         return cfg;
17329     },
17330     
17331     initEvents:  function()
17332     {
17333         var p = this.parent();
17334         
17335         this.navId = this.navId || p.navId;
17336         
17337         if (typeof(this.navId) != 'undefined') {
17338             // not really needed.. but just in case.. parent should be a NavGroup.
17339             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17340             
17341             tg.register(this);
17342             
17343             var i = tg.tabs.length - 1;
17344             
17345             if(this.active && tg.bullets > 0 && i < tg.bullets){
17346                 tg.setActiveBullet(i);
17347             }
17348         }
17349         
17350         this.el.on('click', this.onClick, this);
17351         
17352         if(Roo.isTouch){
17353             this.el.on("touchstart", this.onTouchStart, this);
17354             this.el.on("touchmove", this.onTouchMove, this);
17355             this.el.on("touchend", this.onTouchEnd, this);
17356         }
17357         
17358     },
17359     
17360     onRender : function(ct, position)
17361     {
17362         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17363     },
17364     
17365     setActive : function(state)
17366     {
17367         Roo.log("panel - set active " + this.tabId + "=" + state);
17368         
17369         this.active = state;
17370         if (!state) {
17371             this.el.removeClass('active');
17372             
17373         } else  if (!this.el.hasClass('active')) {
17374             this.el.addClass('active');
17375         }
17376         
17377         this.fireEvent('changed', this, state);
17378     },
17379     
17380     onClick : function(e)
17381     {
17382         e.preventDefault();
17383         
17384         if(!this.href.length){
17385             return;
17386         }
17387         
17388         window.location.href = this.href;
17389     },
17390     
17391     startX : 0,
17392     startY : 0,
17393     endX : 0,
17394     endY : 0,
17395     swiping : false,
17396     
17397     onTouchStart : function(e)
17398     {
17399         this.swiping = false;
17400         
17401         this.startX = e.browserEvent.touches[0].clientX;
17402         this.startY = e.browserEvent.touches[0].clientY;
17403     },
17404     
17405     onTouchMove : function(e)
17406     {
17407         this.swiping = true;
17408         
17409         this.endX = e.browserEvent.touches[0].clientX;
17410         this.endY = e.browserEvent.touches[0].clientY;
17411     },
17412     
17413     onTouchEnd : function(e)
17414     {
17415         if(!this.swiping){
17416             this.onClick(e);
17417             return;
17418         }
17419         
17420         var tabGroup = this.parent();
17421         
17422         if(this.endX > this.startX){ // swiping right
17423             tabGroup.showPanelPrev();
17424             return;
17425         }
17426         
17427         if(this.startX > this.endX){ // swiping left
17428             tabGroup.showPanelNext();
17429             return;
17430         }
17431     }
17432     
17433     
17434 });
17435  
17436
17437  
17438
17439  /*
17440  * - LGPL
17441  *
17442  * DateField
17443  * 
17444  */
17445
17446 /**
17447  * @class Roo.bootstrap.DateField
17448  * @extends Roo.bootstrap.Input
17449  * Bootstrap DateField class
17450  * @cfg {Number} weekStart default 0
17451  * @cfg {String} viewMode default empty, (months|years)
17452  * @cfg {String} minViewMode default empty, (months|years)
17453  * @cfg {Number} startDate default -Infinity
17454  * @cfg {Number} endDate default Infinity
17455  * @cfg {Boolean} todayHighlight default false
17456  * @cfg {Boolean} todayBtn default false
17457  * @cfg {Boolean} calendarWeeks default false
17458  * @cfg {Object} daysOfWeekDisabled default empty
17459  * @cfg {Boolean} singleMode default false (true | false)
17460  * 
17461  * @cfg {Boolean} keyboardNavigation default true
17462  * @cfg {String} language default en
17463  * 
17464  * @constructor
17465  * Create a new DateField
17466  * @param {Object} config The config object
17467  */
17468
17469 Roo.bootstrap.DateField = function(config){
17470     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
17471      this.addEvents({
17472             /**
17473              * @event show
17474              * Fires when this field show.
17475              * @param {Roo.bootstrap.DateField} this
17476              * @param {Mixed} date The date value
17477              */
17478             show : true,
17479             /**
17480              * @event show
17481              * Fires when this field hide.
17482              * @param {Roo.bootstrap.DateField} this
17483              * @param {Mixed} date The date value
17484              */
17485             hide : true,
17486             /**
17487              * @event select
17488              * Fires when select a date.
17489              * @param {Roo.bootstrap.DateField} this
17490              * @param {Mixed} date The date value
17491              */
17492             select : true,
17493             /**
17494              * @event beforeselect
17495              * Fires when before select a date.
17496              * @param {Roo.bootstrap.DateField} this
17497              * @param {Mixed} date The date value
17498              */
17499             beforeselect : true
17500         });
17501 };
17502
17503 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
17504     
17505     /**
17506      * @cfg {String} format
17507      * The default date format string which can be overriden for localization support.  The format must be
17508      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17509      */
17510     format : "m/d/y",
17511     /**
17512      * @cfg {String} altFormats
17513      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17514      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17515      */
17516     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17517     
17518     weekStart : 0,
17519     
17520     viewMode : '',
17521     
17522     minViewMode : '',
17523     
17524     todayHighlight : false,
17525     
17526     todayBtn: false,
17527     
17528     language: 'en',
17529     
17530     keyboardNavigation: true,
17531     
17532     calendarWeeks: false,
17533     
17534     startDate: -Infinity,
17535     
17536     endDate: Infinity,
17537     
17538     daysOfWeekDisabled: [],
17539     
17540     _events: [],
17541     
17542     singleMode : false,
17543     
17544     UTCDate: function()
17545     {
17546         return new Date(Date.UTC.apply(Date, arguments));
17547     },
17548     
17549     UTCToday: function()
17550     {
17551         var today = new Date();
17552         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
17553     },
17554     
17555     getDate: function() {
17556             var d = this.getUTCDate();
17557             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
17558     },
17559     
17560     getUTCDate: function() {
17561             return this.date;
17562     },
17563     
17564     setDate: function(d) {
17565             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
17566     },
17567     
17568     setUTCDate: function(d) {
17569             this.date = d;
17570             this.setValue(this.formatDate(this.date));
17571     },
17572         
17573     onRender: function(ct, position)
17574     {
17575         
17576         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
17577         
17578         this.language = this.language || 'en';
17579         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
17580         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
17581         
17582         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17583         this.format = this.format || 'm/d/y';
17584         this.isInline = false;
17585         this.isInput = true;
17586         this.component = this.el.select('.add-on', true).first() || false;
17587         this.component = (this.component && this.component.length === 0) ? false : this.component;
17588         this.hasInput = this.component && this.inputEl().length;
17589         
17590         if (typeof(this.minViewMode === 'string')) {
17591             switch (this.minViewMode) {
17592                 case 'months':
17593                     this.minViewMode = 1;
17594                     break;
17595                 case 'years':
17596                     this.minViewMode = 2;
17597                     break;
17598                 default:
17599                     this.minViewMode = 0;
17600                     break;
17601             }
17602         }
17603         
17604         if (typeof(this.viewMode === 'string')) {
17605             switch (this.viewMode) {
17606                 case 'months':
17607                     this.viewMode = 1;
17608                     break;
17609                 case 'years':
17610                     this.viewMode = 2;
17611                     break;
17612                 default:
17613                     this.viewMode = 0;
17614                     break;
17615             }
17616         }
17617                 
17618         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
17619         
17620 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
17621         
17622         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17623         
17624         this.picker().on('mousedown', this.onMousedown, this);
17625         this.picker().on('click', this.onClick, this);
17626         
17627         this.picker().addClass('datepicker-dropdown');
17628         
17629         this.startViewMode = this.viewMode;
17630         
17631         if(this.singleMode){
17632             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17633                 v.setVisibilityMode(Roo.Element.DISPLAY);
17634                 v.hide();
17635             });
17636             
17637             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17638                 v.setStyle('width', '189px');
17639             });
17640         }
17641         
17642         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17643             if(!this.calendarWeeks){
17644                 v.remove();
17645                 return;
17646             }
17647             
17648             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17649             v.attr('colspan', function(i, val){
17650                 return parseInt(val) + 1;
17651             });
17652         });
17653                         
17654         
17655         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17656         
17657         this.setStartDate(this.startDate);
17658         this.setEndDate(this.endDate);
17659         
17660         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17661         
17662         this.fillDow();
17663         this.fillMonths();
17664         this.update();
17665         this.showMode();
17666         
17667         if(this.isInline) {
17668             this.show();
17669         }
17670     },
17671     
17672     picker : function()
17673     {
17674         return this.pickerEl;
17675 //        return this.el.select('.datepicker', true).first();
17676     },
17677     
17678     fillDow: function()
17679     {
17680         var dowCnt = this.weekStart;
17681         
17682         var dow = {
17683             tag: 'tr',
17684             cn: [
17685                 
17686             ]
17687         };
17688         
17689         if(this.calendarWeeks){
17690             dow.cn.push({
17691                 tag: 'th',
17692                 cls: 'cw',
17693                 html: '&nbsp;'
17694             })
17695         }
17696         
17697         while (dowCnt < this.weekStart + 7) {
17698             dow.cn.push({
17699                 tag: 'th',
17700                 cls: 'dow',
17701                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17702             });
17703         }
17704         
17705         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17706     },
17707     
17708     fillMonths: function()
17709     {    
17710         var i = 0;
17711         var months = this.picker().select('>.datepicker-months td', true).first();
17712         
17713         months.dom.innerHTML = '';
17714         
17715         while (i < 12) {
17716             var month = {
17717                 tag: 'span',
17718                 cls: 'month',
17719                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17720             };
17721             
17722             months.createChild(month);
17723         }
17724         
17725     },
17726     
17727     update: function()
17728     {
17729         this.date = (typeof(this.date) === 'undefined' || ((typeof(this.date) === 'string') && !this.date.length)) ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
17730         
17731         if (this.date < this.startDate) {
17732             this.viewDate = new Date(this.startDate);
17733         } else if (this.date > this.endDate) {
17734             this.viewDate = new Date(this.endDate);
17735         } else {
17736             this.viewDate = new Date(this.date);
17737         }
17738         
17739         this.fill();
17740     },
17741     
17742     fill: function() 
17743     {
17744         var d = new Date(this.viewDate),
17745                 year = d.getUTCFullYear(),
17746                 month = d.getUTCMonth(),
17747                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17748                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17749                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17750                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17751                 currentDate = this.date && this.date.valueOf(),
17752                 today = this.UTCToday();
17753         
17754         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17755         
17756 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17757         
17758 //        this.picker.select('>tfoot th.today').
17759 //                                              .text(dates[this.language].today)
17760 //                                              .toggle(this.todayBtn !== false);
17761     
17762         this.updateNavArrows();
17763         this.fillMonths();
17764                                                 
17765         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17766         
17767         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17768          
17769         prevMonth.setUTCDate(day);
17770         
17771         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17772         
17773         var nextMonth = new Date(prevMonth);
17774         
17775         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17776         
17777         nextMonth = nextMonth.valueOf();
17778         
17779         var fillMonths = false;
17780         
17781         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17782         
17783         while(prevMonth.valueOf() < nextMonth) {
17784             var clsName = '';
17785             
17786             if (prevMonth.getUTCDay() === this.weekStart) {
17787                 if(fillMonths){
17788                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17789                 }
17790                     
17791                 fillMonths = {
17792                     tag: 'tr',
17793                     cn: []
17794                 };
17795                 
17796                 if(this.calendarWeeks){
17797                     // ISO 8601: First week contains first thursday.
17798                     // ISO also states week starts on Monday, but we can be more abstract here.
17799                     var
17800                     // Start of current week: based on weekstart/current date
17801                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17802                     // Thursday of this week
17803                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17804                     // First Thursday of year, year from thursday
17805                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17806                     // Calendar week: ms between thursdays, div ms per day, div 7 days
17807                     calWeek =  (th - yth) / 864e5 / 7 + 1;
17808                     
17809                     fillMonths.cn.push({
17810                         tag: 'td',
17811                         cls: 'cw',
17812                         html: calWeek
17813                     });
17814                 }
17815             }
17816             
17817             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17818                 clsName += ' old';
17819             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17820                 clsName += ' new';
17821             }
17822             if (this.todayHighlight &&
17823                 prevMonth.getUTCFullYear() == today.getFullYear() &&
17824                 prevMonth.getUTCMonth() == today.getMonth() &&
17825                 prevMonth.getUTCDate() == today.getDate()) {
17826                 clsName += ' today';
17827             }
17828             
17829             if (currentDate && prevMonth.valueOf() === currentDate) {
17830                 clsName += ' active';
17831             }
17832             
17833             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17834                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17835                     clsName += ' disabled';
17836             }
17837             
17838             fillMonths.cn.push({
17839                 tag: 'td',
17840                 cls: 'day ' + clsName,
17841                 html: prevMonth.getDate()
17842             });
17843             
17844             prevMonth.setDate(prevMonth.getDate()+1);
17845         }
17846           
17847         var currentYear = this.date && this.date.getUTCFullYear();
17848         var currentMonth = this.date && this.date.getUTCMonth();
17849         
17850         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17851         
17852         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17853             v.removeClass('active');
17854             
17855             if(currentYear === year && k === currentMonth){
17856                 v.addClass('active');
17857             }
17858             
17859             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17860                 v.addClass('disabled');
17861             }
17862             
17863         });
17864         
17865         
17866         year = parseInt(year/10, 10) * 10;
17867         
17868         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17869         
17870         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17871         
17872         year -= 1;
17873         for (var i = -1; i < 11; i++) {
17874             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17875                 tag: 'span',
17876                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17877                 html: year
17878             });
17879             
17880             year += 1;
17881         }
17882     },
17883     
17884     showMode: function(dir) 
17885     {
17886         if (dir) {
17887             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17888         }
17889         
17890         Roo.each(this.picker().select('>div',true).elements, function(v){
17891             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17892             v.hide();
17893         });
17894         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17895     },
17896     
17897     place: function()
17898     {
17899         if(this.isInline) {
17900             return;
17901         }
17902         
17903         this.picker().removeClass(['bottom', 'top']);
17904         
17905         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17906             /*
17907              * place to the top of element!
17908              *
17909              */
17910             
17911             this.picker().addClass('top');
17912             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17913             
17914             return;
17915         }
17916         
17917         this.picker().addClass('bottom');
17918         
17919         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17920     },
17921     
17922     parseDate : function(value)
17923     {
17924         if(!value || value instanceof Date){
17925             return value;
17926         }
17927         var v = Date.parseDate(value, this.format);
17928         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17929             v = Date.parseDate(value, 'Y-m-d');
17930         }
17931         if(!v && this.altFormats){
17932             if(!this.altFormatsArray){
17933                 this.altFormatsArray = this.altFormats.split("|");
17934             }
17935             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17936                 v = Date.parseDate(value, this.altFormatsArray[i]);
17937             }
17938         }
17939         return v;
17940     },
17941     
17942     formatDate : function(date, fmt)
17943     {   
17944         return (!date || !(date instanceof Date)) ?
17945         date : date.dateFormat(fmt || this.format);
17946     },
17947     
17948     onFocus : function()
17949     {
17950         Roo.bootstrap.DateField.superclass.onFocus.call(this);
17951         this.show();
17952     },
17953     
17954     onBlur : function()
17955     {
17956         Roo.bootstrap.DateField.superclass.onBlur.call(this);
17957         
17958         var d = this.inputEl().getValue();
17959         
17960         this.setValue(d);
17961                 
17962         this.hide();
17963     },
17964     
17965     show : function()
17966     {
17967         this.picker().show();
17968         this.update();
17969         this.place();
17970         
17971         this.fireEvent('show', this, this.date);
17972     },
17973     
17974     hide : function()
17975     {
17976         if(this.isInline) {
17977             return;
17978         }
17979         this.picker().hide();
17980         this.viewMode = this.startViewMode;
17981         this.showMode();
17982         
17983         this.fireEvent('hide', this, this.date);
17984         
17985     },
17986     
17987     onMousedown: function(e)
17988     {
17989         e.stopPropagation();
17990         e.preventDefault();
17991     },
17992     
17993     keyup: function(e)
17994     {
17995         Roo.bootstrap.DateField.superclass.keyup.call(this);
17996         this.update();
17997     },
17998
17999     setValue: function(v)
18000     {
18001         if(this.fireEvent('beforeselect', this, v) !== false){
18002             var d = new Date(this.parseDate(v) ).clearTime();
18003         
18004             if(isNaN(d.getTime())){
18005                 this.date = this.viewDate = '';
18006                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18007                 return;
18008             }
18009
18010             v = this.formatDate(d);
18011
18012             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18013
18014             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18015
18016             this.update();
18017
18018             this.fireEvent('select', this, this.date);
18019         }
18020     },
18021     
18022     getValue: function()
18023     {
18024         return this.formatDate(this.date);
18025     },
18026     
18027     fireKey: function(e)
18028     {
18029         if (!this.picker().isVisible()){
18030             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18031                 this.show();
18032             }
18033             return;
18034         }
18035         
18036         var dateChanged = false,
18037         dir, day, month,
18038         newDate, newViewDate;
18039         
18040         switch(e.keyCode){
18041             case 27: // escape
18042                 this.hide();
18043                 e.preventDefault();
18044                 break;
18045             case 37: // left
18046             case 39: // right
18047                 if (!this.keyboardNavigation) {
18048                     break;
18049                 }
18050                 dir = e.keyCode == 37 ? -1 : 1;
18051                 
18052                 if (e.ctrlKey){
18053                     newDate = this.moveYear(this.date, dir);
18054                     newViewDate = this.moveYear(this.viewDate, dir);
18055                 } else if (e.shiftKey){
18056                     newDate = this.moveMonth(this.date, dir);
18057                     newViewDate = this.moveMonth(this.viewDate, dir);
18058                 } else {
18059                     newDate = new Date(this.date);
18060                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18061                     newViewDate = new Date(this.viewDate);
18062                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18063                 }
18064                 if (this.dateWithinRange(newDate)){
18065                     this.date = newDate;
18066                     this.viewDate = newViewDate;
18067                     this.setValue(this.formatDate(this.date));
18068 //                    this.update();
18069                     e.preventDefault();
18070                     dateChanged = true;
18071                 }
18072                 break;
18073             case 38: // up
18074             case 40: // down
18075                 if (!this.keyboardNavigation) {
18076                     break;
18077                 }
18078                 dir = e.keyCode == 38 ? -1 : 1;
18079                 if (e.ctrlKey){
18080                     newDate = this.moveYear(this.date, dir);
18081                     newViewDate = this.moveYear(this.viewDate, dir);
18082                 } else if (e.shiftKey){
18083                     newDate = this.moveMonth(this.date, dir);
18084                     newViewDate = this.moveMonth(this.viewDate, dir);
18085                 } else {
18086                     newDate = new Date(this.date);
18087                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18088                     newViewDate = new Date(this.viewDate);
18089                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18090                 }
18091                 if (this.dateWithinRange(newDate)){
18092                     this.date = newDate;
18093                     this.viewDate = newViewDate;
18094                     this.setValue(this.formatDate(this.date));
18095 //                    this.update();
18096                     e.preventDefault();
18097                     dateChanged = true;
18098                 }
18099                 break;
18100             case 13: // enter
18101                 this.setValue(this.formatDate(this.date));
18102                 this.hide();
18103                 e.preventDefault();
18104                 break;
18105             case 9: // tab
18106                 this.setValue(this.formatDate(this.date));
18107                 this.hide();
18108                 break;
18109             case 16: // shift
18110             case 17: // ctrl
18111             case 18: // alt
18112                 break;
18113             default :
18114                 this.hide();
18115                 
18116         }
18117     },
18118     
18119     
18120     onClick: function(e) 
18121     {
18122         e.stopPropagation();
18123         e.preventDefault();
18124         
18125         var target = e.getTarget();
18126         
18127         if(target.nodeName.toLowerCase() === 'i'){
18128             target = Roo.get(target).dom.parentNode;
18129         }
18130         
18131         var nodeName = target.nodeName;
18132         var className = target.className;
18133         var html = target.innerHTML;
18134         //Roo.log(nodeName);
18135         
18136         switch(nodeName.toLowerCase()) {
18137             case 'th':
18138                 switch(className) {
18139                     case 'switch':
18140                         this.showMode(1);
18141                         break;
18142                     case 'prev':
18143                     case 'next':
18144                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18145                         switch(this.viewMode){
18146                                 case 0:
18147                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18148                                         break;
18149                                 case 1:
18150                                 case 2:
18151                                         this.viewDate = this.moveYear(this.viewDate, dir);
18152                                         break;
18153                         }
18154                         this.fill();
18155                         break;
18156                     case 'today':
18157                         var date = new Date();
18158                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18159 //                        this.fill()
18160                         this.setValue(this.formatDate(this.date));
18161                         
18162                         this.hide();
18163                         break;
18164                 }
18165                 break;
18166             case 'span':
18167                 if (className.indexOf('disabled') < 0) {
18168                     this.viewDate.setUTCDate(1);
18169                     if (className.indexOf('month') > -1) {
18170                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18171                     } else {
18172                         var year = parseInt(html, 10) || 0;
18173                         this.viewDate.setUTCFullYear(year);
18174                         
18175                     }
18176                     
18177                     if(this.singleMode){
18178                         this.setValue(this.formatDate(this.viewDate));
18179                         this.hide();
18180                         return;
18181                     }
18182                     
18183                     this.showMode(-1);
18184                     this.fill();
18185                 }
18186                 break;
18187                 
18188             case 'td':
18189                 //Roo.log(className);
18190                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18191                     var day = parseInt(html, 10) || 1;
18192                     var year = this.viewDate.getUTCFullYear(),
18193                         month = this.viewDate.getUTCMonth();
18194
18195                     if (className.indexOf('old') > -1) {
18196                         if(month === 0 ){
18197                             month = 11;
18198                             year -= 1;
18199                         }else{
18200                             month -= 1;
18201                         }
18202                     } else if (className.indexOf('new') > -1) {
18203                         if (month == 11) {
18204                             month = 0;
18205                             year += 1;
18206                         } else {
18207                             month += 1;
18208                         }
18209                     }
18210                     //Roo.log([year,month,day]);
18211                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18212                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18213 //                    this.fill();
18214                     //Roo.log(this.formatDate(this.date));
18215                     this.setValue(this.formatDate(this.date));
18216                     this.hide();
18217                 }
18218                 break;
18219         }
18220     },
18221     
18222     setStartDate: function(startDate)
18223     {
18224         this.startDate = startDate || -Infinity;
18225         if (this.startDate !== -Infinity) {
18226             this.startDate = this.parseDate(this.startDate);
18227         }
18228         this.update();
18229         this.updateNavArrows();
18230     },
18231
18232     setEndDate: function(endDate)
18233     {
18234         this.endDate = endDate || Infinity;
18235         if (this.endDate !== Infinity) {
18236             this.endDate = this.parseDate(this.endDate);
18237         }
18238         this.update();
18239         this.updateNavArrows();
18240     },
18241     
18242     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18243     {
18244         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18245         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18246             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18247         }
18248         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18249             return parseInt(d, 10);
18250         });
18251         this.update();
18252         this.updateNavArrows();
18253     },
18254     
18255     updateNavArrows: function() 
18256     {
18257         if(this.singleMode){
18258             return;
18259         }
18260         
18261         var d = new Date(this.viewDate),
18262         year = d.getUTCFullYear(),
18263         month = d.getUTCMonth();
18264         
18265         Roo.each(this.picker().select('.prev', true).elements, function(v){
18266             v.show();
18267             switch (this.viewMode) {
18268                 case 0:
18269
18270                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18271                         v.hide();
18272                     }
18273                     break;
18274                 case 1:
18275                 case 2:
18276                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18277                         v.hide();
18278                     }
18279                     break;
18280             }
18281         });
18282         
18283         Roo.each(this.picker().select('.next', true).elements, function(v){
18284             v.show();
18285             switch (this.viewMode) {
18286                 case 0:
18287
18288                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18289                         v.hide();
18290                     }
18291                     break;
18292                 case 1:
18293                 case 2:
18294                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18295                         v.hide();
18296                     }
18297                     break;
18298             }
18299         })
18300     },
18301     
18302     moveMonth: function(date, dir)
18303     {
18304         if (!dir) {
18305             return date;
18306         }
18307         var new_date = new Date(date.valueOf()),
18308         day = new_date.getUTCDate(),
18309         month = new_date.getUTCMonth(),
18310         mag = Math.abs(dir),
18311         new_month, test;
18312         dir = dir > 0 ? 1 : -1;
18313         if (mag == 1){
18314             test = dir == -1
18315             // If going back one month, make sure month is not current month
18316             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18317             ? function(){
18318                 return new_date.getUTCMonth() == month;
18319             }
18320             // If going forward one month, make sure month is as expected
18321             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18322             : function(){
18323                 return new_date.getUTCMonth() != new_month;
18324             };
18325             new_month = month + dir;
18326             new_date.setUTCMonth(new_month);
18327             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18328             if (new_month < 0 || new_month > 11) {
18329                 new_month = (new_month + 12) % 12;
18330             }
18331         } else {
18332             // For magnitudes >1, move one month at a time...
18333             for (var i=0; i<mag; i++) {
18334                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18335                 new_date = this.moveMonth(new_date, dir);
18336             }
18337             // ...then reset the day, keeping it in the new month
18338             new_month = new_date.getUTCMonth();
18339             new_date.setUTCDate(day);
18340             test = function(){
18341                 return new_month != new_date.getUTCMonth();
18342             };
18343         }
18344         // Common date-resetting loop -- if date is beyond end of month, make it
18345         // end of month
18346         while (test()){
18347             new_date.setUTCDate(--day);
18348             new_date.setUTCMonth(new_month);
18349         }
18350         return new_date;
18351     },
18352
18353     moveYear: function(date, dir)
18354     {
18355         return this.moveMonth(date, dir*12);
18356     },
18357
18358     dateWithinRange: function(date)
18359     {
18360         return date >= this.startDate && date <= this.endDate;
18361     },
18362
18363     
18364     remove: function() 
18365     {
18366         this.picker().remove();
18367     },
18368     
18369     validateValue : function(value)
18370     {
18371         if(value.length < 1)  {
18372             if(this.allowBlank){
18373                 return true;
18374             }
18375             return false;
18376         }
18377         
18378         if(value.length < this.minLength){
18379             return false;
18380         }
18381         if(value.length > this.maxLength){
18382             return false;
18383         }
18384         if(this.vtype){
18385             var vt = Roo.form.VTypes;
18386             if(!vt[this.vtype](value, this)){
18387                 return false;
18388             }
18389         }
18390         if(typeof this.validator == "function"){
18391             var msg = this.validator(value);
18392             if(msg !== true){
18393                 return false;
18394             }
18395         }
18396         
18397         if(this.regex && !this.regex.test(value)){
18398             return false;
18399         }
18400         
18401         if(typeof(this.parseDate(value)) == 'undefined'){
18402             return false;
18403         }
18404         
18405         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
18406             return false;
18407         }      
18408         
18409         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
18410             return false;
18411         } 
18412         
18413         
18414         return true;
18415     }
18416    
18417 });
18418
18419 Roo.apply(Roo.bootstrap.DateField,  {
18420     
18421     head : {
18422         tag: 'thead',
18423         cn: [
18424         {
18425             tag: 'tr',
18426             cn: [
18427             {
18428                 tag: 'th',
18429                 cls: 'prev',
18430                 html: '<i class="fa fa-arrow-left"/>'
18431             },
18432             {
18433                 tag: 'th',
18434                 cls: 'switch',
18435                 colspan: '5'
18436             },
18437             {
18438                 tag: 'th',
18439                 cls: 'next',
18440                 html: '<i class="fa fa-arrow-right"/>'
18441             }
18442
18443             ]
18444         }
18445         ]
18446     },
18447     
18448     content : {
18449         tag: 'tbody',
18450         cn: [
18451         {
18452             tag: 'tr',
18453             cn: [
18454             {
18455                 tag: 'td',
18456                 colspan: '7'
18457             }
18458             ]
18459         }
18460         ]
18461     },
18462     
18463     footer : {
18464         tag: 'tfoot',
18465         cn: [
18466         {
18467             tag: 'tr',
18468             cn: [
18469             {
18470                 tag: 'th',
18471                 colspan: '7',
18472                 cls: 'today'
18473             }
18474                     
18475             ]
18476         }
18477         ]
18478     },
18479     
18480     dates:{
18481         en: {
18482             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
18483             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
18484             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
18485             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18486             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
18487             today: "Today"
18488         }
18489     },
18490     
18491     modes: [
18492     {
18493         clsName: 'days',
18494         navFnc: 'Month',
18495         navStep: 1
18496     },
18497     {
18498         clsName: 'months',
18499         navFnc: 'FullYear',
18500         navStep: 1
18501     },
18502     {
18503         clsName: 'years',
18504         navFnc: 'FullYear',
18505         navStep: 10
18506     }]
18507 });
18508
18509 Roo.apply(Roo.bootstrap.DateField,  {
18510   
18511     template : {
18512         tag: 'div',
18513         cls: 'datepicker dropdown-menu roo-dynamic',
18514         cn: [
18515         {
18516             tag: 'div',
18517             cls: 'datepicker-days',
18518             cn: [
18519             {
18520                 tag: 'table',
18521                 cls: 'table-condensed',
18522                 cn:[
18523                 Roo.bootstrap.DateField.head,
18524                 {
18525                     tag: 'tbody'
18526                 },
18527                 Roo.bootstrap.DateField.footer
18528                 ]
18529             }
18530             ]
18531         },
18532         {
18533             tag: 'div',
18534             cls: 'datepicker-months',
18535             cn: [
18536             {
18537                 tag: 'table',
18538                 cls: 'table-condensed',
18539                 cn:[
18540                 Roo.bootstrap.DateField.head,
18541                 Roo.bootstrap.DateField.content,
18542                 Roo.bootstrap.DateField.footer
18543                 ]
18544             }
18545             ]
18546         },
18547         {
18548             tag: 'div',
18549             cls: 'datepicker-years',
18550             cn: [
18551             {
18552                 tag: 'table',
18553                 cls: 'table-condensed',
18554                 cn:[
18555                 Roo.bootstrap.DateField.head,
18556                 Roo.bootstrap.DateField.content,
18557                 Roo.bootstrap.DateField.footer
18558                 ]
18559             }
18560             ]
18561         }
18562         ]
18563     }
18564 });
18565
18566  
18567
18568  /*
18569  * - LGPL
18570  *
18571  * TimeField
18572  * 
18573  */
18574
18575 /**
18576  * @class Roo.bootstrap.TimeField
18577  * @extends Roo.bootstrap.Input
18578  * Bootstrap DateField class
18579  * 
18580  * 
18581  * @constructor
18582  * Create a new TimeField
18583  * @param {Object} config The config object
18584  */
18585
18586 Roo.bootstrap.TimeField = function(config){
18587     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
18588     this.addEvents({
18589             /**
18590              * @event show
18591              * Fires when this field show.
18592              * @param {Roo.bootstrap.DateField} thisthis
18593              * @param {Mixed} date The date value
18594              */
18595             show : true,
18596             /**
18597              * @event show
18598              * Fires when this field hide.
18599              * @param {Roo.bootstrap.DateField} this
18600              * @param {Mixed} date The date value
18601              */
18602             hide : true,
18603             /**
18604              * @event select
18605              * Fires when select a date.
18606              * @param {Roo.bootstrap.DateField} this
18607              * @param {Mixed} date The date value
18608              */
18609             select : true
18610         });
18611 };
18612
18613 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
18614     
18615     /**
18616      * @cfg {String} format
18617      * The default time format string which can be overriden for localization support.  The format must be
18618      * valid according to {@link Date#parseDate} (defaults to 'H:i').
18619      */
18620     format : "H:i",
18621        
18622     onRender: function(ct, position)
18623     {
18624         
18625         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
18626                 
18627         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
18628         
18629         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18630         
18631         this.pop = this.picker().select('>.datepicker-time',true).first();
18632         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18633         
18634         this.picker().on('mousedown', this.onMousedown, this);
18635         this.picker().on('click', this.onClick, this);
18636         
18637         this.picker().addClass('datepicker-dropdown');
18638     
18639         this.fillTime();
18640         this.update();
18641             
18642         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
18643         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
18644         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
18645         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
18646         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
18647         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
18648
18649     },
18650     
18651     fireKey: function(e){
18652         if (!this.picker().isVisible()){
18653             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18654                 this.show();
18655             }
18656             return;
18657         }
18658
18659         e.preventDefault();
18660         
18661         switch(e.keyCode){
18662             case 27: // escape
18663                 this.hide();
18664                 break;
18665             case 37: // left
18666             case 39: // right
18667                 this.onTogglePeriod();
18668                 break;
18669             case 38: // up
18670                 this.onIncrementMinutes();
18671                 break;
18672             case 40: // down
18673                 this.onDecrementMinutes();
18674                 break;
18675             case 13: // enter
18676             case 9: // tab
18677                 this.setTime();
18678                 break;
18679         }
18680     },
18681     
18682     onClick: function(e) {
18683         e.stopPropagation();
18684         e.preventDefault();
18685     },
18686     
18687     picker : function()
18688     {
18689         return this.el.select('.datepicker', true).first();
18690     },
18691     
18692     fillTime: function()
18693     {    
18694         var time = this.pop.select('tbody', true).first();
18695         
18696         time.dom.innerHTML = '';
18697         
18698         time.createChild({
18699             tag: 'tr',
18700             cn: [
18701                 {
18702                     tag: 'td',
18703                     cn: [
18704                         {
18705                             tag: 'a',
18706                             href: '#',
18707                             cls: 'btn',
18708                             cn: [
18709                                 {
18710                                     tag: 'span',
18711                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
18712                                 }
18713                             ]
18714                         } 
18715                     ]
18716                 },
18717                 {
18718                     tag: 'td',
18719                     cls: 'separator'
18720                 },
18721                 {
18722                     tag: 'td',
18723                     cn: [
18724                         {
18725                             tag: 'a',
18726                             href: '#',
18727                             cls: 'btn',
18728                             cn: [
18729                                 {
18730                                     tag: 'span',
18731                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
18732                                 }
18733                             ]
18734                         }
18735                     ]
18736                 },
18737                 {
18738                     tag: 'td',
18739                     cls: 'separator'
18740                 }
18741             ]
18742         });
18743         
18744         time.createChild({
18745             tag: 'tr',
18746             cn: [
18747                 {
18748                     tag: 'td',
18749                     cn: [
18750                         {
18751                             tag: 'span',
18752                             cls: 'timepicker-hour',
18753                             html: '00'
18754                         }  
18755                     ]
18756                 },
18757                 {
18758                     tag: 'td',
18759                     cls: 'separator',
18760                     html: ':'
18761                 },
18762                 {
18763                     tag: 'td',
18764                     cn: [
18765                         {
18766                             tag: 'span',
18767                             cls: 'timepicker-minute',
18768                             html: '00'
18769                         }  
18770                     ]
18771                 },
18772                 {
18773                     tag: 'td',
18774                     cls: 'separator'
18775                 },
18776                 {
18777                     tag: 'td',
18778                     cn: [
18779                         {
18780                             tag: 'button',
18781                             type: 'button',
18782                             cls: 'btn btn-primary period',
18783                             html: 'AM'
18784                             
18785                         }
18786                     ]
18787                 }
18788             ]
18789         });
18790         
18791         time.createChild({
18792             tag: 'tr',
18793             cn: [
18794                 {
18795                     tag: 'td',
18796                     cn: [
18797                         {
18798                             tag: 'a',
18799                             href: '#',
18800                             cls: 'btn',
18801                             cn: [
18802                                 {
18803                                     tag: 'span',
18804                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
18805                                 }
18806                             ]
18807                         }
18808                     ]
18809                 },
18810                 {
18811                     tag: 'td',
18812                     cls: 'separator'
18813                 },
18814                 {
18815                     tag: 'td',
18816                     cn: [
18817                         {
18818                             tag: 'a',
18819                             href: '#',
18820                             cls: 'btn',
18821                             cn: [
18822                                 {
18823                                     tag: 'span',
18824                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
18825                                 }
18826                             ]
18827                         }
18828                     ]
18829                 },
18830                 {
18831                     tag: 'td',
18832                     cls: 'separator'
18833                 }
18834             ]
18835         });
18836         
18837     },
18838     
18839     update: function()
18840     {
18841         
18842         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18843         
18844         this.fill();
18845     },
18846     
18847     fill: function() 
18848     {
18849         var hours = this.time.getHours();
18850         var minutes = this.time.getMinutes();
18851         var period = 'AM';
18852         
18853         if(hours > 11){
18854             period = 'PM';
18855         }
18856         
18857         if(hours == 0){
18858             hours = 12;
18859         }
18860         
18861         
18862         if(hours > 12){
18863             hours = hours - 12;
18864         }
18865         
18866         if(hours < 10){
18867             hours = '0' + hours;
18868         }
18869         
18870         if(minutes < 10){
18871             minutes = '0' + minutes;
18872         }
18873         
18874         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18875         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18876         this.pop.select('button', true).first().dom.innerHTML = period;
18877         
18878     },
18879     
18880     place: function()
18881     {   
18882         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18883         
18884         var cls = ['bottom'];
18885         
18886         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18887             cls.pop();
18888             cls.push('top');
18889         }
18890         
18891         cls.push('right');
18892         
18893         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18894             cls.pop();
18895             cls.push('left');
18896         }
18897         
18898         this.picker().addClass(cls.join('-'));
18899         
18900         var _this = this;
18901         
18902         Roo.each(cls, function(c){
18903             if(c == 'bottom'){
18904                 _this.picker().setTop(_this.inputEl().getHeight());
18905                 return;
18906             }
18907             if(c == 'top'){
18908                 _this.picker().setTop(0 - _this.picker().getHeight());
18909                 return;
18910             }
18911             
18912             if(c == 'left'){
18913                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18914                 return;
18915             }
18916             if(c == 'right'){
18917                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18918                 return;
18919             }
18920         });
18921         
18922     },
18923   
18924     onFocus : function()
18925     {
18926         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
18927         this.show();
18928     },
18929     
18930     onBlur : function()
18931     {
18932         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
18933         this.hide();
18934     },
18935     
18936     show : function()
18937     {
18938         this.picker().show();
18939         this.pop.show();
18940         this.update();
18941         this.place();
18942         
18943         this.fireEvent('show', this, this.date);
18944     },
18945     
18946     hide : function()
18947     {
18948         this.picker().hide();
18949         this.pop.hide();
18950         
18951         this.fireEvent('hide', this, this.date);
18952     },
18953     
18954     setTime : function()
18955     {
18956         this.hide();
18957         this.setValue(this.time.format(this.format));
18958         
18959         this.fireEvent('select', this, this.date);
18960         
18961         
18962     },
18963     
18964     onMousedown: function(e){
18965         e.stopPropagation();
18966         e.preventDefault();
18967     },
18968     
18969     onIncrementHours: function()
18970     {
18971         Roo.log('onIncrementHours');
18972         this.time = this.time.add(Date.HOUR, 1);
18973         this.update();
18974         
18975     },
18976     
18977     onDecrementHours: function()
18978     {
18979         Roo.log('onDecrementHours');
18980         this.time = this.time.add(Date.HOUR, -1);
18981         this.update();
18982     },
18983     
18984     onIncrementMinutes: function()
18985     {
18986         Roo.log('onIncrementMinutes');
18987         this.time = this.time.add(Date.MINUTE, 1);
18988         this.update();
18989     },
18990     
18991     onDecrementMinutes: function()
18992     {
18993         Roo.log('onDecrementMinutes');
18994         this.time = this.time.add(Date.MINUTE, -1);
18995         this.update();
18996     },
18997     
18998     onTogglePeriod: function()
18999     {
19000         Roo.log('onTogglePeriod');
19001         this.time = this.time.add(Date.HOUR, 12);
19002         this.update();
19003     }
19004     
19005    
19006 });
19007
19008 Roo.apply(Roo.bootstrap.TimeField,  {
19009     
19010     content : {
19011         tag: 'tbody',
19012         cn: [
19013             {
19014                 tag: 'tr',
19015                 cn: [
19016                 {
19017                     tag: 'td',
19018                     colspan: '7'
19019                 }
19020                 ]
19021             }
19022         ]
19023     },
19024     
19025     footer : {
19026         tag: 'tfoot',
19027         cn: [
19028             {
19029                 tag: 'tr',
19030                 cn: [
19031                 {
19032                     tag: 'th',
19033                     colspan: '7',
19034                     cls: '',
19035                     cn: [
19036                         {
19037                             tag: 'button',
19038                             cls: 'btn btn-info ok',
19039                             html: 'OK'
19040                         }
19041                     ]
19042                 }
19043
19044                 ]
19045             }
19046         ]
19047     }
19048 });
19049
19050 Roo.apply(Roo.bootstrap.TimeField,  {
19051   
19052     template : {
19053         tag: 'div',
19054         cls: 'datepicker dropdown-menu',
19055         cn: [
19056             {
19057                 tag: 'div',
19058                 cls: 'datepicker-time',
19059                 cn: [
19060                 {
19061                     tag: 'table',
19062                     cls: 'table-condensed',
19063                     cn:[
19064                     Roo.bootstrap.TimeField.content,
19065                     Roo.bootstrap.TimeField.footer
19066                     ]
19067                 }
19068                 ]
19069             }
19070         ]
19071     }
19072 });
19073
19074  
19075
19076  /*
19077  * - LGPL
19078  *
19079  * MonthField
19080  * 
19081  */
19082
19083 /**
19084  * @class Roo.bootstrap.MonthField
19085  * @extends Roo.bootstrap.Input
19086  * Bootstrap MonthField class
19087  * 
19088  * @cfg {String} language default en
19089  * 
19090  * @constructor
19091  * Create a new MonthField
19092  * @param {Object} config The config object
19093  */
19094
19095 Roo.bootstrap.MonthField = function(config){
19096     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19097     
19098     this.addEvents({
19099         /**
19100          * @event show
19101          * Fires when this field show.
19102          * @param {Roo.bootstrap.MonthField} this
19103          * @param {Mixed} date The date value
19104          */
19105         show : true,
19106         /**
19107          * @event show
19108          * Fires when this field hide.
19109          * @param {Roo.bootstrap.MonthField} this
19110          * @param {Mixed} date The date value
19111          */
19112         hide : true,
19113         /**
19114          * @event select
19115          * Fires when select a date.
19116          * @param {Roo.bootstrap.MonthField} this
19117          * @param {String} oldvalue The old value
19118          * @param {String} newvalue The new value
19119          */
19120         select : true
19121     });
19122 };
19123
19124 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19125     
19126     onRender: function(ct, position)
19127     {
19128         
19129         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19130         
19131         this.language = this.language || 'en';
19132         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19133         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19134         
19135         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19136         this.isInline = false;
19137         this.isInput = true;
19138         this.component = this.el.select('.add-on', true).first() || false;
19139         this.component = (this.component && this.component.length === 0) ? false : this.component;
19140         this.hasInput = this.component && this.inputEL().length;
19141         
19142         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19143         
19144         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19145         
19146         this.picker().on('mousedown', this.onMousedown, this);
19147         this.picker().on('click', this.onClick, this);
19148         
19149         this.picker().addClass('datepicker-dropdown');
19150         
19151         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19152             v.setStyle('width', '189px');
19153         });
19154         
19155         this.fillMonths();
19156         
19157         this.update();
19158         
19159         if(this.isInline) {
19160             this.show();
19161         }
19162         
19163     },
19164     
19165     setValue: function(v, suppressEvent)
19166     {   
19167         var o = this.getValue();
19168         
19169         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19170         
19171         this.update();
19172
19173         if(suppressEvent !== true){
19174             this.fireEvent('select', this, o, v);
19175         }
19176         
19177     },
19178     
19179     getValue: function()
19180     {
19181         return this.value;
19182     },
19183     
19184     onClick: function(e) 
19185     {
19186         e.stopPropagation();
19187         e.preventDefault();
19188         
19189         var target = e.getTarget();
19190         
19191         if(target.nodeName.toLowerCase() === 'i'){
19192             target = Roo.get(target).dom.parentNode;
19193         }
19194         
19195         var nodeName = target.nodeName;
19196         var className = target.className;
19197         var html = target.innerHTML;
19198         
19199         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19200             return;
19201         }
19202         
19203         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19204         
19205         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19206         
19207         this.hide();
19208                         
19209     },
19210     
19211     picker : function()
19212     {
19213         return this.pickerEl;
19214     },
19215     
19216     fillMonths: function()
19217     {    
19218         var i = 0;
19219         var months = this.picker().select('>.datepicker-months td', true).first();
19220         
19221         months.dom.innerHTML = '';
19222         
19223         while (i < 12) {
19224             var month = {
19225                 tag: 'span',
19226                 cls: 'month',
19227                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19228             };
19229             
19230             months.createChild(month);
19231         }
19232         
19233     },
19234     
19235     update: function()
19236     {
19237         var _this = this;
19238         
19239         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19240             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19241         }
19242         
19243         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19244             e.removeClass('active');
19245             
19246             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19247                 e.addClass('active');
19248             }
19249         })
19250     },
19251     
19252     place: function()
19253     {
19254         if(this.isInline) {
19255             return;
19256         }
19257         
19258         this.picker().removeClass(['bottom', 'top']);
19259         
19260         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19261             /*
19262              * place to the top of element!
19263              *
19264              */
19265             
19266             this.picker().addClass('top');
19267             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19268             
19269             return;
19270         }
19271         
19272         this.picker().addClass('bottom');
19273         
19274         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19275     },
19276     
19277     onFocus : function()
19278     {
19279         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19280         this.show();
19281     },
19282     
19283     onBlur : function()
19284     {
19285         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19286         
19287         var d = this.inputEl().getValue();
19288         
19289         this.setValue(d);
19290                 
19291         this.hide();
19292     },
19293     
19294     show : function()
19295     {
19296         this.picker().show();
19297         this.picker().select('>.datepicker-months', true).first().show();
19298         this.update();
19299         this.place();
19300         
19301         this.fireEvent('show', this, this.date);
19302     },
19303     
19304     hide : function()
19305     {
19306         if(this.isInline) {
19307             return;
19308         }
19309         this.picker().hide();
19310         this.fireEvent('hide', this, this.date);
19311         
19312     },
19313     
19314     onMousedown: function(e)
19315     {
19316         e.stopPropagation();
19317         e.preventDefault();
19318     },
19319     
19320     keyup: function(e)
19321     {
19322         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19323         this.update();
19324     },
19325
19326     fireKey: function(e)
19327     {
19328         if (!this.picker().isVisible()){
19329             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19330                 this.show();
19331             }
19332             return;
19333         }
19334         
19335         var dir;
19336         
19337         switch(e.keyCode){
19338             case 27: // escape
19339                 this.hide();
19340                 e.preventDefault();
19341                 break;
19342             case 37: // left
19343             case 39: // right
19344                 dir = e.keyCode == 37 ? -1 : 1;
19345                 
19346                 this.vIndex = this.vIndex + dir;
19347                 
19348                 if(this.vIndex < 0){
19349                     this.vIndex = 0;
19350                 }
19351                 
19352                 if(this.vIndex > 11){
19353                     this.vIndex = 11;
19354                 }
19355                 
19356                 if(isNaN(this.vIndex)){
19357                     this.vIndex = 0;
19358                 }
19359                 
19360                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19361                 
19362                 break;
19363             case 38: // up
19364             case 40: // down
19365                 
19366                 dir = e.keyCode == 38 ? -1 : 1;
19367                 
19368                 this.vIndex = this.vIndex + dir * 4;
19369                 
19370                 if(this.vIndex < 0){
19371                     this.vIndex = 0;
19372                 }
19373                 
19374                 if(this.vIndex > 11){
19375                     this.vIndex = 11;
19376                 }
19377                 
19378                 if(isNaN(this.vIndex)){
19379                     this.vIndex = 0;
19380                 }
19381                 
19382                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19383                 break;
19384                 
19385             case 13: // enter
19386                 
19387                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19388                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19389                 }
19390                 
19391                 this.hide();
19392                 e.preventDefault();
19393                 break;
19394             case 9: // tab
19395                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19396                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19397                 }
19398                 this.hide();
19399                 break;
19400             case 16: // shift
19401             case 17: // ctrl
19402             case 18: // alt
19403                 break;
19404             default :
19405                 this.hide();
19406                 
19407         }
19408     },
19409     
19410     remove: function() 
19411     {
19412         this.picker().remove();
19413     }
19414    
19415 });
19416
19417 Roo.apply(Roo.bootstrap.MonthField,  {
19418     
19419     content : {
19420         tag: 'tbody',
19421         cn: [
19422         {
19423             tag: 'tr',
19424             cn: [
19425             {
19426                 tag: 'td',
19427                 colspan: '7'
19428             }
19429             ]
19430         }
19431         ]
19432     },
19433     
19434     dates:{
19435         en: {
19436             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19437             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19438         }
19439     }
19440 });
19441
19442 Roo.apply(Roo.bootstrap.MonthField,  {
19443   
19444     template : {
19445         tag: 'div',
19446         cls: 'datepicker dropdown-menu roo-dynamic',
19447         cn: [
19448             {
19449                 tag: 'div',
19450                 cls: 'datepicker-months',
19451                 cn: [
19452                 {
19453                     tag: 'table',
19454                     cls: 'table-condensed',
19455                     cn:[
19456                         Roo.bootstrap.DateField.content
19457                     ]
19458                 }
19459                 ]
19460             }
19461         ]
19462     }
19463 });
19464
19465  
19466
19467  
19468  /*
19469  * - LGPL
19470  *
19471  * CheckBox
19472  * 
19473  */
19474
19475 /**
19476  * @class Roo.bootstrap.CheckBox
19477  * @extends Roo.bootstrap.Input
19478  * Bootstrap CheckBox class
19479  * 
19480  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
19481  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
19482  * @cfg {String} boxLabel The text that appears beside the checkbox
19483  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
19484  * @cfg {Boolean} checked initnal the element
19485  * @cfg {Boolean} inline inline the element (default false)
19486  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
19487  * 
19488  * @constructor
19489  * Create a new CheckBox
19490  * @param {Object} config The config object
19491  */
19492
19493 Roo.bootstrap.CheckBox = function(config){
19494     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
19495    
19496     this.addEvents({
19497         /**
19498         * @event check
19499         * Fires when the element is checked or unchecked.
19500         * @param {Roo.bootstrap.CheckBox} this This input
19501         * @param {Boolean} checked The new checked value
19502         */
19503        check : true
19504     });
19505     
19506 };
19507
19508 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
19509   
19510     inputType: 'checkbox',
19511     inputValue: 1,
19512     valueOff: 0,
19513     boxLabel: false,
19514     checked: false,
19515     weight : false,
19516     inline: false,
19517     
19518     getAutoCreate : function()
19519     {
19520         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19521         
19522         var id = Roo.id();
19523         
19524         var cfg = {};
19525         
19526         cfg.cls = 'form-group ' + this.inputType; //input-group
19527         
19528         if(this.inline){
19529             cfg.cls += ' ' + this.inputType + '-inline';
19530         }
19531         
19532         var input =  {
19533             tag: 'input',
19534             id : id,
19535             type : this.inputType,
19536             value : this.inputValue,
19537             cls : 'roo-' + this.inputType, //'form-box',
19538             placeholder : this.placeholder || ''
19539             
19540         };
19541         
19542         if(this.inputType != 'radio'){
19543             var hidden =  {
19544                 tag: 'input',
19545                 type : 'hidden',
19546                 cls : 'roo-hidden-value',
19547                 value : this.checked ? this.valueOff : this.inputValue
19548             };
19549         }
19550         
19551             
19552         if (this.weight) { // Validity check?
19553             cfg.cls += " " + this.inputType + "-" + this.weight;
19554         }
19555         
19556         if (this.disabled) {
19557             input.disabled=true;
19558         }
19559         
19560         if(this.checked){
19561             input.checked = this.checked;
19562             
19563         }
19564         
19565         
19566         if (this.name) {
19567             
19568             input.name = this.name;
19569             
19570             if(this.inputType != 'radio'){
19571                 hidden.name = this.name;
19572                 input.name = '_hidden_' + this.name;
19573             }
19574         }
19575         
19576         if (this.size) {
19577             input.cls += ' input-' + this.size;
19578         }
19579         
19580         var settings=this;
19581         
19582         ['xs','sm','md','lg'].map(function(size){
19583             if (settings[size]) {
19584                 cfg.cls += ' col-' + size + '-' + settings[size];
19585             }
19586         });
19587         
19588         var inputblock = input;
19589          
19590         if (this.before || this.after) {
19591             
19592             inputblock = {
19593                 cls : 'input-group',
19594                 cn :  [] 
19595             };
19596             
19597             if (this.before) {
19598                 inputblock.cn.push({
19599                     tag :'span',
19600                     cls : 'input-group-addon',
19601                     html : this.before
19602                 });
19603             }
19604             
19605             inputblock.cn.push(input);
19606             
19607             if(this.inputType != 'radio'){
19608                 inputblock.cn.push(hidden);
19609             }
19610             
19611             if (this.after) {
19612                 inputblock.cn.push({
19613                     tag :'span',
19614                     cls : 'input-group-addon',
19615                     html : this.after
19616                 });
19617             }
19618             
19619         }
19620         
19621         if (align ==='left' && this.fieldLabel.length) {
19622 //                Roo.log("left and has label");
19623                 cfg.cn = [
19624                     
19625                     {
19626                         tag: 'label',
19627                         'for' :  id,
19628                         cls : 'control-label col-md-' + this.labelWidth,
19629                         html : this.fieldLabel
19630                         
19631                     },
19632                     {
19633                         cls : "col-md-" + (12 - this.labelWidth), 
19634                         cn: [
19635                             inputblock
19636                         ]
19637                     }
19638                     
19639                 ];
19640         } else if ( this.fieldLabel.length) {
19641 //                Roo.log(" label");
19642                 cfg.cn = [
19643                    
19644                     {
19645                         tag: this.boxLabel ? 'span' : 'label',
19646                         'for': id,
19647                         cls: 'control-label box-input-label',
19648                         //cls : 'input-group-addon',
19649                         html : this.fieldLabel
19650                         
19651                     },
19652                     
19653                     inputblock
19654                     
19655                 ];
19656
19657         } else {
19658             
19659 //                Roo.log(" no label && no align");
19660                 cfg.cn = [  inputblock ] ;
19661                 
19662                 
19663         }
19664         
19665         if(this.boxLabel){
19666              var boxLabelCfg = {
19667                 tag: 'label',
19668                 //'for': id, // box label is handled by onclick - so no for...
19669                 cls: 'box-label',
19670                 html: this.boxLabel
19671             };
19672             
19673             if(this.tooltip){
19674                 boxLabelCfg.tooltip = this.tooltip;
19675             }
19676              
19677             cfg.cn.push(boxLabelCfg);
19678         }
19679         
19680         if(this.inputType != 'radio'){
19681             cfg.cn.push(hidden);
19682         }
19683         
19684         return cfg;
19685         
19686     },
19687     
19688     /**
19689      * return the real input element.
19690      */
19691     inputEl: function ()
19692     {
19693         return this.el.select('input.roo-' + this.inputType,true).first();
19694     },
19695     hiddenEl: function ()
19696     {
19697         return this.el.select('input.roo-hidden-value',true).first();
19698     },
19699     
19700     labelEl: function()
19701     {
19702         return this.el.select('label.control-label',true).first();
19703     },
19704     /* depricated... */
19705     
19706     label: function()
19707     {
19708         return this.labelEl();
19709     },
19710     
19711     boxLabelEl: function()
19712     {
19713         return this.el.select('label.box-label',true).first();
19714     },
19715     
19716     initEvents : function()
19717     {
19718 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19719         
19720         this.inputEl().on('click', this.onClick,  this);
19721         
19722         if (this.boxLabel) { 
19723             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
19724         }
19725         
19726         this.startValue = this.getValue();
19727         
19728         if(this.groupId){
19729             Roo.bootstrap.CheckBox.register(this);
19730         }
19731     },
19732     
19733     onClick : function()
19734     {   
19735         this.setChecked(!this.checked);
19736     },
19737     
19738     setChecked : function(state,suppressEvent)
19739     {
19740         this.startValue = this.getValue();
19741         
19742         if(this.inputType == 'radio'){
19743             
19744             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19745                 e.dom.checked = false;
19746             });
19747             
19748             this.inputEl().dom.checked = true;
19749             
19750             this.inputEl().dom.value = this.inputValue;
19751             
19752             if(suppressEvent !== true){
19753                 this.fireEvent('check', this, true);
19754             }
19755             
19756             this.validate();
19757             
19758             return;
19759         }
19760         
19761         this.checked = state;
19762         
19763         this.inputEl().dom.checked = state;
19764         
19765         
19766         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
19767         
19768         if(suppressEvent !== true){
19769             this.fireEvent('check', this, state);
19770         }
19771         
19772         this.validate();
19773     },
19774     
19775     getValue : function()
19776     {
19777         if(this.inputType == 'radio'){
19778             return this.getGroupValue();
19779         }
19780         
19781         return this.hiddenEl().dom.value;
19782         
19783     },
19784     
19785     getGroupValue : function()
19786     {
19787         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19788             return '';
19789         }
19790         
19791         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19792     },
19793     
19794     setValue : function(v,suppressEvent)
19795     {
19796         if(this.inputType == 'radio'){
19797             this.setGroupValue(v, suppressEvent);
19798             return;
19799         }
19800         
19801         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19802         
19803         this.validate();
19804     },
19805     
19806     setGroupValue : function(v, suppressEvent)
19807     {
19808         this.startValue = this.getValue();
19809         
19810         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19811             e.dom.checked = false;
19812             
19813             if(e.dom.value == v){
19814                 e.dom.checked = true;
19815             }
19816         });
19817         
19818         if(suppressEvent !== true){
19819             this.fireEvent('check', this, true);
19820         }
19821
19822         this.validate();
19823         
19824         return;
19825     },
19826     
19827     validate : function()
19828     {
19829         if(
19830                 this.disabled || 
19831                 (this.inputType == 'radio' && this.validateRadio()) ||
19832                 (this.inputType == 'checkbox' && this.validateCheckbox())
19833         ){
19834             this.markValid();
19835             return true;
19836         }
19837         
19838         this.markInvalid();
19839         return false;
19840     },
19841     
19842     validateRadio : function()
19843     {
19844         if(this.allowBlank){
19845             return true;
19846         }
19847         
19848         var valid = false;
19849         
19850         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19851             if(!e.dom.checked){
19852                 return;
19853             }
19854             
19855             valid = true;
19856             
19857             return false;
19858         });
19859         
19860         return valid;
19861     },
19862     
19863     validateCheckbox : function()
19864     {
19865         if(!this.groupId){
19866             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19867         }
19868         
19869         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19870         
19871         if(!group){
19872             return false;
19873         }
19874         
19875         var r = false;
19876         
19877         for(var i in group){
19878             if(r){
19879                 break;
19880             }
19881             
19882             r = (group[i].getValue() == group[i].inputValue) ? true : false;
19883         }
19884         
19885         return r;
19886     },
19887     
19888     /**
19889      * Mark this field as valid
19890      */
19891     markValid : function()
19892     {
19893         if(this.allowBlank){
19894             return;
19895         }
19896         
19897         var _this = this;
19898         
19899         this.fireEvent('valid', this);
19900         
19901         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19902         
19903         if(this.groupId){
19904             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19905         }
19906         
19907         if(label){
19908             label.markValid();
19909         }
19910         
19911         if(this.inputType == 'radio'){
19912             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19913                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19914                 e.findParent('.form-group', false, true).addClass(_this.validClass);
19915             });
19916             
19917             return;
19918         }
19919         
19920         if(!this.groupId){
19921             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19922             this.el.findParent('.form-group', false, true).addClass(this.validClass);
19923             return;
19924         }
19925         
19926         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19927             
19928         if(!group){
19929             return;
19930         }
19931         
19932         for(var i in group){
19933             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19934             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
19935         }
19936     },
19937     
19938      /**
19939      * Mark this field as invalid
19940      * @param {String} msg The validation message
19941      */
19942     markInvalid : function(msg)
19943     {
19944         if(this.allowBlank){
19945             return;
19946         }
19947         
19948         var _this = this;
19949         
19950         this.fireEvent('invalid', this, msg);
19951         
19952         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19953         
19954         if(this.groupId){
19955             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19956         }
19957         
19958         if(label){
19959             label.markInvalid();
19960         }
19961             
19962         if(this.inputType == 'radio'){
19963             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19964                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19965                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
19966             });
19967             
19968             return;
19969         }
19970         
19971         if(!this.groupId){
19972             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19973             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
19974             return;
19975         }
19976         
19977         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19978         
19979         if(!group){
19980             return;
19981         }
19982         
19983         for(var i in group){
19984             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19985             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
19986         }
19987         
19988     },
19989     
19990     disable : function()
19991     {
19992         if(this.inputType != 'radio'){
19993             Roo.bootstrap.CheckBox.superclass.disable.call(this);
19994             return;
19995         }
19996         
19997         var _this = this;
19998         
19999         if(this.rendered){
20000             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20001                 _this.getActionEl().addClass(this.disabledClass);
20002                 e.dom.disabled = true;
20003             });
20004         }
20005         
20006         this.disabled = true;
20007         this.fireEvent("disable", this);
20008         return this;
20009     },
20010
20011     enable : function()
20012     {
20013         if(this.inputType != 'radio'){
20014             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20015             return;
20016         }
20017         
20018         var _this = this;
20019         
20020         if(this.rendered){
20021             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20022                 _this.getActionEl().removeClass(this.disabledClass);
20023                 e.dom.disabled = false;
20024             });
20025         }
20026         
20027         this.disabled = false;
20028         this.fireEvent("enable", this);
20029         return this;
20030     }
20031
20032 });
20033
20034 Roo.apply(Roo.bootstrap.CheckBox, {
20035     
20036     groups: {},
20037     
20038      /**
20039     * register a CheckBox Group
20040     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20041     */
20042     register : function(checkbox)
20043     {
20044         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20045             this.groups[checkbox.groupId] = {};
20046         }
20047         
20048         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20049             return;
20050         }
20051         
20052         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20053         
20054     },
20055     /**
20056     * fetch a CheckBox Group based on the group ID
20057     * @param {string} the group ID
20058     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20059     */
20060     get: function(groupId) {
20061         if (typeof(this.groups[groupId]) == 'undefined') {
20062             return false;
20063         }
20064         
20065         return this.groups[groupId] ;
20066     }
20067     
20068     
20069 });
20070 /*
20071  * - LGPL
20072  *
20073  * Radio
20074  *
20075  *
20076  * not inline
20077  *<div class="radio">
20078   <label>
20079     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
20080     Option one is this and that&mdash;be sure to include why it's great
20081   </label>
20082 </div>
20083  *
20084  *
20085  *inline
20086  *<span>
20087  *<label class="radio-inline">fieldLabel</label>
20088  *<label class="radio-inline">
20089   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
20090 </label>
20091 <span>
20092  *
20093  *
20094  */
20095
20096 /**
20097  * @class Roo.bootstrap.Radio
20098  * @extends Roo.bootstrap.CheckBox
20099  * Bootstrap Radio class
20100
20101  * @constructor
20102  * Create a new Radio
20103  * @param {Object} config The config object
20104  */
20105
20106 Roo.bootstrap.Radio = function(config){
20107     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20108
20109 };
20110
20111 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
20112
20113     inputType: 'radio',
20114     inputValue: '',
20115     valueOff: '',
20116
20117     getAutoCreate : function()
20118     {
20119         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20120         align = align || 'left'; // default...
20121
20122
20123
20124         var id = Roo.id();
20125
20126         var cfg = {
20127                 tag : this.inline ? 'span' : 'div',
20128                 cls : 'form-group',
20129                 cn : []
20130         };
20131
20132         var inline = this.inline ? ' radio-inline' : '';
20133
20134         var lbl = {
20135                 tag: 'label' ,
20136                 // does not need for, as we wrap the input with it..
20137                 'for' : id,
20138                 cls : 'control-label box-label' + inline,
20139                 cn : []
20140         };
20141         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
20142
20143         var fieldLabel = {
20144             tag: 'label' ,
20145             //cls : 'control-label' + inline,
20146             html : this.fieldLabel,
20147             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
20148         };
20149
20150         var input =  {
20151             tag: 'input',
20152             id : id,
20153             type : this.inputType,
20154             //value : (!this.checked) ? this.valueOff : this.inputValue,
20155             value : this.inputValue,
20156             cls : 'roo-radio',
20157             placeholder : this.placeholder || '' // ?? needed????
20158
20159         };
20160         if (this.weight) { // Validity check?
20161             input.cls += " radio-" + this.weight;
20162         }
20163         if (this.disabled) {
20164             input.disabled=true;
20165         }
20166
20167         if(this.checked){
20168             input.checked = this.checked;
20169         }
20170
20171         if (this.name) {
20172             input.name = this.name;
20173         }
20174
20175         if (this.size) {
20176             input.cls += ' input-' + this.size;
20177         }
20178
20179         //?? can span's inline have a width??
20180
20181         var settings=this;
20182         ['xs','sm','md','lg'].map(function(size){
20183             if (settings[size]) {
20184                 cfg.cls += ' col-' + size + '-' + settings[size];
20185             }
20186         });
20187
20188         var inputblock = input;
20189
20190         if (this.before || this.after) {
20191
20192             inputblock = {
20193                 cls : 'input-group',
20194                 tag : 'span',
20195                 cn :  []
20196             };
20197             if (this.before) {
20198                 inputblock.cn.push({
20199                     tag :'span',
20200                     cls : 'input-group-addon',
20201                     html : this.before
20202                 });
20203             }
20204             inputblock.cn.push(input);
20205             if (this.after) {
20206                 inputblock.cn.push({
20207                     tag :'span',
20208                     cls : 'input-group-addon',
20209                     html : this.after
20210                 });
20211             }
20212
20213         };
20214
20215
20216         if (this.fieldLabel && this.fieldLabel.length) {
20217             cfg.cn.push(fieldLabel);
20218         }
20219
20220         // normal bootstrap puts the input inside the label.
20221         // however with our styled version - it has to go after the input.
20222
20223         //lbl.cn.push(inputblock);
20224
20225         var lblwrap =  {
20226             tag: 'span',
20227             cls: 'radio' + inline,
20228             cn: [
20229                 inputblock,
20230                 lbl
20231             ]
20232         };
20233
20234         cfg.cn.push( lblwrap);
20235
20236         if(this.boxLabel){
20237             lbl.cn.push({
20238                 tag: 'span',
20239                 html: this.boxLabel
20240             })
20241         }
20242
20243
20244         return cfg;
20245
20246     },
20247
20248     initEvents : function()
20249     {
20250 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20251
20252         this.inputEl().on('click', this.onClick,  this);
20253         if (this.boxLabel) {
20254             //Roo.log('find label');
20255             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
20256         }
20257
20258     },
20259
20260     inputEl: function ()
20261     {
20262         return this.el.select('input.roo-radio',true).first();
20263     },
20264     onClick : function()
20265     {
20266         Roo.log("click");
20267         this.setChecked(true);
20268     },
20269
20270     setChecked : function(state,suppressEvent)
20271     {
20272         if(state){
20273             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
20274                 v.dom.checked = false;
20275             });
20276         }
20277         Roo.log(this.inputEl().dom);
20278         this.checked = state;
20279         this.inputEl().dom.checked = state;
20280
20281         if(suppressEvent !== true){
20282             this.fireEvent('check', this, state);
20283         }
20284
20285         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
20286
20287     },
20288
20289     getGroupValue : function()
20290     {
20291         var value = '';
20292         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
20293             if(v.dom.checked == true){
20294                 value = v.dom.value;
20295             }
20296         });
20297
20298         return value;
20299     },
20300
20301     /**
20302      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
20303      * @return {Mixed} value The field value
20304      */
20305     getValue : function(){
20306         return this.getGroupValue();
20307     }
20308
20309 });
20310 //<script type="text/javascript">
20311
20312 /*
20313  * Based  Ext JS Library 1.1.1
20314  * Copyright(c) 2006-2007, Ext JS, LLC.
20315  * LGPL
20316  *
20317  */
20318  
20319 /**
20320  * @class Roo.HtmlEditorCore
20321  * @extends Roo.Component
20322  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20323  *
20324  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20325  */
20326
20327 Roo.HtmlEditorCore = function(config){
20328     
20329     
20330     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20331     
20332     
20333     this.addEvents({
20334         /**
20335          * @event initialize
20336          * Fires when the editor is fully initialized (including the iframe)
20337          * @param {Roo.HtmlEditorCore} this
20338          */
20339         initialize: true,
20340         /**
20341          * @event activate
20342          * Fires when the editor is first receives the focus. Any insertion must wait
20343          * until after this event.
20344          * @param {Roo.HtmlEditorCore} this
20345          */
20346         activate: true,
20347          /**
20348          * @event beforesync
20349          * Fires before the textarea is updated with content from the editor iframe. Return false
20350          * to cancel the sync.
20351          * @param {Roo.HtmlEditorCore} this
20352          * @param {String} html
20353          */
20354         beforesync: true,
20355          /**
20356          * @event beforepush
20357          * Fires before the iframe editor is updated with content from the textarea. Return false
20358          * to cancel the push.
20359          * @param {Roo.HtmlEditorCore} this
20360          * @param {String} html
20361          */
20362         beforepush: true,
20363          /**
20364          * @event sync
20365          * Fires when the textarea is updated with content from the editor iframe.
20366          * @param {Roo.HtmlEditorCore} this
20367          * @param {String} html
20368          */
20369         sync: true,
20370          /**
20371          * @event push
20372          * Fires when the iframe editor is updated with content from the textarea.
20373          * @param {Roo.HtmlEditorCore} this
20374          * @param {String} html
20375          */
20376         push: true,
20377         
20378         /**
20379          * @event editorevent
20380          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20381          * @param {Roo.HtmlEditorCore} this
20382          */
20383         editorevent: true
20384         
20385     });
20386     
20387     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20388     
20389     // defaults : white / black...
20390     this.applyBlacklists();
20391     
20392     
20393     
20394 };
20395
20396
20397 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20398
20399
20400      /**
20401      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20402      */
20403     
20404     owner : false,
20405     
20406      /**
20407      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20408      *                        Roo.resizable.
20409      */
20410     resizable : false,
20411      /**
20412      * @cfg {Number} height (in pixels)
20413      */   
20414     height: 300,
20415    /**
20416      * @cfg {Number} width (in pixels)
20417      */   
20418     width: 500,
20419     
20420     /**
20421      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20422      * 
20423      */
20424     stylesheets: false,
20425     
20426     // id of frame..
20427     frameId: false,
20428     
20429     // private properties
20430     validationEvent : false,
20431     deferHeight: true,
20432     initialized : false,
20433     activated : false,
20434     sourceEditMode : false,
20435     onFocus : Roo.emptyFn,
20436     iframePad:3,
20437     hideMode:'offsets',
20438     
20439     clearUp: true,
20440     
20441     // blacklist + whitelisted elements..
20442     black: false,
20443     white: false,
20444      
20445     
20446
20447     /**
20448      * Protected method that will not generally be called directly. It
20449      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20450      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20451      */
20452     getDocMarkup : function(){
20453         // body styles..
20454         var st = '';
20455         
20456         // inherit styels from page...?? 
20457         if (this.stylesheets === false) {
20458             
20459             Roo.get(document.head).select('style').each(function(node) {
20460                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20461             });
20462             
20463             Roo.get(document.head).select('link').each(function(node) { 
20464                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20465             });
20466             
20467         } else if (!this.stylesheets.length) {
20468                 // simple..
20469                 st = '<style type="text/css">' +
20470                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20471                    '</style>';
20472         } else { 
20473             
20474         }
20475         
20476         st +=  '<style type="text/css">' +
20477             'IMG { cursor: pointer } ' +
20478         '</style>';
20479
20480         
20481         return '<html><head>' + st  +
20482             //<style type="text/css">' +
20483             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20484             //'</style>' +
20485             ' </head><body class="roo-htmleditor-body"></body></html>';
20486     },
20487
20488     // private
20489     onRender : function(ct, position)
20490     {
20491         var _t = this;
20492         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20493         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20494         
20495         
20496         this.el.dom.style.border = '0 none';
20497         this.el.dom.setAttribute('tabIndex', -1);
20498         this.el.addClass('x-hidden hide');
20499         
20500         
20501         
20502         if(Roo.isIE){ // fix IE 1px bogus margin
20503             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20504         }
20505        
20506         
20507         this.frameId = Roo.id();
20508         
20509          
20510         
20511         var iframe = this.owner.wrap.createChild({
20512             tag: 'iframe',
20513             cls: 'form-control', // bootstrap..
20514             id: this.frameId,
20515             name: this.frameId,
20516             frameBorder : 'no',
20517             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20518         }, this.el
20519         );
20520         
20521         
20522         this.iframe = iframe.dom;
20523
20524          this.assignDocWin();
20525         
20526         this.doc.designMode = 'on';
20527        
20528         this.doc.open();
20529         this.doc.write(this.getDocMarkup());
20530         this.doc.close();
20531
20532         
20533         var task = { // must defer to wait for browser to be ready
20534             run : function(){
20535                 //console.log("run task?" + this.doc.readyState);
20536                 this.assignDocWin();
20537                 if(this.doc.body || this.doc.readyState == 'complete'){
20538                     try {
20539                         this.doc.designMode="on";
20540                     } catch (e) {
20541                         return;
20542                     }
20543                     Roo.TaskMgr.stop(task);
20544                     this.initEditor.defer(10, this);
20545                 }
20546             },
20547             interval : 10,
20548             duration: 10000,
20549             scope: this
20550         };
20551         Roo.TaskMgr.start(task);
20552
20553     },
20554
20555     // private
20556     onResize : function(w, h)
20557     {
20558          Roo.log('resize: ' +w + ',' + h );
20559         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20560         if(!this.iframe){
20561             return;
20562         }
20563         if(typeof w == 'number'){
20564             
20565             this.iframe.style.width = w + 'px';
20566         }
20567         if(typeof h == 'number'){
20568             
20569             this.iframe.style.height = h + 'px';
20570             if(this.doc){
20571                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20572             }
20573         }
20574         
20575     },
20576
20577     /**
20578      * Toggles the editor between standard and source edit mode.
20579      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20580      */
20581     toggleSourceEdit : function(sourceEditMode){
20582         
20583         this.sourceEditMode = sourceEditMode === true;
20584         
20585         if(this.sourceEditMode){
20586  
20587             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20588             
20589         }else{
20590             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20591             //this.iframe.className = '';
20592             this.deferFocus();
20593         }
20594         //this.setSize(this.owner.wrap.getSize());
20595         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20596     },
20597
20598     
20599   
20600
20601     /**
20602      * Protected method that will not generally be called directly. If you need/want
20603      * custom HTML cleanup, this is the method you should override.
20604      * @param {String} html The HTML to be cleaned
20605      * return {String} The cleaned HTML
20606      */
20607     cleanHtml : function(html){
20608         html = String(html);
20609         if(html.length > 5){
20610             if(Roo.isSafari){ // strip safari nonsense
20611                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20612             }
20613         }
20614         if(html == '&nbsp;'){
20615             html = '';
20616         }
20617         return html;
20618     },
20619
20620     /**
20621      * HTML Editor -> Textarea
20622      * Protected method that will not generally be called directly. Syncs the contents
20623      * of the editor iframe with the textarea.
20624      */
20625     syncValue : function(){
20626         if(this.initialized){
20627             var bd = (this.doc.body || this.doc.documentElement);
20628             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20629             var html = bd.innerHTML;
20630             if(Roo.isSafari){
20631                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20632                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20633                 if(m && m[1]){
20634                     html = '<div style="'+m[0]+'">' + html + '</div>';
20635                 }
20636             }
20637             html = this.cleanHtml(html);
20638             // fix up the special chars.. normaly like back quotes in word...
20639             // however we do not want to do this with chinese..
20640             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20641                 var cc = b.charCodeAt();
20642                 if (
20643                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20644                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20645                     (cc >= 0xf900 && cc < 0xfb00 )
20646                 ) {
20647                         return b;
20648                 }
20649                 return "&#"+cc+";" 
20650             });
20651             if(this.owner.fireEvent('beforesync', this, html) !== false){
20652                 this.el.dom.value = html;
20653                 this.owner.fireEvent('sync', this, html);
20654             }
20655         }
20656     },
20657
20658     /**
20659      * Protected method that will not generally be called directly. Pushes the value of the textarea
20660      * into the iframe editor.
20661      */
20662     pushValue : function(){
20663         if(this.initialized){
20664             var v = this.el.dom.value.trim();
20665             
20666 //            if(v.length < 1){
20667 //                v = '&#160;';
20668 //            }
20669             
20670             if(this.owner.fireEvent('beforepush', this, v) !== false){
20671                 var d = (this.doc.body || this.doc.documentElement);
20672                 d.innerHTML = v;
20673                 this.cleanUpPaste();
20674                 this.el.dom.value = d.innerHTML;
20675                 this.owner.fireEvent('push', this, v);
20676             }
20677         }
20678     },
20679
20680     // private
20681     deferFocus : function(){
20682         this.focus.defer(10, this);
20683     },
20684
20685     // doc'ed in Field
20686     focus : function(){
20687         if(this.win && !this.sourceEditMode){
20688             this.win.focus();
20689         }else{
20690             this.el.focus();
20691         }
20692     },
20693     
20694     assignDocWin: function()
20695     {
20696         var iframe = this.iframe;
20697         
20698          if(Roo.isIE){
20699             this.doc = iframe.contentWindow.document;
20700             this.win = iframe.contentWindow;
20701         } else {
20702 //            if (!Roo.get(this.frameId)) {
20703 //                return;
20704 //            }
20705 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20706 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20707             
20708             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20709                 return;
20710             }
20711             
20712             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20713             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20714         }
20715     },
20716     
20717     // private
20718     initEditor : function(){
20719         //console.log("INIT EDITOR");
20720         this.assignDocWin();
20721         
20722         
20723         
20724         this.doc.designMode="on";
20725         this.doc.open();
20726         this.doc.write(this.getDocMarkup());
20727         this.doc.close();
20728         
20729         var dbody = (this.doc.body || this.doc.documentElement);
20730         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20731         // this copies styles from the containing element into thsi one..
20732         // not sure why we need all of this..
20733         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20734         
20735         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20736         //ss['background-attachment'] = 'fixed'; // w3c
20737         dbody.bgProperties = 'fixed'; // ie
20738         //Roo.DomHelper.applyStyles(dbody, ss);
20739         Roo.EventManager.on(this.doc, {
20740             //'mousedown': this.onEditorEvent,
20741             'mouseup': this.onEditorEvent,
20742             'dblclick': this.onEditorEvent,
20743             'click': this.onEditorEvent,
20744             'keyup': this.onEditorEvent,
20745             buffer:100,
20746             scope: this
20747         });
20748         if(Roo.isGecko){
20749             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20750         }
20751         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20752             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20753         }
20754         this.initialized = true;
20755
20756         this.owner.fireEvent('initialize', this);
20757         this.pushValue();
20758     },
20759
20760     // private
20761     onDestroy : function(){
20762         
20763         
20764         
20765         if(this.rendered){
20766             
20767             //for (var i =0; i < this.toolbars.length;i++) {
20768             //    // fixme - ask toolbars for heights?
20769             //    this.toolbars[i].onDestroy();
20770            // }
20771             
20772             //this.wrap.dom.innerHTML = '';
20773             //this.wrap.remove();
20774         }
20775     },
20776
20777     // private
20778     onFirstFocus : function(){
20779         
20780         this.assignDocWin();
20781         
20782         
20783         this.activated = true;
20784          
20785     
20786         if(Roo.isGecko){ // prevent silly gecko errors
20787             this.win.focus();
20788             var s = this.win.getSelection();
20789             if(!s.focusNode || s.focusNode.nodeType != 3){
20790                 var r = s.getRangeAt(0);
20791                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20792                 r.collapse(true);
20793                 this.deferFocus();
20794             }
20795             try{
20796                 this.execCmd('useCSS', true);
20797                 this.execCmd('styleWithCSS', false);
20798             }catch(e){}
20799         }
20800         this.owner.fireEvent('activate', this);
20801     },
20802
20803     // private
20804     adjustFont: function(btn){
20805         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20806         //if(Roo.isSafari){ // safari
20807         //    adjust *= 2;
20808        // }
20809         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20810         if(Roo.isSafari){ // safari
20811             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20812             v =  (v < 10) ? 10 : v;
20813             v =  (v > 48) ? 48 : v;
20814             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20815             
20816         }
20817         
20818         
20819         v = Math.max(1, v+adjust);
20820         
20821         this.execCmd('FontSize', v  );
20822     },
20823
20824     onEditorEvent : function(e)
20825     {
20826         this.owner.fireEvent('editorevent', this, e);
20827       //  this.updateToolbar();
20828         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20829     },
20830
20831     insertTag : function(tg)
20832     {
20833         // could be a bit smarter... -> wrap the current selected tRoo..
20834         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20835             
20836             range = this.createRange(this.getSelection());
20837             var wrappingNode = this.doc.createElement(tg.toLowerCase());
20838             wrappingNode.appendChild(range.extractContents());
20839             range.insertNode(wrappingNode);
20840
20841             return;
20842             
20843             
20844             
20845         }
20846         this.execCmd("formatblock",   tg);
20847         
20848     },
20849     
20850     insertText : function(txt)
20851     {
20852         
20853         
20854         var range = this.createRange();
20855         range.deleteContents();
20856                //alert(Sender.getAttribute('label'));
20857                
20858         range.insertNode(this.doc.createTextNode(txt));
20859     } ,
20860     
20861      
20862
20863     /**
20864      * Executes a Midas editor command on the editor document and performs necessary focus and
20865      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20866      * @param {String} cmd The Midas command
20867      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20868      */
20869     relayCmd : function(cmd, value){
20870         this.win.focus();
20871         this.execCmd(cmd, value);
20872         this.owner.fireEvent('editorevent', this);
20873         //this.updateToolbar();
20874         this.owner.deferFocus();
20875     },
20876
20877     /**
20878      * Executes a Midas editor command directly on the editor document.
20879      * For visual commands, you should use {@link #relayCmd} instead.
20880      * <b>This should only be called after the editor is initialized.</b>
20881      * @param {String} cmd The Midas command
20882      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20883      */
20884     execCmd : function(cmd, value){
20885         this.doc.execCommand(cmd, false, value === undefined ? null : value);
20886         this.syncValue();
20887     },
20888  
20889  
20890    
20891     /**
20892      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20893      * to insert tRoo.
20894      * @param {String} text | dom node.. 
20895      */
20896     insertAtCursor : function(text)
20897     {
20898         
20899         
20900         
20901         if(!this.activated){
20902             return;
20903         }
20904         /*
20905         if(Roo.isIE){
20906             this.win.focus();
20907             var r = this.doc.selection.createRange();
20908             if(r){
20909                 r.collapse(true);
20910                 r.pasteHTML(text);
20911                 this.syncValue();
20912                 this.deferFocus();
20913             
20914             }
20915             return;
20916         }
20917         */
20918         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20919             this.win.focus();
20920             
20921             
20922             // from jquery ui (MIT licenced)
20923             var range, node;
20924             var win = this.win;
20925             
20926             if (win.getSelection && win.getSelection().getRangeAt) {
20927                 range = win.getSelection().getRangeAt(0);
20928                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20929                 range.insertNode(node);
20930             } else if (win.document.selection && win.document.selection.createRange) {
20931                 // no firefox support
20932                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20933                 win.document.selection.createRange().pasteHTML(txt);
20934             } else {
20935                 // no firefox support
20936                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20937                 this.execCmd('InsertHTML', txt);
20938             } 
20939             
20940             this.syncValue();
20941             
20942             this.deferFocus();
20943         }
20944     },
20945  // private
20946     mozKeyPress : function(e){
20947         if(e.ctrlKey){
20948             var c = e.getCharCode(), cmd;
20949           
20950             if(c > 0){
20951                 c = String.fromCharCode(c).toLowerCase();
20952                 switch(c){
20953                     case 'b':
20954                         cmd = 'bold';
20955                         break;
20956                     case 'i':
20957                         cmd = 'italic';
20958                         break;
20959                     
20960                     case 'u':
20961                         cmd = 'underline';
20962                         break;
20963                     
20964                     case 'v':
20965                         this.cleanUpPaste.defer(100, this);
20966                         return;
20967                         
20968                 }
20969                 if(cmd){
20970                     this.win.focus();
20971                     this.execCmd(cmd);
20972                     this.deferFocus();
20973                     e.preventDefault();
20974                 }
20975                 
20976             }
20977         }
20978     },
20979
20980     // private
20981     fixKeys : function(){ // load time branching for fastest keydown performance
20982         if(Roo.isIE){
20983             return function(e){
20984                 var k = e.getKey(), r;
20985                 if(k == e.TAB){
20986                     e.stopEvent();
20987                     r = this.doc.selection.createRange();
20988                     if(r){
20989                         r.collapse(true);
20990                         r.pasteHTML('&#160;&#160;&#160;&#160;');
20991                         this.deferFocus();
20992                     }
20993                     return;
20994                 }
20995                 
20996                 if(k == e.ENTER){
20997                     r = this.doc.selection.createRange();
20998                     if(r){
20999                         var target = r.parentElement();
21000                         if(!target || target.tagName.toLowerCase() != 'li'){
21001                             e.stopEvent();
21002                             r.pasteHTML('<br />');
21003                             r.collapse(false);
21004                             r.select();
21005                         }
21006                     }
21007                 }
21008                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21009                     this.cleanUpPaste.defer(100, this);
21010                     return;
21011                 }
21012                 
21013                 
21014             };
21015         }else if(Roo.isOpera){
21016             return function(e){
21017                 var k = e.getKey();
21018                 if(k == e.TAB){
21019                     e.stopEvent();
21020                     this.win.focus();
21021                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21022                     this.deferFocus();
21023                 }
21024                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21025                     this.cleanUpPaste.defer(100, this);
21026                     return;
21027                 }
21028                 
21029             };
21030         }else if(Roo.isSafari){
21031             return function(e){
21032                 var k = e.getKey();
21033                 
21034                 if(k == e.TAB){
21035                     e.stopEvent();
21036                     this.execCmd('InsertText','\t');
21037                     this.deferFocus();
21038                     return;
21039                 }
21040                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21041                     this.cleanUpPaste.defer(100, this);
21042                     return;
21043                 }
21044                 
21045              };
21046         }
21047     }(),
21048     
21049     getAllAncestors: function()
21050     {
21051         var p = this.getSelectedNode();
21052         var a = [];
21053         if (!p) {
21054             a.push(p); // push blank onto stack..
21055             p = this.getParentElement();
21056         }
21057         
21058         
21059         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21060             a.push(p);
21061             p = p.parentNode;
21062         }
21063         a.push(this.doc.body);
21064         return a;
21065     },
21066     lastSel : false,
21067     lastSelNode : false,
21068     
21069     
21070     getSelection : function() 
21071     {
21072         this.assignDocWin();
21073         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21074     },
21075     
21076     getSelectedNode: function() 
21077     {
21078         // this may only work on Gecko!!!
21079         
21080         // should we cache this!!!!
21081         
21082         
21083         
21084          
21085         var range = this.createRange(this.getSelection()).cloneRange();
21086         
21087         if (Roo.isIE) {
21088             var parent = range.parentElement();
21089             while (true) {
21090                 var testRange = range.duplicate();
21091                 testRange.moveToElementText(parent);
21092                 if (testRange.inRange(range)) {
21093                     break;
21094                 }
21095                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21096                     break;
21097                 }
21098                 parent = parent.parentElement;
21099             }
21100             return parent;
21101         }
21102         
21103         // is ancestor a text element.
21104         var ac =  range.commonAncestorContainer;
21105         if (ac.nodeType == 3) {
21106             ac = ac.parentNode;
21107         }
21108         
21109         var ar = ac.childNodes;
21110          
21111         var nodes = [];
21112         var other_nodes = [];
21113         var has_other_nodes = false;
21114         for (var i=0;i<ar.length;i++) {
21115             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21116                 continue;
21117             }
21118             // fullly contained node.
21119             
21120             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21121                 nodes.push(ar[i]);
21122                 continue;
21123             }
21124             
21125             // probably selected..
21126             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21127                 other_nodes.push(ar[i]);
21128                 continue;
21129             }
21130             // outer..
21131             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21132                 continue;
21133             }
21134             
21135             
21136             has_other_nodes = true;
21137         }
21138         if (!nodes.length && other_nodes.length) {
21139             nodes= other_nodes;
21140         }
21141         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21142             return false;
21143         }
21144         
21145         return nodes[0];
21146     },
21147     createRange: function(sel)
21148     {
21149         // this has strange effects when using with 
21150         // top toolbar - not sure if it's a great idea.
21151         //this.editor.contentWindow.focus();
21152         if (typeof sel != "undefined") {
21153             try {
21154                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21155             } catch(e) {
21156                 return this.doc.createRange();
21157             }
21158         } else {
21159             return this.doc.createRange();
21160         }
21161     },
21162     getParentElement: function()
21163     {
21164         
21165         this.assignDocWin();
21166         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21167         
21168         var range = this.createRange(sel);
21169          
21170         try {
21171             var p = range.commonAncestorContainer;
21172             while (p.nodeType == 3) { // text node
21173                 p = p.parentNode;
21174             }
21175             return p;
21176         } catch (e) {
21177             return null;
21178         }
21179     
21180     },
21181     /***
21182      *
21183      * Range intersection.. the hard stuff...
21184      *  '-1' = before
21185      *  '0' = hits..
21186      *  '1' = after.
21187      *         [ -- selected range --- ]
21188      *   [fail]                        [fail]
21189      *
21190      *    basically..
21191      *      if end is before start or  hits it. fail.
21192      *      if start is after end or hits it fail.
21193      *
21194      *   if either hits (but other is outside. - then it's not 
21195      *   
21196      *    
21197      **/
21198     
21199     
21200     // @see http://www.thismuchiknow.co.uk/?p=64.
21201     rangeIntersectsNode : function(range, node)
21202     {
21203         var nodeRange = node.ownerDocument.createRange();
21204         try {
21205             nodeRange.selectNode(node);
21206         } catch (e) {
21207             nodeRange.selectNodeContents(node);
21208         }
21209     
21210         var rangeStartRange = range.cloneRange();
21211         rangeStartRange.collapse(true);
21212     
21213         var rangeEndRange = range.cloneRange();
21214         rangeEndRange.collapse(false);
21215     
21216         var nodeStartRange = nodeRange.cloneRange();
21217         nodeStartRange.collapse(true);
21218     
21219         var nodeEndRange = nodeRange.cloneRange();
21220         nodeEndRange.collapse(false);
21221     
21222         return rangeStartRange.compareBoundaryPoints(
21223                  Range.START_TO_START, nodeEndRange) == -1 &&
21224                rangeEndRange.compareBoundaryPoints(
21225                  Range.START_TO_START, nodeStartRange) == 1;
21226         
21227          
21228     },
21229     rangeCompareNode : function(range, node)
21230     {
21231         var nodeRange = node.ownerDocument.createRange();
21232         try {
21233             nodeRange.selectNode(node);
21234         } catch (e) {
21235             nodeRange.selectNodeContents(node);
21236         }
21237         
21238         
21239         range.collapse(true);
21240     
21241         nodeRange.collapse(true);
21242      
21243         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21244         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21245          
21246         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21247         
21248         var nodeIsBefore   =  ss == 1;
21249         var nodeIsAfter    = ee == -1;
21250         
21251         if (nodeIsBefore && nodeIsAfter) {
21252             return 0; // outer
21253         }
21254         if (!nodeIsBefore && nodeIsAfter) {
21255             return 1; //right trailed.
21256         }
21257         
21258         if (nodeIsBefore && !nodeIsAfter) {
21259             return 2;  // left trailed.
21260         }
21261         // fully contined.
21262         return 3;
21263     },
21264
21265     // private? - in a new class?
21266     cleanUpPaste :  function()
21267     {
21268         // cleans up the whole document..
21269         Roo.log('cleanuppaste');
21270         
21271         this.cleanUpChildren(this.doc.body);
21272         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21273         if (clean != this.doc.body.innerHTML) {
21274             this.doc.body.innerHTML = clean;
21275         }
21276         
21277     },
21278     
21279     cleanWordChars : function(input) {// change the chars to hex code
21280         var he = Roo.HtmlEditorCore;
21281         
21282         var output = input;
21283         Roo.each(he.swapCodes, function(sw) { 
21284             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21285             
21286             output = output.replace(swapper, sw[1]);
21287         });
21288         
21289         return output;
21290     },
21291     
21292     
21293     cleanUpChildren : function (n)
21294     {
21295         if (!n.childNodes.length) {
21296             return;
21297         }
21298         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21299            this.cleanUpChild(n.childNodes[i]);
21300         }
21301     },
21302     
21303     
21304         
21305     
21306     cleanUpChild : function (node)
21307     {
21308         var ed = this;
21309         //console.log(node);
21310         if (node.nodeName == "#text") {
21311             // clean up silly Windows -- stuff?
21312             return; 
21313         }
21314         if (node.nodeName == "#comment") {
21315             node.parentNode.removeChild(node);
21316             // clean up silly Windows -- stuff?
21317             return; 
21318         }
21319         var lcname = node.tagName.toLowerCase();
21320         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21321         // whitelist of tags..
21322         
21323         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21324             // remove node.
21325             node.parentNode.removeChild(node);
21326             return;
21327             
21328         }
21329         
21330         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21331         
21332         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21333         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21334         
21335         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21336         //    remove_keep_children = true;
21337         //}
21338         
21339         if (remove_keep_children) {
21340             this.cleanUpChildren(node);
21341             // inserts everything just before this node...
21342             while (node.childNodes.length) {
21343                 var cn = node.childNodes[0];
21344                 node.removeChild(cn);
21345                 node.parentNode.insertBefore(cn, node);
21346             }
21347             node.parentNode.removeChild(node);
21348             return;
21349         }
21350         
21351         if (!node.attributes || !node.attributes.length) {
21352             this.cleanUpChildren(node);
21353             return;
21354         }
21355         
21356         function cleanAttr(n,v)
21357         {
21358             
21359             if (v.match(/^\./) || v.match(/^\//)) {
21360                 return;
21361             }
21362             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
21363                 return;
21364             }
21365             if (v.match(/^#/)) {
21366                 return;
21367             }
21368 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21369             node.removeAttribute(n);
21370             
21371         }
21372         
21373         var cwhite = this.cwhite;
21374         var cblack = this.cblack;
21375             
21376         function cleanStyle(n,v)
21377         {
21378             if (v.match(/expression/)) { //XSS?? should we even bother..
21379                 node.removeAttribute(n);
21380                 return;
21381             }
21382             
21383             var parts = v.split(/;/);
21384             var clean = [];
21385             
21386             Roo.each(parts, function(p) {
21387                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21388                 if (!p.length) {
21389                     return true;
21390                 }
21391                 var l = p.split(':').shift().replace(/\s+/g,'');
21392                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21393                 
21394                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21395 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21396                     //node.removeAttribute(n);
21397                     return true;
21398                 }
21399                 //Roo.log()
21400                 // only allow 'c whitelisted system attributes'
21401                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21402 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21403                     //node.removeAttribute(n);
21404                     return true;
21405                 }
21406                 
21407                 
21408                  
21409                 
21410                 clean.push(p);
21411                 return true;
21412             });
21413             if (clean.length) { 
21414                 node.setAttribute(n, clean.join(';'));
21415             } else {
21416                 node.removeAttribute(n);
21417             }
21418             
21419         }
21420         
21421         
21422         for (var i = node.attributes.length-1; i > -1 ; i--) {
21423             var a = node.attributes[i];
21424             //console.log(a);
21425             
21426             if (a.name.toLowerCase().substr(0,2)=='on')  {
21427                 node.removeAttribute(a.name);
21428                 continue;
21429             }
21430             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21431                 node.removeAttribute(a.name);
21432                 continue;
21433             }
21434             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21435                 cleanAttr(a.name,a.value); // fixme..
21436                 continue;
21437             }
21438             if (a.name == 'style') {
21439                 cleanStyle(a.name,a.value);
21440                 continue;
21441             }
21442             /// clean up MS crap..
21443             // tecnically this should be a list of valid class'es..
21444             
21445             
21446             if (a.name == 'class') {
21447                 if (a.value.match(/^Mso/)) {
21448                     node.className = '';
21449                 }
21450                 
21451                 if (a.value.match(/body/)) {
21452                     node.className = '';
21453                 }
21454                 continue;
21455             }
21456             
21457             // style cleanup!?
21458             // class cleanup?
21459             
21460         }
21461         
21462         
21463         this.cleanUpChildren(node);
21464         
21465         
21466     },
21467     
21468     /**
21469      * Clean up MS wordisms...
21470      */
21471     cleanWord : function(node)
21472     {
21473         
21474         
21475         if (!node) {
21476             this.cleanWord(this.doc.body);
21477             return;
21478         }
21479         if (node.nodeName == "#text") {
21480             // clean up silly Windows -- stuff?
21481             return; 
21482         }
21483         if (node.nodeName == "#comment") {
21484             node.parentNode.removeChild(node);
21485             // clean up silly Windows -- stuff?
21486             return; 
21487         }
21488         
21489         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21490             node.parentNode.removeChild(node);
21491             return;
21492         }
21493         
21494         // remove - but keep children..
21495         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21496             while (node.childNodes.length) {
21497                 var cn = node.childNodes[0];
21498                 node.removeChild(cn);
21499                 node.parentNode.insertBefore(cn, node);
21500             }
21501             node.parentNode.removeChild(node);
21502             this.iterateChildren(node, this.cleanWord);
21503             return;
21504         }
21505         // clean styles
21506         if (node.className.length) {
21507             
21508             var cn = node.className.split(/\W+/);
21509             var cna = [];
21510             Roo.each(cn, function(cls) {
21511                 if (cls.match(/Mso[a-zA-Z]+/)) {
21512                     return;
21513                 }
21514                 cna.push(cls);
21515             });
21516             node.className = cna.length ? cna.join(' ') : '';
21517             if (!cna.length) {
21518                 node.removeAttribute("class");
21519             }
21520         }
21521         
21522         if (node.hasAttribute("lang")) {
21523             node.removeAttribute("lang");
21524         }
21525         
21526         if (node.hasAttribute("style")) {
21527             
21528             var styles = node.getAttribute("style").split(";");
21529             var nstyle = [];
21530             Roo.each(styles, function(s) {
21531                 if (!s.match(/:/)) {
21532                     return;
21533                 }
21534                 var kv = s.split(":");
21535                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21536                     return;
21537                 }
21538                 // what ever is left... we allow.
21539                 nstyle.push(s);
21540             });
21541             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21542             if (!nstyle.length) {
21543                 node.removeAttribute('style');
21544             }
21545         }
21546         this.iterateChildren(node, this.cleanWord);
21547         
21548         
21549         
21550     },
21551     /**
21552      * iterateChildren of a Node, calling fn each time, using this as the scole..
21553      * @param {DomNode} node node to iterate children of.
21554      * @param {Function} fn method of this class to call on each item.
21555      */
21556     iterateChildren : function(node, fn)
21557     {
21558         if (!node.childNodes.length) {
21559                 return;
21560         }
21561         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21562            fn.call(this, node.childNodes[i])
21563         }
21564     },
21565     
21566     
21567     /**
21568      * cleanTableWidths.
21569      *
21570      * Quite often pasting from word etc.. results in tables with column and widths.
21571      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21572      *
21573      */
21574     cleanTableWidths : function(node)
21575     {
21576          
21577          
21578         if (!node) {
21579             this.cleanTableWidths(this.doc.body);
21580             return;
21581         }
21582         
21583         // ignore list...
21584         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21585             return; 
21586         }
21587         Roo.log(node.tagName);
21588         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21589             this.iterateChildren(node, this.cleanTableWidths);
21590             return;
21591         }
21592         if (node.hasAttribute('width')) {
21593             node.removeAttribute('width');
21594         }
21595         
21596          
21597         if (node.hasAttribute("style")) {
21598             // pretty basic...
21599             
21600             var styles = node.getAttribute("style").split(";");
21601             var nstyle = [];
21602             Roo.each(styles, function(s) {
21603                 if (!s.match(/:/)) {
21604                     return;
21605                 }
21606                 var kv = s.split(":");
21607                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21608                     return;
21609                 }
21610                 // what ever is left... we allow.
21611                 nstyle.push(s);
21612             });
21613             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21614             if (!nstyle.length) {
21615                 node.removeAttribute('style');
21616             }
21617         }
21618         
21619         this.iterateChildren(node, this.cleanTableWidths);
21620         
21621         
21622     },
21623     
21624     
21625     
21626     
21627     domToHTML : function(currentElement, depth, nopadtext) {
21628         
21629         depth = depth || 0;
21630         nopadtext = nopadtext || false;
21631     
21632         if (!currentElement) {
21633             return this.domToHTML(this.doc.body);
21634         }
21635         
21636         //Roo.log(currentElement);
21637         var j;
21638         var allText = false;
21639         var nodeName = currentElement.nodeName;
21640         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21641         
21642         if  (nodeName == '#text') {
21643             
21644             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21645         }
21646         
21647         
21648         var ret = '';
21649         if (nodeName != 'BODY') {
21650              
21651             var i = 0;
21652             // Prints the node tagName, such as <A>, <IMG>, etc
21653             if (tagName) {
21654                 var attr = [];
21655                 for(i = 0; i < currentElement.attributes.length;i++) {
21656                     // quoting?
21657                     var aname = currentElement.attributes.item(i).name;
21658                     if (!currentElement.attributes.item(i).value.length) {
21659                         continue;
21660                     }
21661                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21662                 }
21663                 
21664                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21665             } 
21666             else {
21667                 
21668                 // eack
21669             }
21670         } else {
21671             tagName = false;
21672         }
21673         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21674             return ret;
21675         }
21676         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21677             nopadtext = true;
21678         }
21679         
21680         
21681         // Traverse the tree
21682         i = 0;
21683         var currentElementChild = currentElement.childNodes.item(i);
21684         var allText = true;
21685         var innerHTML  = '';
21686         lastnode = '';
21687         while (currentElementChild) {
21688             // Formatting code (indent the tree so it looks nice on the screen)
21689             var nopad = nopadtext;
21690             if (lastnode == 'SPAN') {
21691                 nopad  = true;
21692             }
21693             // text
21694             if  (currentElementChild.nodeName == '#text') {
21695                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21696                 toadd = nopadtext ? toadd : toadd.trim();
21697                 if (!nopad && toadd.length > 80) {
21698                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21699                 }
21700                 innerHTML  += toadd;
21701                 
21702                 i++;
21703                 currentElementChild = currentElement.childNodes.item(i);
21704                 lastNode = '';
21705                 continue;
21706             }
21707             allText = false;
21708             
21709             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21710                 
21711             // Recursively traverse the tree structure of the child node
21712             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21713             lastnode = currentElementChild.nodeName;
21714             i++;
21715             currentElementChild=currentElement.childNodes.item(i);
21716         }
21717         
21718         ret += innerHTML;
21719         
21720         if (!allText) {
21721                 // The remaining code is mostly for formatting the tree
21722             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21723         }
21724         
21725         
21726         if (tagName) {
21727             ret+= "</"+tagName+">";
21728         }
21729         return ret;
21730         
21731     },
21732         
21733     applyBlacklists : function()
21734     {
21735         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
21736         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
21737         
21738         this.white = [];
21739         this.black = [];
21740         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21741             if (b.indexOf(tag) > -1) {
21742                 return;
21743             }
21744             this.white.push(tag);
21745             
21746         }, this);
21747         
21748         Roo.each(w, function(tag) {
21749             if (b.indexOf(tag) > -1) {
21750                 return;
21751             }
21752             if (this.white.indexOf(tag) > -1) {
21753                 return;
21754             }
21755             this.white.push(tag);
21756             
21757         }, this);
21758         
21759         
21760         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21761             if (w.indexOf(tag) > -1) {
21762                 return;
21763             }
21764             this.black.push(tag);
21765             
21766         }, this);
21767         
21768         Roo.each(b, function(tag) {
21769             if (w.indexOf(tag) > -1) {
21770                 return;
21771             }
21772             if (this.black.indexOf(tag) > -1) {
21773                 return;
21774             }
21775             this.black.push(tag);
21776             
21777         }, this);
21778         
21779         
21780         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
21781         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
21782         
21783         this.cwhite = [];
21784         this.cblack = [];
21785         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21786             if (b.indexOf(tag) > -1) {
21787                 return;
21788             }
21789             this.cwhite.push(tag);
21790             
21791         }, this);
21792         
21793         Roo.each(w, function(tag) {
21794             if (b.indexOf(tag) > -1) {
21795                 return;
21796             }
21797             if (this.cwhite.indexOf(tag) > -1) {
21798                 return;
21799             }
21800             this.cwhite.push(tag);
21801             
21802         }, this);
21803         
21804         
21805         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21806             if (w.indexOf(tag) > -1) {
21807                 return;
21808             }
21809             this.cblack.push(tag);
21810             
21811         }, this);
21812         
21813         Roo.each(b, function(tag) {
21814             if (w.indexOf(tag) > -1) {
21815                 return;
21816             }
21817             if (this.cblack.indexOf(tag) > -1) {
21818                 return;
21819             }
21820             this.cblack.push(tag);
21821             
21822         }, this);
21823     },
21824     
21825     setStylesheets : function(stylesheets)
21826     {
21827         if(typeof(stylesheets) == 'string'){
21828             Roo.get(this.iframe.contentDocument.head).createChild({
21829                 tag : 'link',
21830                 rel : 'stylesheet',
21831                 type : 'text/css',
21832                 href : stylesheets
21833             });
21834             
21835             return;
21836         }
21837         var _this = this;
21838      
21839         Roo.each(stylesheets, function(s) {
21840             if(!s.length){
21841                 return;
21842             }
21843             
21844             Roo.get(_this.iframe.contentDocument.head).createChild({
21845                 tag : 'link',
21846                 rel : 'stylesheet',
21847                 type : 'text/css',
21848                 href : s
21849             });
21850         });
21851
21852         
21853     },
21854     
21855     removeStylesheets : function()
21856     {
21857         var _this = this;
21858         
21859         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21860             s.remove();
21861         });
21862     }
21863     
21864     // hide stuff that is not compatible
21865     /**
21866      * @event blur
21867      * @hide
21868      */
21869     /**
21870      * @event change
21871      * @hide
21872      */
21873     /**
21874      * @event focus
21875      * @hide
21876      */
21877     /**
21878      * @event specialkey
21879      * @hide
21880      */
21881     /**
21882      * @cfg {String} fieldClass @hide
21883      */
21884     /**
21885      * @cfg {String} focusClass @hide
21886      */
21887     /**
21888      * @cfg {String} autoCreate @hide
21889      */
21890     /**
21891      * @cfg {String} inputType @hide
21892      */
21893     /**
21894      * @cfg {String} invalidClass @hide
21895      */
21896     /**
21897      * @cfg {String} invalidText @hide
21898      */
21899     /**
21900      * @cfg {String} msgFx @hide
21901      */
21902     /**
21903      * @cfg {String} validateOnBlur @hide
21904      */
21905 });
21906
21907 Roo.HtmlEditorCore.white = [
21908         'area', 'br', 'img', 'input', 'hr', 'wbr',
21909         
21910        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
21911        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
21912        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
21913        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
21914        'table',   'ul',         'xmp', 
21915        
21916        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
21917       'thead',   'tr', 
21918      
21919       'dir', 'menu', 'ol', 'ul', 'dl',
21920        
21921       'embed',  'object'
21922 ];
21923
21924
21925 Roo.HtmlEditorCore.black = [
21926     //    'embed',  'object', // enable - backend responsiblity to clean thiese
21927         'applet', // 
21928         'base',   'basefont', 'bgsound', 'blink',  'body', 
21929         'frame',  'frameset', 'head',    'html',   'ilayer', 
21930         'iframe', 'layer',  'link',     'meta',    'object',   
21931         'script', 'style' ,'title',  'xml' // clean later..
21932 ];
21933 Roo.HtmlEditorCore.clean = [
21934     'script', 'style', 'title', 'xml'
21935 ];
21936 Roo.HtmlEditorCore.remove = [
21937     'font'
21938 ];
21939 // attributes..
21940
21941 Roo.HtmlEditorCore.ablack = [
21942     'on'
21943 ];
21944     
21945 Roo.HtmlEditorCore.aclean = [ 
21946     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
21947 ];
21948
21949 // protocols..
21950 Roo.HtmlEditorCore.pwhite= [
21951         'http',  'https',  'mailto'
21952 ];
21953
21954 // white listed style attributes.
21955 Roo.HtmlEditorCore.cwhite= [
21956       //  'text-align', /// default is to allow most things..
21957       
21958          
21959 //        'font-size'//??
21960 ];
21961
21962 // black listed style attributes.
21963 Roo.HtmlEditorCore.cblack= [
21964       //  'font-size' -- this can be set by the project 
21965 ];
21966
21967
21968 Roo.HtmlEditorCore.swapCodes   =[ 
21969     [    8211, "--" ], 
21970     [    8212, "--" ], 
21971     [    8216,  "'" ],  
21972     [    8217, "'" ],  
21973     [    8220, '"' ],  
21974     [    8221, '"' ],  
21975     [    8226, "*" ],  
21976     [    8230, "..." ]
21977 ]; 
21978
21979     /*
21980  * - LGPL
21981  *
21982  * HtmlEditor
21983  * 
21984  */
21985
21986 /**
21987  * @class Roo.bootstrap.HtmlEditor
21988  * @extends Roo.bootstrap.TextArea
21989  * Bootstrap HtmlEditor class
21990
21991  * @constructor
21992  * Create a new HtmlEditor
21993  * @param {Object} config The config object
21994  */
21995
21996 Roo.bootstrap.HtmlEditor = function(config){
21997     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
21998     if (!this.toolbars) {
21999         this.toolbars = [];
22000     }
22001     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22002     this.addEvents({
22003             /**
22004              * @event initialize
22005              * Fires when the editor is fully initialized (including the iframe)
22006              * @param {HtmlEditor} this
22007              */
22008             initialize: true,
22009             /**
22010              * @event activate
22011              * Fires when the editor is first receives the focus. Any insertion must wait
22012              * until after this event.
22013              * @param {HtmlEditor} this
22014              */
22015             activate: true,
22016              /**
22017              * @event beforesync
22018              * Fires before the textarea is updated with content from the editor iframe. Return false
22019              * to cancel the sync.
22020              * @param {HtmlEditor} this
22021              * @param {String} html
22022              */
22023             beforesync: true,
22024              /**
22025              * @event beforepush
22026              * Fires before the iframe editor is updated with content from the textarea. Return false
22027              * to cancel the push.
22028              * @param {HtmlEditor} this
22029              * @param {String} html
22030              */
22031             beforepush: true,
22032              /**
22033              * @event sync
22034              * Fires when the textarea is updated with content from the editor iframe.
22035              * @param {HtmlEditor} this
22036              * @param {String} html
22037              */
22038             sync: true,
22039              /**
22040              * @event push
22041              * Fires when the iframe editor is updated with content from the textarea.
22042              * @param {HtmlEditor} this
22043              * @param {String} html
22044              */
22045             push: true,
22046              /**
22047              * @event editmodechange
22048              * Fires when the editor switches edit modes
22049              * @param {HtmlEditor} this
22050              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22051              */
22052             editmodechange: true,
22053             /**
22054              * @event editorevent
22055              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22056              * @param {HtmlEditor} this
22057              */
22058             editorevent: true,
22059             /**
22060              * @event firstfocus
22061              * Fires when on first focus - needed by toolbars..
22062              * @param {HtmlEditor} this
22063              */
22064             firstfocus: true,
22065             /**
22066              * @event autosave
22067              * Auto save the htmlEditor value as a file into Events
22068              * @param {HtmlEditor} this
22069              */
22070             autosave: true,
22071             /**
22072              * @event savedpreview
22073              * preview the saved version of htmlEditor
22074              * @param {HtmlEditor} this
22075              */
22076             savedpreview: true
22077         });
22078 };
22079
22080
22081 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
22082     
22083     
22084       /**
22085      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22086      */
22087     toolbars : false,
22088    
22089      /**
22090      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22091      *                        Roo.resizable.
22092      */
22093     resizable : false,
22094      /**
22095      * @cfg {Number} height (in pixels)
22096      */   
22097     height: 300,
22098    /**
22099      * @cfg {Number} width (in pixels)
22100      */   
22101     width: false,
22102     
22103     /**
22104      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22105      * 
22106      */
22107     stylesheets: false,
22108     
22109     // id of frame..
22110     frameId: false,
22111     
22112     // private properties
22113     validationEvent : false,
22114     deferHeight: true,
22115     initialized : false,
22116     activated : false,
22117     
22118     onFocus : Roo.emptyFn,
22119     iframePad:3,
22120     hideMode:'offsets',
22121     
22122     
22123     tbContainer : false,
22124     
22125     toolbarContainer :function() {
22126         return this.wrap.select('.x-html-editor-tb',true).first();
22127     },
22128
22129     /**
22130      * Protected method that will not generally be called directly. It
22131      * is called when the editor creates its toolbar. Override this method if you need to
22132      * add custom toolbar buttons.
22133      * @param {HtmlEditor} editor
22134      */
22135     createToolbar : function(){
22136         
22137         Roo.log("create toolbars");
22138         
22139         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
22140         this.toolbars[0].render(this.toolbarContainer());
22141         
22142         return;
22143         
22144 //        if (!editor.toolbars || !editor.toolbars.length) {
22145 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
22146 //        }
22147 //        
22148 //        for (var i =0 ; i < editor.toolbars.length;i++) {
22149 //            editor.toolbars[i] = Roo.factory(
22150 //                    typeof(editor.toolbars[i]) == 'string' ?
22151 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
22152 //                Roo.bootstrap.HtmlEditor);
22153 //            editor.toolbars[i].init(editor);
22154 //        }
22155     },
22156
22157      
22158     // private
22159     onRender : function(ct, position)
22160     {
22161        // Roo.log("Call onRender: " + this.xtype);
22162         var _t = this;
22163         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
22164       
22165         this.wrap = this.inputEl().wrap({
22166             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22167         });
22168         
22169         this.editorcore.onRender(ct, position);
22170          
22171         if (this.resizable) {
22172             this.resizeEl = new Roo.Resizable(this.wrap, {
22173                 pinned : true,
22174                 wrap: true,
22175                 dynamic : true,
22176                 minHeight : this.height,
22177                 height: this.height,
22178                 handles : this.resizable,
22179                 width: this.width,
22180                 listeners : {
22181                     resize : function(r, w, h) {
22182                         _t.onResize(w,h); // -something
22183                     }
22184                 }
22185             });
22186             
22187         }
22188         this.createToolbar(this);
22189        
22190         
22191         if(!this.width && this.resizable){
22192             this.setSize(this.wrap.getSize());
22193         }
22194         if (this.resizeEl) {
22195             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22196             // should trigger onReize..
22197         }
22198         
22199     },
22200
22201     // private
22202     onResize : function(w, h)
22203     {
22204         Roo.log('resize: ' +w + ',' + h );
22205         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
22206         var ew = false;
22207         var eh = false;
22208         
22209         if(this.inputEl() ){
22210             if(typeof w == 'number'){
22211                 var aw = w - this.wrap.getFrameWidth('lr');
22212                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
22213                 ew = aw;
22214             }
22215             if(typeof h == 'number'){
22216                  var tbh = -11;  // fixme it needs to tool bar size!
22217                 for (var i =0; i < this.toolbars.length;i++) {
22218                     // fixme - ask toolbars for heights?
22219                     tbh += this.toolbars[i].el.getHeight();
22220                     //if (this.toolbars[i].footer) {
22221                     //    tbh += this.toolbars[i].footer.el.getHeight();
22222                     //}
22223                 }
22224               
22225                 
22226                 
22227                 
22228                 
22229                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22230                 ah -= 5; // knock a few pixes off for look..
22231                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
22232                 var eh = ah;
22233             }
22234         }
22235         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22236         this.editorcore.onResize(ew,eh);
22237         
22238     },
22239
22240     /**
22241      * Toggles the editor between standard and source edit mode.
22242      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22243      */
22244     toggleSourceEdit : function(sourceEditMode)
22245     {
22246         this.editorcore.toggleSourceEdit(sourceEditMode);
22247         
22248         if(this.editorcore.sourceEditMode){
22249             Roo.log('editor - showing textarea');
22250             
22251 //            Roo.log('in');
22252 //            Roo.log(this.syncValue());
22253             this.syncValue();
22254             this.inputEl().removeClass(['hide', 'x-hidden']);
22255             this.inputEl().dom.removeAttribute('tabIndex');
22256             this.inputEl().focus();
22257         }else{
22258             Roo.log('editor - hiding textarea');
22259 //            Roo.log('out')
22260 //            Roo.log(this.pushValue()); 
22261             this.pushValue();
22262             
22263             this.inputEl().addClass(['hide', 'x-hidden']);
22264             this.inputEl().dom.setAttribute('tabIndex', -1);
22265             //this.deferFocus();
22266         }
22267          
22268         if(this.resizable){
22269             this.setSize(this.wrap.getSize());
22270         }
22271         
22272         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22273     },
22274  
22275     // private (for BoxComponent)
22276     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22277
22278     // private (for BoxComponent)
22279     getResizeEl : function(){
22280         return this.wrap;
22281     },
22282
22283     // private (for BoxComponent)
22284     getPositionEl : function(){
22285         return this.wrap;
22286     },
22287
22288     // private
22289     initEvents : function(){
22290         this.originalValue = this.getValue();
22291     },
22292
22293 //    /**
22294 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22295 //     * @method
22296 //     */
22297 //    markInvalid : Roo.emptyFn,
22298 //    /**
22299 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22300 //     * @method
22301 //     */
22302 //    clearInvalid : Roo.emptyFn,
22303
22304     setValue : function(v){
22305         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
22306         this.editorcore.pushValue();
22307     },
22308
22309      
22310     // private
22311     deferFocus : function(){
22312         this.focus.defer(10, this);
22313     },
22314
22315     // doc'ed in Field
22316     focus : function(){
22317         this.editorcore.focus();
22318         
22319     },
22320       
22321
22322     // private
22323     onDestroy : function(){
22324         
22325         
22326         
22327         if(this.rendered){
22328             
22329             for (var i =0; i < this.toolbars.length;i++) {
22330                 // fixme - ask toolbars for heights?
22331                 this.toolbars[i].onDestroy();
22332             }
22333             
22334             this.wrap.dom.innerHTML = '';
22335             this.wrap.remove();
22336         }
22337     },
22338
22339     // private
22340     onFirstFocus : function(){
22341         //Roo.log("onFirstFocus");
22342         this.editorcore.onFirstFocus();
22343          for (var i =0; i < this.toolbars.length;i++) {
22344             this.toolbars[i].onFirstFocus();
22345         }
22346         
22347     },
22348     
22349     // private
22350     syncValue : function()
22351     {   
22352         this.editorcore.syncValue();
22353     },
22354     
22355     pushValue : function()
22356     {   
22357         this.editorcore.pushValue();
22358     }
22359      
22360     
22361     // hide stuff that is not compatible
22362     /**
22363      * @event blur
22364      * @hide
22365      */
22366     /**
22367      * @event change
22368      * @hide
22369      */
22370     /**
22371      * @event focus
22372      * @hide
22373      */
22374     /**
22375      * @event specialkey
22376      * @hide
22377      */
22378     /**
22379      * @cfg {String} fieldClass @hide
22380      */
22381     /**
22382      * @cfg {String} focusClass @hide
22383      */
22384     /**
22385      * @cfg {String} autoCreate @hide
22386      */
22387     /**
22388      * @cfg {String} inputType @hide
22389      */
22390     /**
22391      * @cfg {String} invalidClass @hide
22392      */
22393     /**
22394      * @cfg {String} invalidText @hide
22395      */
22396     /**
22397      * @cfg {String} msgFx @hide
22398      */
22399     /**
22400      * @cfg {String} validateOnBlur @hide
22401      */
22402 });
22403  
22404     
22405    
22406    
22407    
22408       
22409 Roo.namespace('Roo.bootstrap.htmleditor');
22410 /**
22411  * @class Roo.bootstrap.HtmlEditorToolbar1
22412  * Basic Toolbar
22413  * 
22414  * Usage:
22415  *
22416  new Roo.bootstrap.HtmlEditor({
22417     ....
22418     toolbars : [
22419         new Roo.bootstrap.HtmlEditorToolbar1({
22420             disable : { fonts: 1 , format: 1, ..., ... , ...],
22421             btns : [ .... ]
22422         })
22423     }
22424      
22425  * 
22426  * @cfg {Object} disable List of elements to disable..
22427  * @cfg {Array} btns List of additional buttons.
22428  * 
22429  * 
22430  * NEEDS Extra CSS? 
22431  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22432  */
22433  
22434 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
22435 {
22436     
22437     Roo.apply(this, config);
22438     
22439     // default disabled, based on 'good practice'..
22440     this.disable = this.disable || {};
22441     Roo.applyIf(this.disable, {
22442         fontSize : true,
22443         colors : true,
22444         specialElements : true
22445     });
22446     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
22447     
22448     this.editor = config.editor;
22449     this.editorcore = config.editor.editorcore;
22450     
22451     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
22452     
22453     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22454     // dont call parent... till later.
22455 }
22456 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
22457      
22458     bar : true,
22459     
22460     editor : false,
22461     editorcore : false,
22462     
22463     
22464     formats : [
22465         "p" ,  
22466         "h1","h2","h3","h4","h5","h6", 
22467         "pre", "code", 
22468         "abbr", "acronym", "address", "cite", "samp", "var",
22469         'div','span'
22470     ],
22471     
22472     onRender : function(ct, position)
22473     {
22474        // Roo.log("Call onRender: " + this.xtype);
22475         
22476        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
22477        Roo.log(this.el);
22478        this.el.dom.style.marginBottom = '0';
22479        var _this = this;
22480        var editorcore = this.editorcore;
22481        var editor= this.editor;
22482        
22483        var children = [];
22484        var btn = function(id,cmd , toggle, handler){
22485        
22486             var  event = toggle ? 'toggle' : 'click';
22487        
22488             var a = {
22489                 size : 'sm',
22490                 xtype: 'Button',
22491                 xns: Roo.bootstrap,
22492                 glyphicon : id,
22493                 cmd : id || cmd,
22494                 enableToggle:toggle !== false,
22495                 //html : 'submit'
22496                 pressed : toggle ? false : null,
22497                 listeners : {}
22498             };
22499             a.listeners[toggle ? 'toggle' : 'click'] = function() {
22500                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
22501             };
22502             children.push(a);
22503             return a;
22504        }
22505         
22506         var style = {
22507                 xtype: 'Button',
22508                 size : 'sm',
22509                 xns: Roo.bootstrap,
22510                 glyphicon : 'font',
22511                 //html : 'submit'
22512                 menu : {
22513                     xtype: 'Menu',
22514                     xns: Roo.bootstrap,
22515                     items:  []
22516                 }
22517         };
22518         Roo.each(this.formats, function(f) {
22519             style.menu.items.push({
22520                 xtype :'MenuItem',
22521                 xns: Roo.bootstrap,
22522                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
22523                 tagname : f,
22524                 listeners : {
22525                     click : function()
22526                     {
22527                         editorcore.insertTag(this.tagname);
22528                         editor.focus();
22529                     }
22530                 }
22531                 
22532             });
22533         });
22534          children.push(style);   
22535             
22536             
22537         btn('bold',false,true);
22538         btn('italic',false,true);
22539         btn('align-left', 'justifyleft',true);
22540         btn('align-center', 'justifycenter',true);
22541         btn('align-right' , 'justifyright',true);
22542         btn('link', false, false, function(btn) {
22543             //Roo.log("create link?");
22544             var url = prompt(this.createLinkText, this.defaultLinkValue);
22545             if(url && url != 'http:/'+'/'){
22546                 this.editorcore.relayCmd('createlink', url);
22547             }
22548         }),
22549         btn('list','insertunorderedlist',true);
22550         btn('pencil', false,true, function(btn){
22551                 Roo.log(this);
22552                 
22553                 this.toggleSourceEdit(btn.pressed);
22554         });
22555         /*
22556         var cog = {
22557                 xtype: 'Button',
22558                 size : 'sm',
22559                 xns: Roo.bootstrap,
22560                 glyphicon : 'cog',
22561                 //html : 'submit'
22562                 menu : {
22563                     xtype: 'Menu',
22564                     xns: Roo.bootstrap,
22565                     items:  []
22566                 }
22567         };
22568         
22569         cog.menu.items.push({
22570             xtype :'MenuItem',
22571             xns: Roo.bootstrap,
22572             html : Clean styles,
22573             tagname : f,
22574             listeners : {
22575                 click : function()
22576                 {
22577                     editorcore.insertTag(this.tagname);
22578                     editor.focus();
22579                 }
22580             }
22581             
22582         });
22583        */
22584         
22585          
22586        this.xtype = 'NavSimplebar';
22587         
22588         for(var i=0;i< children.length;i++) {
22589             
22590             this.buttons.add(this.addxtypeChild(children[i]));
22591             
22592         }
22593         
22594         editor.on('editorevent', this.updateToolbar, this);
22595     },
22596     onBtnClick : function(id)
22597     {
22598        this.editorcore.relayCmd(id);
22599        this.editorcore.focus();
22600     },
22601     
22602     /**
22603      * Protected method that will not generally be called directly. It triggers
22604      * a toolbar update by reading the markup state of the current selection in the editor.
22605      */
22606     updateToolbar: function(){
22607
22608         if(!this.editorcore.activated){
22609             this.editor.onFirstFocus(); // is this neeed?
22610             return;
22611         }
22612
22613         var btns = this.buttons; 
22614         var doc = this.editorcore.doc;
22615         btns.get('bold').setActive(doc.queryCommandState('bold'));
22616         btns.get('italic').setActive(doc.queryCommandState('italic'));
22617         //btns.get('underline').setActive(doc.queryCommandState('underline'));
22618         
22619         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
22620         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
22621         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
22622         
22623         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
22624         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
22625          /*
22626         
22627         var ans = this.editorcore.getAllAncestors();
22628         if (this.formatCombo) {
22629             
22630             
22631             var store = this.formatCombo.store;
22632             this.formatCombo.setValue("");
22633             for (var i =0; i < ans.length;i++) {
22634                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
22635                     // select it..
22636                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
22637                     break;
22638                 }
22639             }
22640         }
22641         
22642         
22643         
22644         // hides menus... - so this cant be on a menu...
22645         Roo.bootstrap.MenuMgr.hideAll();
22646         */
22647         Roo.bootstrap.MenuMgr.hideAll();
22648         //this.editorsyncValue();
22649     },
22650     onFirstFocus: function() {
22651         this.buttons.each(function(item){
22652            item.enable();
22653         });
22654     },
22655     toggleSourceEdit : function(sourceEditMode){
22656         
22657           
22658         if(sourceEditMode){
22659             Roo.log("disabling buttons");
22660            this.buttons.each( function(item){
22661                 if(item.cmd != 'pencil'){
22662                     item.disable();
22663                 }
22664             });
22665           
22666         }else{
22667             Roo.log("enabling buttons");
22668             if(this.editorcore.initialized){
22669                 this.buttons.each( function(item){
22670                     item.enable();
22671                 });
22672             }
22673             
22674         }
22675         Roo.log("calling toggole on editor");
22676         // tell the editor that it's been pressed..
22677         this.editor.toggleSourceEdit(sourceEditMode);
22678        
22679     }
22680 });
22681
22682
22683
22684
22685
22686 /**
22687  * @class Roo.bootstrap.Table.AbstractSelectionModel
22688  * @extends Roo.util.Observable
22689  * Abstract base class for grid SelectionModels.  It provides the interface that should be
22690  * implemented by descendant classes.  This class should not be directly instantiated.
22691  * @constructor
22692  */
22693 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22694     this.locked = false;
22695     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22696 };
22697
22698
22699 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
22700     /** @ignore Called by the grid automatically. Do not call directly. */
22701     init : function(grid){
22702         this.grid = grid;
22703         this.initEvents();
22704     },
22705
22706     /**
22707      * Locks the selections.
22708      */
22709     lock : function(){
22710         this.locked = true;
22711     },
22712
22713     /**
22714      * Unlocks the selections.
22715      */
22716     unlock : function(){
22717         this.locked = false;
22718     },
22719
22720     /**
22721      * Returns true if the selections are locked.
22722      * @return {Boolean}
22723      */
22724     isLocked : function(){
22725         return this.locked;
22726     }
22727 });
22728 /**
22729  * @extends Roo.bootstrap.Table.AbstractSelectionModel
22730  * @class Roo.bootstrap.Table.RowSelectionModel
22731  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22732  * It supports multiple selections and keyboard selection/navigation. 
22733  * @constructor
22734  * @param {Object} config
22735  */
22736
22737 Roo.bootstrap.Table.RowSelectionModel = function(config){
22738     Roo.apply(this, config);
22739     this.selections = new Roo.util.MixedCollection(false, function(o){
22740         return o.id;
22741     });
22742
22743     this.last = false;
22744     this.lastActive = false;
22745
22746     this.addEvents({
22747         /**
22748              * @event selectionchange
22749              * Fires when the selection changes
22750              * @param {SelectionModel} this
22751              */
22752             "selectionchange" : true,
22753         /**
22754              * @event afterselectionchange
22755              * Fires after the selection changes (eg. by key press or clicking)
22756              * @param {SelectionModel} this
22757              */
22758             "afterselectionchange" : true,
22759         /**
22760              * @event beforerowselect
22761              * Fires when a row is selected being selected, return false to cancel.
22762              * @param {SelectionModel} this
22763              * @param {Number} rowIndex The selected index
22764              * @param {Boolean} keepExisting False if other selections will be cleared
22765              */
22766             "beforerowselect" : true,
22767         /**
22768              * @event rowselect
22769              * Fires when a row is selected.
22770              * @param {SelectionModel} this
22771              * @param {Number} rowIndex The selected index
22772              * @param {Roo.data.Record} r The record
22773              */
22774             "rowselect" : true,
22775         /**
22776              * @event rowdeselect
22777              * Fires when a row is deselected.
22778              * @param {SelectionModel} this
22779              * @param {Number} rowIndex The selected index
22780              */
22781         "rowdeselect" : true
22782     });
22783     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22784     this.locked = false;
22785  };
22786
22787 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
22788     /**
22789      * @cfg {Boolean} singleSelect
22790      * True to allow selection of only one row at a time (defaults to false)
22791      */
22792     singleSelect : false,
22793
22794     // private
22795     initEvents : function()
22796     {
22797
22798         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22799         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
22800         //}else{ // allow click to work like normal
22801          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
22802         //}
22803         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
22804         this.grid.on("rowclick", this.handleMouseDown, this);
22805         
22806         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22807             "up" : function(e){
22808                 if(!e.shiftKey){
22809                     this.selectPrevious(e.shiftKey);
22810                 }else if(this.last !== false && this.lastActive !== false){
22811                     var last = this.last;
22812                     this.selectRange(this.last,  this.lastActive-1);
22813                     this.grid.getView().focusRow(this.lastActive);
22814                     if(last !== false){
22815                         this.last = last;
22816                     }
22817                 }else{
22818                     this.selectFirstRow();
22819                 }
22820                 this.fireEvent("afterselectionchange", this);
22821             },
22822             "down" : function(e){
22823                 if(!e.shiftKey){
22824                     this.selectNext(e.shiftKey);
22825                 }else if(this.last !== false && this.lastActive !== false){
22826                     var last = this.last;
22827                     this.selectRange(this.last,  this.lastActive+1);
22828                     this.grid.getView().focusRow(this.lastActive);
22829                     if(last !== false){
22830                         this.last = last;
22831                     }
22832                 }else{
22833                     this.selectFirstRow();
22834                 }
22835                 this.fireEvent("afterselectionchange", this);
22836             },
22837             scope: this
22838         });
22839         this.grid.store.on('load', function(){
22840             this.selections.clear();
22841         },this);
22842         /*
22843         var view = this.grid.view;
22844         view.on("refresh", this.onRefresh, this);
22845         view.on("rowupdated", this.onRowUpdated, this);
22846         view.on("rowremoved", this.onRemove, this);
22847         */
22848     },
22849
22850     // private
22851     onRefresh : function()
22852     {
22853         var ds = this.grid.store, i, v = this.grid.view;
22854         var s = this.selections;
22855         s.each(function(r){
22856             if((i = ds.indexOfId(r.id)) != -1){
22857                 v.onRowSelect(i);
22858             }else{
22859                 s.remove(r);
22860             }
22861         });
22862     },
22863
22864     // private
22865     onRemove : function(v, index, r){
22866         this.selections.remove(r);
22867     },
22868
22869     // private
22870     onRowUpdated : function(v, index, r){
22871         if(this.isSelected(r)){
22872             v.onRowSelect(index);
22873         }
22874     },
22875
22876     /**
22877      * Select records.
22878      * @param {Array} records The records to select
22879      * @param {Boolean} keepExisting (optional) True to keep existing selections
22880      */
22881     selectRecords : function(records, keepExisting)
22882     {
22883         if(!keepExisting){
22884             this.clearSelections();
22885         }
22886             var ds = this.grid.store;
22887         for(var i = 0, len = records.length; i < len; i++){
22888             this.selectRow(ds.indexOf(records[i]), true);
22889         }
22890     },
22891
22892     /**
22893      * Gets the number of selected rows.
22894      * @return {Number}
22895      */
22896     getCount : function(){
22897         return this.selections.length;
22898     },
22899
22900     /**
22901      * Selects the first row in the grid.
22902      */
22903     selectFirstRow : function(){
22904         this.selectRow(0);
22905     },
22906
22907     /**
22908      * Select the last row.
22909      * @param {Boolean} keepExisting (optional) True to keep existing selections
22910      */
22911     selectLastRow : function(keepExisting){
22912         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22913         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
22914     },
22915
22916     /**
22917      * Selects the row immediately following the last selected row.
22918      * @param {Boolean} keepExisting (optional) True to keep existing selections
22919      */
22920     selectNext : function(keepExisting)
22921     {
22922             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
22923             this.selectRow(this.last+1, keepExisting);
22924             this.grid.getView().focusRow(this.last);
22925         }
22926     },
22927
22928     /**
22929      * Selects the row that precedes the last selected row.
22930      * @param {Boolean} keepExisting (optional) True to keep existing selections
22931      */
22932     selectPrevious : function(keepExisting){
22933         if(this.last){
22934             this.selectRow(this.last-1, keepExisting);
22935             this.grid.getView().focusRow(this.last);
22936         }
22937     },
22938
22939     /**
22940      * Returns the selected records
22941      * @return {Array} Array of selected records
22942      */
22943     getSelections : function(){
22944         return [].concat(this.selections.items);
22945     },
22946
22947     /**
22948      * Returns the first selected record.
22949      * @return {Record}
22950      */
22951     getSelected : function(){
22952         return this.selections.itemAt(0);
22953     },
22954
22955
22956     /**
22957      * Clears all selections.
22958      */
22959     clearSelections : function(fast)
22960     {
22961         if(this.locked) {
22962             return;
22963         }
22964         if(fast !== true){
22965                 var ds = this.grid.store;
22966             var s = this.selections;
22967             s.each(function(r){
22968                 this.deselectRow(ds.indexOfId(r.id));
22969             }, this);
22970             s.clear();
22971         }else{
22972             this.selections.clear();
22973         }
22974         this.last = false;
22975     },
22976
22977
22978     /**
22979      * Selects all rows.
22980      */
22981     selectAll : function(){
22982         if(this.locked) {
22983             return;
22984         }
22985         this.selections.clear();
22986         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
22987             this.selectRow(i, true);
22988         }
22989     },
22990
22991     /**
22992      * Returns True if there is a selection.
22993      * @return {Boolean}
22994      */
22995     hasSelection : function(){
22996         return this.selections.length > 0;
22997     },
22998
22999     /**
23000      * Returns True if the specified row is selected.
23001      * @param {Number/Record} record The record or index of the record to check
23002      * @return {Boolean}
23003      */
23004     isSelected : function(index){
23005             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23006         return (r && this.selections.key(r.id) ? true : false);
23007     },
23008
23009     /**
23010      * Returns True if the specified record id is selected.
23011      * @param {String} id The id of record to check
23012      * @return {Boolean}
23013      */
23014     isIdSelected : function(id){
23015         return (this.selections.key(id) ? true : false);
23016     },
23017
23018
23019     // private
23020     handleMouseDBClick : function(e, t){
23021         
23022     },
23023     // private
23024     handleMouseDown : function(e, t)
23025     {
23026             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23027         if(this.isLocked() || rowIndex < 0 ){
23028             return;
23029         };
23030         if(e.shiftKey && this.last !== false){
23031             var last = this.last;
23032             this.selectRange(last, rowIndex, e.ctrlKey);
23033             this.last = last; // reset the last
23034             t.focus();
23035     
23036         }else{
23037             var isSelected = this.isSelected(rowIndex);
23038             //Roo.log("select row:" + rowIndex);
23039             if(isSelected){
23040                 this.deselectRow(rowIndex);
23041             } else {
23042                         this.selectRow(rowIndex, true);
23043             }
23044     
23045             /*
23046                 if(e.button !== 0 && isSelected){
23047                 alert('rowIndex 2: ' + rowIndex);
23048                     view.focusRow(rowIndex);
23049                 }else if(e.ctrlKey && isSelected){
23050                     this.deselectRow(rowIndex);
23051                 }else if(!isSelected){
23052                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23053                     view.focusRow(rowIndex);
23054                 }
23055             */
23056         }
23057         this.fireEvent("afterselectionchange", this);
23058     },
23059     // private
23060     handleDragableRowClick :  function(grid, rowIndex, e) 
23061     {
23062         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23063             this.selectRow(rowIndex, false);
23064             grid.view.focusRow(rowIndex);
23065              this.fireEvent("afterselectionchange", this);
23066         }
23067     },
23068     
23069     /**
23070      * Selects multiple rows.
23071      * @param {Array} rows Array of the indexes of the row to select
23072      * @param {Boolean} keepExisting (optional) True to keep existing selections
23073      */
23074     selectRows : function(rows, keepExisting){
23075         if(!keepExisting){
23076             this.clearSelections();
23077         }
23078         for(var i = 0, len = rows.length; i < len; i++){
23079             this.selectRow(rows[i], true);
23080         }
23081     },
23082
23083     /**
23084      * Selects a range of rows. All rows in between startRow and endRow are also selected.
23085      * @param {Number} startRow The index of the first row in the range
23086      * @param {Number} endRow The index of the last row in the range
23087      * @param {Boolean} keepExisting (optional) True to retain existing selections
23088      */
23089     selectRange : function(startRow, endRow, keepExisting){
23090         if(this.locked) {
23091             return;
23092         }
23093         if(!keepExisting){
23094             this.clearSelections();
23095         }
23096         if(startRow <= endRow){
23097             for(var i = startRow; i <= endRow; i++){
23098                 this.selectRow(i, true);
23099             }
23100         }else{
23101             for(var i = startRow; i >= endRow; i--){
23102                 this.selectRow(i, true);
23103             }
23104         }
23105     },
23106
23107     /**
23108      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
23109      * @param {Number} startRow The index of the first row in the range
23110      * @param {Number} endRow The index of the last row in the range
23111      */
23112     deselectRange : function(startRow, endRow, preventViewNotify){
23113         if(this.locked) {
23114             return;
23115         }
23116         for(var i = startRow; i <= endRow; i++){
23117             this.deselectRow(i, preventViewNotify);
23118         }
23119     },
23120
23121     /**
23122      * Selects a row.
23123      * @param {Number} row The index of the row to select
23124      * @param {Boolean} keepExisting (optional) True to keep existing selections
23125      */
23126     selectRow : function(index, keepExisting, preventViewNotify)
23127     {
23128             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
23129             return;
23130         }
23131         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
23132             if(!keepExisting || this.singleSelect){
23133                 this.clearSelections();
23134             }
23135             
23136             var r = this.grid.store.getAt(index);
23137             //console.log('selectRow - record id :' + r.id);
23138             
23139             this.selections.add(r);
23140             this.last = this.lastActive = index;
23141             if(!preventViewNotify){
23142                 var proxy = new Roo.Element(
23143                                 this.grid.getRowDom(index)
23144                 );
23145                 proxy.addClass('bg-info info');
23146             }
23147             this.fireEvent("rowselect", this, index, r);
23148             this.fireEvent("selectionchange", this);
23149         }
23150     },
23151
23152     /**
23153      * Deselects a row.
23154      * @param {Number} row The index of the row to deselect
23155      */
23156     deselectRow : function(index, preventViewNotify)
23157     {
23158         if(this.locked) {
23159             return;
23160         }
23161         if(this.last == index){
23162             this.last = false;
23163         }
23164         if(this.lastActive == index){
23165             this.lastActive = false;
23166         }
23167         
23168         var r = this.grid.store.getAt(index);
23169         if (!r) {
23170             return;
23171         }
23172         
23173         this.selections.remove(r);
23174         //.console.log('deselectRow - record id :' + r.id);
23175         if(!preventViewNotify){
23176         
23177             var proxy = new Roo.Element(
23178                 this.grid.getRowDom(index)
23179             );
23180             proxy.removeClass('bg-info info');
23181         }
23182         this.fireEvent("rowdeselect", this, index);
23183         this.fireEvent("selectionchange", this);
23184     },
23185
23186     // private
23187     restoreLast : function(){
23188         if(this._last){
23189             this.last = this._last;
23190         }
23191     },
23192
23193     // private
23194     acceptsNav : function(row, col, cm){
23195         return !cm.isHidden(col) && cm.isCellEditable(col, row);
23196     },
23197
23198     // private
23199     onEditorKey : function(field, e){
23200         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
23201         if(k == e.TAB){
23202             e.stopEvent();
23203             ed.completeEdit();
23204             if(e.shiftKey){
23205                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
23206             }else{
23207                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
23208             }
23209         }else if(k == e.ENTER && !e.ctrlKey){
23210             e.stopEvent();
23211             ed.completeEdit();
23212             if(e.shiftKey){
23213                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
23214             }else{
23215                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
23216             }
23217         }else if(k == e.ESC){
23218             ed.cancelEdit();
23219         }
23220         if(newCell){
23221             g.startEditing(newCell[0], newCell[1]);
23222         }
23223     }
23224 });
23225 /*
23226  * Based on:
23227  * Ext JS Library 1.1.1
23228  * Copyright(c) 2006-2007, Ext JS, LLC.
23229  *
23230  * Originally Released Under LGPL - original licence link has changed is not relivant.
23231  *
23232  * Fork - LGPL
23233  * <script type="text/javascript">
23234  */
23235  
23236 /**
23237  * @class Roo.bootstrap.PagingToolbar
23238  * @extends Roo.bootstrap.NavSimplebar
23239  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
23240  * @constructor
23241  * Create a new PagingToolbar
23242  * @param {Object} config The config object
23243  * @param {Roo.data.Store} store
23244  */
23245 Roo.bootstrap.PagingToolbar = function(config)
23246 {
23247     // old args format still supported... - xtype is prefered..
23248         // created from xtype...
23249     
23250     this.ds = config.dataSource;
23251     
23252     if (config.store && !this.ds) {
23253         this.store= Roo.factory(config.store, Roo.data);
23254         this.ds = this.store;
23255         this.ds.xmodule = this.xmodule || false;
23256     }
23257     
23258     this.toolbarItems = [];
23259     if (config.items) {
23260         this.toolbarItems = config.items;
23261     }
23262     
23263     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
23264     
23265     this.cursor = 0;
23266     
23267     if (this.ds) { 
23268         this.bind(this.ds);
23269     }
23270     
23271     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
23272     
23273 };
23274
23275 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
23276     /**
23277      * @cfg {Roo.data.Store} dataSource
23278      * The underlying data store providing the paged data
23279      */
23280     /**
23281      * @cfg {String/HTMLElement/Element} container
23282      * container The id or element that will contain the toolbar
23283      */
23284     /**
23285      * @cfg {Boolean} displayInfo
23286      * True to display the displayMsg (defaults to false)
23287      */
23288     /**
23289      * @cfg {Number} pageSize
23290      * The number of records to display per page (defaults to 20)
23291      */
23292     pageSize: 20,
23293     /**
23294      * @cfg {String} displayMsg
23295      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
23296      */
23297     displayMsg : 'Displaying {0} - {1} of {2}',
23298     /**
23299      * @cfg {String} emptyMsg
23300      * The message to display when no records are found (defaults to "No data to display")
23301      */
23302     emptyMsg : 'No data to display',
23303     /**
23304      * Customizable piece of the default paging text (defaults to "Page")
23305      * @type String
23306      */
23307     beforePageText : "Page",
23308     /**
23309      * Customizable piece of the default paging text (defaults to "of %0")
23310      * @type String
23311      */
23312     afterPageText : "of {0}",
23313     /**
23314      * Customizable piece of the default paging text (defaults to "First Page")
23315      * @type String
23316      */
23317     firstText : "First Page",
23318     /**
23319      * Customizable piece of the default paging text (defaults to "Previous Page")
23320      * @type String
23321      */
23322     prevText : "Previous Page",
23323     /**
23324      * Customizable piece of the default paging text (defaults to "Next Page")
23325      * @type String
23326      */
23327     nextText : "Next Page",
23328     /**
23329      * Customizable piece of the default paging text (defaults to "Last Page")
23330      * @type String
23331      */
23332     lastText : "Last Page",
23333     /**
23334      * Customizable piece of the default paging text (defaults to "Refresh")
23335      * @type String
23336      */
23337     refreshText : "Refresh",
23338
23339     buttons : false,
23340     // private
23341     onRender : function(ct, position) 
23342     {
23343         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
23344         this.navgroup.parentId = this.id;
23345         this.navgroup.onRender(this.el, null);
23346         // add the buttons to the navgroup
23347         
23348         if(this.displayInfo){
23349             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
23350             this.displayEl = this.el.select('.x-paging-info', true).first();
23351 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
23352 //            this.displayEl = navel.el.select('span',true).first();
23353         }
23354         
23355         var _this = this;
23356         
23357         if(this.buttons){
23358             Roo.each(_this.buttons, function(e){ // this might need to use render????
23359                Roo.factory(e).onRender(_this.el, null);
23360             });
23361         }
23362             
23363         Roo.each(_this.toolbarItems, function(e) {
23364             _this.navgroup.addItem(e);
23365         });
23366         
23367         
23368         this.first = this.navgroup.addItem({
23369             tooltip: this.firstText,
23370             cls: "prev",
23371             icon : 'fa fa-backward',
23372             disabled: true,
23373             preventDefault: true,
23374             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
23375         });
23376         
23377         this.prev =  this.navgroup.addItem({
23378             tooltip: this.prevText,
23379             cls: "prev",
23380             icon : 'fa fa-step-backward',
23381             disabled: true,
23382             preventDefault: true,
23383             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
23384         });
23385     //this.addSeparator();
23386         
23387         
23388         var field = this.navgroup.addItem( {
23389             tagtype : 'span',
23390             cls : 'x-paging-position',
23391             
23392             html : this.beforePageText  +
23393                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
23394                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
23395          } ); //?? escaped?
23396         
23397         this.field = field.el.select('input', true).first();
23398         this.field.on("keydown", this.onPagingKeydown, this);
23399         this.field.on("focus", function(){this.dom.select();});
23400     
23401     
23402         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
23403         //this.field.setHeight(18);
23404         //this.addSeparator();
23405         this.next = this.navgroup.addItem({
23406             tooltip: this.nextText,
23407             cls: "next",
23408             html : ' <i class="fa fa-step-forward">',
23409             disabled: true,
23410             preventDefault: true,
23411             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
23412         });
23413         this.last = this.navgroup.addItem({
23414             tooltip: this.lastText,
23415             icon : 'fa fa-forward',
23416             cls: "next",
23417             disabled: true,
23418             preventDefault: true,
23419             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
23420         });
23421     //this.addSeparator();
23422         this.loading = this.navgroup.addItem({
23423             tooltip: this.refreshText,
23424             icon: 'fa fa-refresh',
23425             preventDefault: true,
23426             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
23427         });
23428         
23429     },
23430
23431     // private
23432     updateInfo : function(){
23433         if(this.displayEl){
23434             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
23435             var msg = count == 0 ?
23436                 this.emptyMsg :
23437                 String.format(
23438                     this.displayMsg,
23439                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
23440                 );
23441             this.displayEl.update(msg);
23442         }
23443     },
23444
23445     // private
23446     onLoad : function(ds, r, o){
23447        this.cursor = o.params ? o.params.start : 0;
23448        var d = this.getPageData(),
23449             ap = d.activePage,
23450             ps = d.pages;
23451         
23452        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
23453        this.field.dom.value = ap;
23454        this.first.setDisabled(ap == 1);
23455        this.prev.setDisabled(ap == 1);
23456        this.next.setDisabled(ap == ps);
23457        this.last.setDisabled(ap == ps);
23458        this.loading.enable();
23459        this.updateInfo();
23460     },
23461
23462     // private
23463     getPageData : function(){
23464         var total = this.ds.getTotalCount();
23465         return {
23466             total : total,
23467             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
23468             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
23469         };
23470     },
23471
23472     // private
23473     onLoadError : function(){
23474         this.loading.enable();
23475     },
23476
23477     // private
23478     onPagingKeydown : function(e){
23479         var k = e.getKey();
23480         var d = this.getPageData();
23481         if(k == e.RETURN){
23482             var v = this.field.dom.value, pageNum;
23483             if(!v || isNaN(pageNum = parseInt(v, 10))){
23484                 this.field.dom.value = d.activePage;
23485                 return;
23486             }
23487             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
23488             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23489             e.stopEvent();
23490         }
23491         else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
23492         {
23493           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
23494           this.field.dom.value = pageNum;
23495           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
23496           e.stopEvent();
23497         }
23498         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
23499         {
23500           var v = this.field.dom.value, pageNum; 
23501           var increment = (e.shiftKey) ? 10 : 1;
23502           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
23503                 increment *= -1;
23504           }
23505           if(!v || isNaN(pageNum = parseInt(v, 10))) {
23506             this.field.dom.value = d.activePage;
23507             return;
23508           }
23509           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
23510           {
23511             this.field.dom.value = parseInt(v, 10) + increment;
23512             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
23513             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23514           }
23515           e.stopEvent();
23516         }
23517     },
23518
23519     // private
23520     beforeLoad : function(){
23521         if(this.loading){
23522             this.loading.disable();
23523         }
23524     },
23525
23526     // private
23527     onClick : function(which){
23528         
23529         var ds = this.ds;
23530         if (!ds) {
23531             return;
23532         }
23533         
23534         switch(which){
23535             case "first":
23536                 ds.load({params:{start: 0, limit: this.pageSize}});
23537             break;
23538             case "prev":
23539                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
23540             break;
23541             case "next":
23542                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
23543             break;
23544             case "last":
23545                 var total = ds.getTotalCount();
23546                 var extra = total % this.pageSize;
23547                 var lastStart = extra ? (total - extra) : total-this.pageSize;
23548                 ds.load({params:{start: lastStart, limit: this.pageSize}});
23549             break;
23550             case "refresh":
23551                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
23552             break;
23553         }
23554     },
23555
23556     /**
23557      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
23558      * @param {Roo.data.Store} store The data store to unbind
23559      */
23560     unbind : function(ds){
23561         ds.un("beforeload", this.beforeLoad, this);
23562         ds.un("load", this.onLoad, this);
23563         ds.un("loadexception", this.onLoadError, this);
23564         ds.un("remove", this.updateInfo, this);
23565         ds.un("add", this.updateInfo, this);
23566         this.ds = undefined;
23567     },
23568
23569     /**
23570      * Binds the paging toolbar to the specified {@link Roo.data.Store}
23571      * @param {Roo.data.Store} store The data store to bind
23572      */
23573     bind : function(ds){
23574         ds.on("beforeload", this.beforeLoad, this);
23575         ds.on("load", this.onLoad, this);
23576         ds.on("loadexception", this.onLoadError, this);
23577         ds.on("remove", this.updateInfo, this);
23578         ds.on("add", this.updateInfo, this);
23579         this.ds = ds;
23580     }
23581 });/*
23582  * - LGPL
23583  *
23584  * element
23585  * 
23586  */
23587
23588 /**
23589  * @class Roo.bootstrap.MessageBar
23590  * @extends Roo.bootstrap.Component
23591  * Bootstrap MessageBar class
23592  * @cfg {String} html contents of the MessageBar
23593  * @cfg {String} weight (info | success | warning | danger) default info
23594  * @cfg {String} beforeClass insert the bar before the given class
23595  * @cfg {Boolean} closable (true | false) default false
23596  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
23597  * 
23598  * @constructor
23599  * Create a new Element
23600  * @param {Object} config The config object
23601  */
23602
23603 Roo.bootstrap.MessageBar = function(config){
23604     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
23605 };
23606
23607 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
23608     
23609     html: '',
23610     weight: 'info',
23611     closable: false,
23612     fixed: false,
23613     beforeClass: 'bootstrap-sticky-wrap',
23614     
23615     getAutoCreate : function(){
23616         
23617         var cfg = {
23618             tag: 'div',
23619             cls: 'alert alert-dismissable alert-' + this.weight,
23620             cn: [
23621                 {
23622                     tag: 'span',
23623                     cls: 'message',
23624                     html: this.html || ''
23625                 }
23626             ]
23627         };
23628         
23629         if(this.fixed){
23630             cfg.cls += ' alert-messages-fixed';
23631         }
23632         
23633         if(this.closable){
23634             cfg.cn.push({
23635                 tag: 'button',
23636                 cls: 'close',
23637                 html: 'x'
23638             });
23639         }
23640         
23641         return cfg;
23642     },
23643     
23644     onRender : function(ct, position)
23645     {
23646         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
23647         
23648         if(!this.el){
23649             var cfg = Roo.apply({},  this.getAutoCreate());
23650             cfg.id = Roo.id();
23651             
23652             if (this.cls) {
23653                 cfg.cls += ' ' + this.cls;
23654             }
23655             if (this.style) {
23656                 cfg.style = this.style;
23657             }
23658             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
23659             
23660             this.el.setVisibilityMode(Roo.Element.DISPLAY);
23661         }
23662         
23663         this.el.select('>button.close').on('click', this.hide, this);
23664         
23665     },
23666     
23667     show : function()
23668     {
23669         if (!this.rendered) {
23670             this.render();
23671         }
23672         
23673         this.el.show();
23674         
23675         this.fireEvent('show', this);
23676         
23677     },
23678     
23679     hide : function()
23680     {
23681         if (!this.rendered) {
23682             this.render();
23683         }
23684         
23685         this.el.hide();
23686         
23687         this.fireEvent('hide', this);
23688     },
23689     
23690     update : function()
23691     {
23692 //        var e = this.el.dom.firstChild;
23693 //        
23694 //        if(this.closable){
23695 //            e = e.nextSibling;
23696 //        }
23697 //        
23698 //        e.data = this.html || '';
23699
23700         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
23701     }
23702    
23703 });
23704
23705  
23706
23707      /*
23708  * - LGPL
23709  *
23710  * Graph
23711  * 
23712  */
23713
23714
23715 /**
23716  * @class Roo.bootstrap.Graph
23717  * @extends Roo.bootstrap.Component
23718  * Bootstrap Graph class
23719 > Prameters
23720  -sm {number} sm 4
23721  -md {number} md 5
23722  @cfg {String} graphtype  bar | vbar | pie
23723  @cfg {number} g_x coodinator | centre x (pie)
23724  @cfg {number} g_y coodinator | centre y (pie)
23725  @cfg {number} g_r radius (pie)
23726  @cfg {number} g_height height of the chart (respected by all elements in the set)
23727  @cfg {number} g_width width of the chart (respected by all elements in the set)
23728  @cfg {Object} title The title of the chart
23729     
23730  -{Array}  values
23731  -opts (object) options for the chart 
23732      o {
23733      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
23734      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
23735      o vgutter (number)
23736      o colors (array) colors be used repeatedly to plot the bars. If multicolumn bar is used each sequence of bars with use a different color.
23737      o stacked (boolean) whether or not to tread values as in a stacked bar chart
23738      o to
23739      o stretch (boolean)
23740      o }
23741  -opts (object) options for the pie
23742      o{
23743      o cut
23744      o startAngle (number)
23745      o endAngle (number)
23746      } 
23747  *
23748  * @constructor
23749  * Create a new Input
23750  * @param {Object} config The config object
23751  */
23752
23753 Roo.bootstrap.Graph = function(config){
23754     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
23755     
23756     this.addEvents({
23757         // img events
23758         /**
23759          * @event click
23760          * The img click event for the img.
23761          * @param {Roo.EventObject} e
23762          */
23763         "click" : true
23764     });
23765 };
23766
23767 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
23768     
23769     sm: 4,
23770     md: 5,
23771     graphtype: 'bar',
23772     g_height: 250,
23773     g_width: 400,
23774     g_x: 50,
23775     g_y: 50,
23776     g_r: 30,
23777     opts:{
23778         //g_colors: this.colors,
23779         g_type: 'soft',
23780         g_gutter: '20%'
23781
23782     },
23783     title : false,
23784
23785     getAutoCreate : function(){
23786         
23787         var cfg = {
23788             tag: 'div',
23789             html : null
23790         };
23791         
23792         
23793         return  cfg;
23794     },
23795
23796     onRender : function(ct,position){
23797         
23798         
23799         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23800         
23801         if (typeof(Raphael) == 'undefined') {
23802             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23803             return;
23804         }
23805         
23806         this.raphael = Raphael(this.el.dom);
23807         
23808                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23809                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23810                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23811                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23812                 /*
23813                 r.text(160, 10, "Single Series Chart").attr(txtattr);
23814                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23815                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23816                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23817                 
23818                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23819                 r.barchart(330, 10, 300, 220, data1);
23820                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23821                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23822                 */
23823                 
23824                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23825                 // r.barchart(30, 30, 560, 250,  xdata, {
23826                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23827                 //     axis : "0 0 1 1",
23828                 //     axisxlabels :  xdata
23829                 //     //yvalues : cols,
23830                    
23831                 // });
23832 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23833 //        
23834 //        this.load(null,xdata,{
23835 //                axis : "0 0 1 1",
23836 //                axisxlabels :  xdata
23837 //                });
23838
23839     },
23840
23841     load : function(graphtype,xdata,opts)
23842     {
23843         this.raphael.clear();
23844         if(!graphtype) {
23845             graphtype = this.graphtype;
23846         }
23847         if(!opts){
23848             opts = this.opts;
23849         }
23850         var r = this.raphael,
23851             fin = function () {
23852                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23853             },
23854             fout = function () {
23855                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23856             },
23857             pfin = function() {
23858                 this.sector.stop();
23859                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23860
23861                 if (this.label) {
23862                     this.label[0].stop();
23863                     this.label[0].attr({ r: 7.5 });
23864                     this.label[1].attr({ "font-weight": 800 });
23865                 }
23866             },
23867             pfout = function() {
23868                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23869
23870                 if (this.label) {
23871                     this.label[0].animate({ r: 5 }, 500, "bounce");
23872                     this.label[1].attr({ "font-weight": 400 });
23873                 }
23874             };
23875
23876         switch(graphtype){
23877             case 'bar':
23878                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23879                 break;
23880             case 'hbar':
23881                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23882                 break;
23883             case 'pie':
23884 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
23885 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23886 //            
23887                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23888                 
23889                 break;
23890
23891         }
23892         
23893         if(this.title){
23894             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23895         }
23896         
23897     },
23898     
23899     setTitle: function(o)
23900     {
23901         this.title = o;
23902     },
23903     
23904     initEvents: function() {
23905         
23906         if(!this.href){
23907             this.el.on('click', this.onClick, this);
23908         }
23909     },
23910     
23911     onClick : function(e)
23912     {
23913         Roo.log('img onclick');
23914         this.fireEvent('click', this, e);
23915     }
23916    
23917 });
23918
23919  
23920 /*
23921  * - LGPL
23922  *
23923  * numberBox
23924  * 
23925  */
23926 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23927
23928 /**
23929  * @class Roo.bootstrap.dash.NumberBox
23930  * @extends Roo.bootstrap.Component
23931  * Bootstrap NumberBox class
23932  * @cfg {String} headline Box headline
23933  * @cfg {String} content Box content
23934  * @cfg {String} icon Box icon
23935  * @cfg {String} footer Footer text
23936  * @cfg {String} fhref Footer href
23937  * 
23938  * @constructor
23939  * Create a new NumberBox
23940  * @param {Object} config The config object
23941  */
23942
23943
23944 Roo.bootstrap.dash.NumberBox = function(config){
23945     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23946     
23947 };
23948
23949 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
23950     
23951     headline : '',
23952     content : '',
23953     icon : '',
23954     footer : '',
23955     fhref : '',
23956     ficon : '',
23957     
23958     getAutoCreate : function(){
23959         
23960         var cfg = {
23961             tag : 'div',
23962             cls : 'small-box ',
23963             cn : [
23964                 {
23965                     tag : 'div',
23966                     cls : 'inner',
23967                     cn :[
23968                         {
23969                             tag : 'h3',
23970                             cls : 'roo-headline',
23971                             html : this.headline
23972                         },
23973                         {
23974                             tag : 'p',
23975                             cls : 'roo-content',
23976                             html : this.content
23977                         }
23978                     ]
23979                 }
23980             ]
23981         };
23982         
23983         if(this.icon){
23984             cfg.cn.push({
23985                 tag : 'div',
23986                 cls : 'icon',
23987                 cn :[
23988                     {
23989                         tag : 'i',
23990                         cls : 'ion ' + this.icon
23991                     }
23992                 ]
23993             });
23994         }
23995         
23996         if(this.footer){
23997             var footer = {
23998                 tag : 'a',
23999                 cls : 'small-box-footer',
24000                 href : this.fhref || '#',
24001                 html : this.footer
24002             };
24003             
24004             cfg.cn.push(footer);
24005             
24006         }
24007         
24008         return  cfg;
24009     },
24010
24011     onRender : function(ct,position){
24012         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24013
24014
24015        
24016                 
24017     },
24018
24019     setHeadline: function (value)
24020     {
24021         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24022     },
24023     
24024     setFooter: function (value, href)
24025     {
24026         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24027         
24028         if(href){
24029             this.el.select('a.small-box-footer',true).first().attr('href', href);
24030         }
24031         
24032     },
24033
24034     setContent: function (value)
24035     {
24036         this.el.select('.roo-content',true).first().dom.innerHTML = value;
24037     },
24038
24039     initEvents: function() 
24040     {   
24041         
24042     }
24043     
24044 });
24045
24046  
24047 /*
24048  * - LGPL
24049  *
24050  * TabBox
24051  * 
24052  */
24053 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24054
24055 /**
24056  * @class Roo.bootstrap.dash.TabBox
24057  * @extends Roo.bootstrap.Component
24058  * Bootstrap TabBox class
24059  * @cfg {String} title Title of the TabBox
24060  * @cfg {String} icon Icon of the TabBox
24061  * @cfg {Boolean} showtabs (true|false) show the tabs default true
24062  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24063  * 
24064  * @constructor
24065  * Create a new TabBox
24066  * @param {Object} config The config object
24067  */
24068
24069
24070 Roo.bootstrap.dash.TabBox = function(config){
24071     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24072     this.addEvents({
24073         // raw events
24074         /**
24075          * @event addpane
24076          * When a pane is added
24077          * @param {Roo.bootstrap.dash.TabPane} pane
24078          */
24079         "addpane" : true,
24080         /**
24081          * @event activatepane
24082          * When a pane is activated
24083          * @param {Roo.bootstrap.dash.TabPane} pane
24084          */
24085         "activatepane" : true
24086         
24087          
24088     });
24089     
24090     this.panes = [];
24091 };
24092
24093 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
24094
24095     title : '',
24096     icon : false,
24097     showtabs : true,
24098     tabScrollable : false,
24099     
24100     getChildContainer : function()
24101     {
24102         return this.el.select('.tab-content', true).first();
24103     },
24104     
24105     getAutoCreate : function(){
24106         
24107         var header = {
24108             tag: 'li',
24109             cls: 'pull-left header',
24110             html: this.title,
24111             cn : []
24112         };
24113         
24114         if(this.icon){
24115             header.cn.push({
24116                 tag: 'i',
24117                 cls: 'fa ' + this.icon
24118             });
24119         }
24120         
24121         var h = {
24122             tag: 'ul',
24123             cls: 'nav nav-tabs pull-right',
24124             cn: [
24125                 header
24126             ]
24127         };
24128         
24129         if(this.tabScrollable){
24130             h = {
24131                 tag: 'div',
24132                 cls: 'tab-header',
24133                 cn: [
24134                     {
24135                         tag: 'ul',
24136                         cls: 'nav nav-tabs pull-right',
24137                         cn: [
24138                             header
24139                         ]
24140                     }
24141                 ]
24142             };
24143         }
24144         
24145         var cfg = {
24146             tag: 'div',
24147             cls: 'nav-tabs-custom',
24148             cn: [
24149                 h,
24150                 {
24151                     tag: 'div',
24152                     cls: 'tab-content no-padding',
24153                     cn: []
24154                 }
24155             ]
24156         };
24157
24158         return  cfg;
24159     },
24160     initEvents : function()
24161     {
24162         //Roo.log('add add pane handler');
24163         this.on('addpane', this.onAddPane, this);
24164     },
24165      /**
24166      * Updates the box title
24167      * @param {String} html to set the title to.
24168      */
24169     setTitle : function(value)
24170     {
24171         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
24172     },
24173     onAddPane : function(pane)
24174     {
24175         this.panes.push(pane);
24176         //Roo.log('addpane');
24177         //Roo.log(pane);
24178         // tabs are rendere left to right..
24179         if(!this.showtabs){
24180             return;
24181         }
24182         
24183         var ctr = this.el.select('.nav-tabs', true).first();
24184          
24185          
24186         var existing = ctr.select('.nav-tab',true);
24187         var qty = existing.getCount();;
24188         
24189         
24190         var tab = ctr.createChild({
24191             tag : 'li',
24192             cls : 'nav-tab' + (qty ? '' : ' active'),
24193             cn : [
24194                 {
24195                     tag : 'a',
24196                     href:'#',
24197                     html : pane.title
24198                 }
24199             ]
24200         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
24201         pane.tab = tab;
24202         
24203         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
24204         if (!qty) {
24205             pane.el.addClass('active');
24206         }
24207         
24208                 
24209     },
24210     onTabClick : function(ev,un,ob,pane)
24211     {
24212         //Roo.log('tab - prev default');
24213         ev.preventDefault();
24214         
24215         
24216         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
24217         pane.tab.addClass('active');
24218         //Roo.log(pane.title);
24219         this.getChildContainer().select('.tab-pane',true).removeClass('active');
24220         // technically we should have a deactivate event.. but maybe add later.
24221         // and it should not de-activate the selected tab...
24222         this.fireEvent('activatepane', pane);
24223         pane.el.addClass('active');
24224         pane.fireEvent('activate');
24225         
24226         
24227     },
24228     
24229     getActivePane : function()
24230     {
24231         var r = false;
24232         Roo.each(this.panes, function(p) {
24233             if(p.el.hasClass('active')){
24234                 r = p;
24235                 return false;
24236             }
24237             
24238             return;
24239         });
24240         
24241         return r;
24242     }
24243     
24244     
24245 });
24246
24247  
24248 /*
24249  * - LGPL
24250  *
24251  * Tab pane
24252  * 
24253  */
24254 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24255 /**
24256  * @class Roo.bootstrap.TabPane
24257  * @extends Roo.bootstrap.Component
24258  * Bootstrap TabPane class
24259  * @cfg {Boolean} active (false | true) Default false
24260  * @cfg {String} title title of panel
24261
24262  * 
24263  * @constructor
24264  * Create a new TabPane
24265  * @param {Object} config The config object
24266  */
24267
24268 Roo.bootstrap.dash.TabPane = function(config){
24269     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
24270     
24271     this.addEvents({
24272         // raw events
24273         /**
24274          * @event activate
24275          * When a pane is activated
24276          * @param {Roo.bootstrap.dash.TabPane} pane
24277          */
24278         "activate" : true
24279          
24280     });
24281 };
24282
24283 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
24284     
24285     active : false,
24286     title : '',
24287     
24288     // the tabBox that this is attached to.
24289     tab : false,
24290      
24291     getAutoCreate : function() 
24292     {
24293         var cfg = {
24294             tag: 'div',
24295             cls: 'tab-pane'
24296         };
24297         
24298         if(this.active){
24299             cfg.cls += ' active';
24300         }
24301         
24302         return cfg;
24303     },
24304     initEvents  : function()
24305     {
24306         //Roo.log('trigger add pane handler');
24307         this.parent().fireEvent('addpane', this)
24308     },
24309     
24310      /**
24311      * Updates the tab title 
24312      * @param {String} html to set the title to.
24313      */
24314     setTitle: function(str)
24315     {
24316         if (!this.tab) {
24317             return;
24318         }
24319         this.title = str;
24320         this.tab.select('a', true).first().dom.innerHTML = str;
24321         
24322     }
24323     
24324     
24325     
24326 });
24327
24328  
24329
24330
24331  /*
24332  * - LGPL
24333  *
24334  * menu
24335  * 
24336  */
24337 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24338
24339 /**
24340  * @class Roo.bootstrap.menu.Menu
24341  * @extends Roo.bootstrap.Component
24342  * Bootstrap Menu class - container for Menu
24343  * @cfg {String} html Text of the menu
24344  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
24345  * @cfg {String} icon Font awesome icon
24346  * @cfg {String} pos Menu align to (top | bottom) default bottom
24347  * 
24348  * 
24349  * @constructor
24350  * Create a new Menu
24351  * @param {Object} config The config object
24352  */
24353
24354
24355 Roo.bootstrap.menu.Menu = function(config){
24356     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
24357     
24358     this.addEvents({
24359         /**
24360          * @event beforeshow
24361          * Fires before this menu is displayed
24362          * @param {Roo.bootstrap.menu.Menu} this
24363          */
24364         beforeshow : true,
24365         /**
24366          * @event beforehide
24367          * Fires before this menu is hidden
24368          * @param {Roo.bootstrap.menu.Menu} this
24369          */
24370         beforehide : true,
24371         /**
24372          * @event show
24373          * Fires after this menu is displayed
24374          * @param {Roo.bootstrap.menu.Menu} this
24375          */
24376         show : true,
24377         /**
24378          * @event hide
24379          * Fires after this menu is hidden
24380          * @param {Roo.bootstrap.menu.Menu} this
24381          */
24382         hide : true,
24383         /**
24384          * @event click
24385          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
24386          * @param {Roo.bootstrap.menu.Menu} this
24387          * @param {Roo.EventObject} e
24388          */
24389         click : true
24390     });
24391     
24392 };
24393
24394 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
24395     
24396     submenu : false,
24397     html : '',
24398     weight : 'default',
24399     icon : false,
24400     pos : 'bottom',
24401     
24402     
24403     getChildContainer : function() {
24404         if(this.isSubMenu){
24405             return this.el;
24406         }
24407         
24408         return this.el.select('ul.dropdown-menu', true).first();  
24409     },
24410     
24411     getAutoCreate : function()
24412     {
24413         var text = [
24414             {
24415                 tag : 'span',
24416                 cls : 'roo-menu-text',
24417                 html : this.html
24418             }
24419         ];
24420         
24421         if(this.icon){
24422             text.unshift({
24423                 tag : 'i',
24424                 cls : 'fa ' + this.icon
24425             })
24426         }
24427         
24428         
24429         var cfg = {
24430             tag : 'div',
24431             cls : 'btn-group',
24432             cn : [
24433                 {
24434                     tag : 'button',
24435                     cls : 'dropdown-button btn btn-' + this.weight,
24436                     cn : text
24437                 },
24438                 {
24439                     tag : 'button',
24440                     cls : 'dropdown-toggle btn btn-' + this.weight,
24441                     cn : [
24442                         {
24443                             tag : 'span',
24444                             cls : 'caret'
24445                         }
24446                     ]
24447                 },
24448                 {
24449                     tag : 'ul',
24450                     cls : 'dropdown-menu'
24451                 }
24452             ]
24453             
24454         };
24455         
24456         if(this.pos == 'top'){
24457             cfg.cls += ' dropup';
24458         }
24459         
24460         if(this.isSubMenu){
24461             cfg = {
24462                 tag : 'ul',
24463                 cls : 'dropdown-menu'
24464             }
24465         }
24466         
24467         return cfg;
24468     },
24469     
24470     onRender : function(ct, position)
24471     {
24472         this.isSubMenu = ct.hasClass('dropdown-submenu');
24473         
24474         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
24475     },
24476     
24477     initEvents : function() 
24478     {
24479         if(this.isSubMenu){
24480             return;
24481         }
24482         
24483         this.hidden = true;
24484         
24485         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
24486         this.triggerEl.on('click', this.onTriggerPress, this);
24487         
24488         this.buttonEl = this.el.select('button.dropdown-button', true).first();
24489         this.buttonEl.on('click', this.onClick, this);
24490         
24491     },
24492     
24493     list : function()
24494     {
24495         if(this.isSubMenu){
24496             return this.el;
24497         }
24498         
24499         return this.el.select('ul.dropdown-menu', true).first();
24500     },
24501     
24502     onClick : function(e)
24503     {
24504         this.fireEvent("click", this, e);
24505     },
24506     
24507     onTriggerPress  : function(e)
24508     {   
24509         if (this.isVisible()) {
24510             this.hide();
24511         } else {
24512             this.show();
24513         }
24514     },
24515     
24516     isVisible : function(){
24517         return !this.hidden;
24518     },
24519     
24520     show : function()
24521     {
24522         this.fireEvent("beforeshow", this);
24523         
24524         this.hidden = false;
24525         this.el.addClass('open');
24526         
24527         Roo.get(document).on("mouseup", this.onMouseUp, this);
24528         
24529         this.fireEvent("show", this);
24530         
24531         
24532     },
24533     
24534     hide : function()
24535     {
24536         this.fireEvent("beforehide", this);
24537         
24538         this.hidden = true;
24539         this.el.removeClass('open');
24540         
24541         Roo.get(document).un("mouseup", this.onMouseUp);
24542         
24543         this.fireEvent("hide", this);
24544     },
24545     
24546     onMouseUp : function()
24547     {
24548         this.hide();
24549     }
24550     
24551 });
24552
24553  
24554  /*
24555  * - LGPL
24556  *
24557  * menu item
24558  * 
24559  */
24560 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24561
24562 /**
24563  * @class Roo.bootstrap.menu.Item
24564  * @extends Roo.bootstrap.Component
24565  * Bootstrap MenuItem class
24566  * @cfg {Boolean} submenu (true | false) default false
24567  * @cfg {String} html text of the item
24568  * @cfg {String} href the link
24569  * @cfg {Boolean} disable (true | false) default false
24570  * @cfg {Boolean} preventDefault (true | false) default true
24571  * @cfg {String} icon Font awesome icon
24572  * @cfg {String} pos Submenu align to (left | right) default right 
24573  * 
24574  * 
24575  * @constructor
24576  * Create a new Item
24577  * @param {Object} config The config object
24578  */
24579
24580
24581 Roo.bootstrap.menu.Item = function(config){
24582     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
24583     this.addEvents({
24584         /**
24585          * @event mouseover
24586          * Fires when the mouse is hovering over this menu
24587          * @param {Roo.bootstrap.menu.Item} this
24588          * @param {Roo.EventObject} e
24589          */
24590         mouseover : true,
24591         /**
24592          * @event mouseout
24593          * Fires when the mouse exits this menu
24594          * @param {Roo.bootstrap.menu.Item} this
24595          * @param {Roo.EventObject} e
24596          */
24597         mouseout : true,
24598         // raw events
24599         /**
24600          * @event click
24601          * The raw click event for the entire grid.
24602          * @param {Roo.EventObject} e
24603          */
24604         click : true
24605     });
24606 };
24607
24608 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
24609     
24610     submenu : false,
24611     href : '',
24612     html : '',
24613     preventDefault: true,
24614     disable : false,
24615     icon : false,
24616     pos : 'right',
24617     
24618     getAutoCreate : function()
24619     {
24620         var text = [
24621             {
24622                 tag : 'span',
24623                 cls : 'roo-menu-item-text',
24624                 html : this.html
24625             }
24626         ];
24627         
24628         if(this.icon){
24629             text.unshift({
24630                 tag : 'i',
24631                 cls : 'fa ' + this.icon
24632             })
24633         }
24634         
24635         var cfg = {
24636             tag : 'li',
24637             cn : [
24638                 {
24639                     tag : 'a',
24640                     href : this.href || '#',
24641                     cn : text
24642                 }
24643             ]
24644         };
24645         
24646         if(this.disable){
24647             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
24648         }
24649         
24650         if(this.submenu){
24651             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
24652             
24653             if(this.pos == 'left'){
24654                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
24655             }
24656         }
24657         
24658         return cfg;
24659     },
24660     
24661     initEvents : function() 
24662     {
24663         this.el.on('mouseover', this.onMouseOver, this);
24664         this.el.on('mouseout', this.onMouseOut, this);
24665         
24666         this.el.select('a', true).first().on('click', this.onClick, this);
24667         
24668     },
24669     
24670     onClick : function(e)
24671     {
24672         if(this.preventDefault){
24673             e.preventDefault();
24674         }
24675         
24676         this.fireEvent("click", this, e);
24677     },
24678     
24679     onMouseOver : function(e)
24680     {
24681         if(this.submenu && this.pos == 'left'){
24682             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
24683         }
24684         
24685         this.fireEvent("mouseover", this, e);
24686     },
24687     
24688     onMouseOut : function(e)
24689     {
24690         this.fireEvent("mouseout", this, e);
24691     }
24692 });
24693
24694  
24695
24696  /*
24697  * - LGPL
24698  *
24699  * menu separator
24700  * 
24701  */
24702 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24703
24704 /**
24705  * @class Roo.bootstrap.menu.Separator
24706  * @extends Roo.bootstrap.Component
24707  * Bootstrap Separator class
24708  * 
24709  * @constructor
24710  * Create a new Separator
24711  * @param {Object} config The config object
24712  */
24713
24714
24715 Roo.bootstrap.menu.Separator = function(config){
24716     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
24717 };
24718
24719 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
24720     
24721     getAutoCreate : function(){
24722         var cfg = {
24723             tag : 'li',
24724             cls: 'divider'
24725         };
24726         
24727         return cfg;
24728     }
24729    
24730 });
24731
24732  
24733
24734  /*
24735  * - LGPL
24736  *
24737  * Tooltip
24738  * 
24739  */
24740
24741 /**
24742  * @class Roo.bootstrap.Tooltip
24743  * Bootstrap Tooltip class
24744  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
24745  * to determine which dom element triggers the tooltip.
24746  * 
24747  * It needs to add support for additional attributes like tooltip-position
24748  * 
24749  * @constructor
24750  * Create a new Toolti
24751  * @param {Object} config The config object
24752  */
24753
24754 Roo.bootstrap.Tooltip = function(config){
24755     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
24756 };
24757
24758 Roo.apply(Roo.bootstrap.Tooltip, {
24759     /**
24760      * @function init initialize tooltip monitoring.
24761      * @static
24762      */
24763     currentEl : false,
24764     currentTip : false,
24765     currentRegion : false,
24766     
24767     //  init : delay?
24768     
24769     init : function()
24770     {
24771         Roo.get(document).on('mouseover', this.enter ,this);
24772         Roo.get(document).on('mouseout', this.leave, this);
24773          
24774         
24775         this.currentTip = new Roo.bootstrap.Tooltip();
24776     },
24777     
24778     enter : function(ev)
24779     {
24780         var dom = ev.getTarget();
24781         
24782         //Roo.log(['enter',dom]);
24783         var el = Roo.fly(dom);
24784         if (this.currentEl) {
24785             //Roo.log(dom);
24786             //Roo.log(this.currentEl);
24787             //Roo.log(this.currentEl.contains(dom));
24788             if (this.currentEl == el) {
24789                 return;
24790             }
24791             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24792                 return;
24793             }
24794
24795         }
24796         
24797         if (this.currentTip.el) {
24798             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24799         }    
24800         //Roo.log(ev);
24801         
24802         if(!el || el.dom == document){
24803             return;
24804         }
24805         
24806         var bindEl = el;
24807         
24808         // you can not look for children, as if el is the body.. then everythign is the child..
24809         if (!el.attr('tooltip')) { //
24810             if (!el.select("[tooltip]").elements.length) {
24811                 return;
24812             }
24813             // is the mouse over this child...?
24814             bindEl = el.select("[tooltip]").first();
24815             var xy = ev.getXY();
24816             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24817                 //Roo.log("not in region.");
24818                 return;
24819             }
24820             //Roo.log("child element over..");
24821             
24822         }
24823         this.currentEl = bindEl;
24824         this.currentTip.bind(bindEl);
24825         this.currentRegion = Roo.lib.Region.getRegion(dom);
24826         this.currentTip.enter();
24827         
24828     },
24829     leave : function(ev)
24830     {
24831         var dom = ev.getTarget();
24832         //Roo.log(['leave',dom]);
24833         if (!this.currentEl) {
24834             return;
24835         }
24836         
24837         
24838         if (dom != this.currentEl.dom) {
24839             return;
24840         }
24841         var xy = ev.getXY();
24842         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
24843             return;
24844         }
24845         // only activate leave if mouse cursor is outside... bounding box..
24846         
24847         
24848         
24849         
24850         if (this.currentTip) {
24851             this.currentTip.leave();
24852         }
24853         //Roo.log('clear currentEl');
24854         this.currentEl = false;
24855         
24856         
24857     },
24858     alignment : {
24859         'left' : ['r-l', [-2,0], 'right'],
24860         'right' : ['l-r', [2,0], 'left'],
24861         'bottom' : ['t-b', [0,2], 'top'],
24862         'top' : [ 'b-t', [0,-2], 'bottom']
24863     }
24864     
24865 });
24866
24867
24868 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
24869     
24870     
24871     bindEl : false,
24872     
24873     delay : null, // can be { show : 300 , hide: 500}
24874     
24875     timeout : null,
24876     
24877     hoverState : null, //???
24878     
24879     placement : 'bottom', 
24880     
24881     getAutoCreate : function(){
24882     
24883         var cfg = {
24884            cls : 'tooltip',
24885            role : 'tooltip',
24886            cn : [
24887                 {
24888                     cls : 'tooltip-arrow'
24889                 },
24890                 {
24891                     cls : 'tooltip-inner'
24892                 }
24893            ]
24894         };
24895         
24896         return cfg;
24897     },
24898     bind : function(el)
24899     {
24900         this.bindEl = el;
24901     },
24902       
24903     
24904     enter : function () {
24905        
24906         if (this.timeout != null) {
24907             clearTimeout(this.timeout);
24908         }
24909         
24910         this.hoverState = 'in';
24911          //Roo.log("enter - show");
24912         if (!this.delay || !this.delay.show) {
24913             this.show();
24914             return;
24915         }
24916         var _t = this;
24917         this.timeout = setTimeout(function () {
24918             if (_t.hoverState == 'in') {
24919                 _t.show();
24920             }
24921         }, this.delay.show);
24922     },
24923     leave : function()
24924     {
24925         clearTimeout(this.timeout);
24926     
24927         this.hoverState = 'out';
24928          if (!this.delay || !this.delay.hide) {
24929             this.hide();
24930             return;
24931         }
24932        
24933         var _t = this;
24934         this.timeout = setTimeout(function () {
24935             //Roo.log("leave - timeout");
24936             
24937             if (_t.hoverState == 'out') {
24938                 _t.hide();
24939                 Roo.bootstrap.Tooltip.currentEl = false;
24940             }
24941         }, delay);
24942     },
24943     
24944     show : function ()
24945     {
24946         if (!this.el) {
24947             this.render(document.body);
24948         }
24949         // set content.
24950         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24951         
24952         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24953         
24954         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24955         
24956         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24957         
24958         var placement = typeof this.placement == 'function' ?
24959             this.placement.call(this, this.el, on_el) :
24960             this.placement;
24961             
24962         var autoToken = /\s?auto?\s?/i;
24963         var autoPlace = autoToken.test(placement);
24964         if (autoPlace) {
24965             placement = placement.replace(autoToken, '') || 'top';
24966         }
24967         
24968         //this.el.detach()
24969         //this.el.setXY([0,0]);
24970         this.el.show();
24971         //this.el.dom.style.display='block';
24972         
24973         //this.el.appendTo(on_el);
24974         
24975         var p = this.getPosition();
24976         var box = this.el.getBox();
24977         
24978         if (autoPlace) {
24979             // fixme..
24980         }
24981         
24982         var align = Roo.bootstrap.Tooltip.alignment[placement];
24983         
24984         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
24985         
24986         if(placement == 'top' || placement == 'bottom'){
24987             if(xy[0] < 0){
24988                 placement = 'right';
24989             }
24990             
24991             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
24992                 placement = 'left';
24993             }
24994             
24995             var scroll = Roo.select('body', true).first().getScroll();
24996             
24997             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
24998                 placement = 'top';
24999             }
25000             
25001         }
25002         
25003         align = Roo.bootstrap.Tooltip.alignment[placement];
25004         
25005         this.el.alignTo(this.bindEl, align[0],align[1]);
25006         //var arrow = this.el.select('.arrow',true).first();
25007         //arrow.set(align[2], 
25008         
25009         this.el.addClass(placement);
25010         
25011         this.el.addClass('in fade');
25012         
25013         this.hoverState = null;
25014         
25015         if (this.el.hasClass('fade')) {
25016             // fade it?
25017         }
25018         
25019     },
25020     hide : function()
25021     {
25022          
25023         if (!this.el) {
25024             return;
25025         }
25026         //this.el.setXY([0,0]);
25027         this.el.removeClass('in');
25028         //this.el.hide();
25029         
25030     }
25031     
25032 });
25033  
25034
25035  /*
25036  * - LGPL
25037  *
25038  * Location Picker
25039  * 
25040  */
25041
25042 /**
25043  * @class Roo.bootstrap.LocationPicker
25044  * @extends Roo.bootstrap.Component
25045  * Bootstrap LocationPicker class
25046  * @cfg {Number} latitude Position when init default 0
25047  * @cfg {Number} longitude Position when init default 0
25048  * @cfg {Number} zoom default 15
25049  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25050  * @cfg {Boolean} mapTypeControl default false
25051  * @cfg {Boolean} disableDoubleClickZoom default false
25052  * @cfg {Boolean} scrollwheel default true
25053  * @cfg {Boolean} streetViewControl default false
25054  * @cfg {Number} radius default 0
25055  * @cfg {String} locationName
25056  * @cfg {Boolean} draggable default true
25057  * @cfg {Boolean} enableAutocomplete default false
25058  * @cfg {Boolean} enableReverseGeocode default true
25059  * @cfg {String} markerTitle
25060  * 
25061  * @constructor
25062  * Create a new LocationPicker
25063  * @param {Object} config The config object
25064  */
25065
25066
25067 Roo.bootstrap.LocationPicker = function(config){
25068     
25069     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25070     
25071     this.addEvents({
25072         /**
25073          * @event initial
25074          * Fires when the picker initialized.
25075          * @param {Roo.bootstrap.LocationPicker} this
25076          * @param {Google Location} location
25077          */
25078         initial : true,
25079         /**
25080          * @event positionchanged
25081          * Fires when the picker position changed.
25082          * @param {Roo.bootstrap.LocationPicker} this
25083          * @param {Google Location} location
25084          */
25085         positionchanged : true,
25086         /**
25087          * @event resize
25088          * Fires when the map resize.
25089          * @param {Roo.bootstrap.LocationPicker} this
25090          */
25091         resize : true,
25092         /**
25093          * @event show
25094          * Fires when the map show.
25095          * @param {Roo.bootstrap.LocationPicker} this
25096          */
25097         show : true,
25098         /**
25099          * @event hide
25100          * Fires when the map hide.
25101          * @param {Roo.bootstrap.LocationPicker} this
25102          */
25103         hide : true,
25104         /**
25105          * @event mapClick
25106          * Fires when click the map.
25107          * @param {Roo.bootstrap.LocationPicker} this
25108          * @param {Map event} e
25109          */
25110         mapClick : true,
25111         /**
25112          * @event mapRightClick
25113          * Fires when right click the map.
25114          * @param {Roo.bootstrap.LocationPicker} this
25115          * @param {Map event} e
25116          */
25117         mapRightClick : true,
25118         /**
25119          * @event markerClick
25120          * Fires when click the marker.
25121          * @param {Roo.bootstrap.LocationPicker} this
25122          * @param {Map event} e
25123          */
25124         markerClick : true,
25125         /**
25126          * @event markerRightClick
25127          * Fires when right click the marker.
25128          * @param {Roo.bootstrap.LocationPicker} this
25129          * @param {Map event} e
25130          */
25131         markerRightClick : true,
25132         /**
25133          * @event OverlayViewDraw
25134          * Fires when OverlayView Draw
25135          * @param {Roo.bootstrap.LocationPicker} this
25136          */
25137         OverlayViewDraw : true,
25138         /**
25139          * @event OverlayViewOnAdd
25140          * Fires when OverlayView Draw
25141          * @param {Roo.bootstrap.LocationPicker} this
25142          */
25143         OverlayViewOnAdd : true,
25144         /**
25145          * @event OverlayViewOnRemove
25146          * Fires when OverlayView Draw
25147          * @param {Roo.bootstrap.LocationPicker} this
25148          */
25149         OverlayViewOnRemove : true,
25150         /**
25151          * @event OverlayViewShow
25152          * Fires when OverlayView Draw
25153          * @param {Roo.bootstrap.LocationPicker} this
25154          * @param {Pixel} cpx
25155          */
25156         OverlayViewShow : true,
25157         /**
25158          * @event OverlayViewHide
25159          * Fires when OverlayView Draw
25160          * @param {Roo.bootstrap.LocationPicker} this
25161          */
25162         OverlayViewHide : true,
25163         /**
25164          * @event loadexception
25165          * Fires when load google lib failed.
25166          * @param {Roo.bootstrap.LocationPicker} this
25167          */
25168         loadexception : true
25169     });
25170         
25171 };
25172
25173 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
25174     
25175     gMapContext: false,
25176     
25177     latitude: 0,
25178     longitude: 0,
25179     zoom: 15,
25180     mapTypeId: false,
25181     mapTypeControl: false,
25182     disableDoubleClickZoom: false,
25183     scrollwheel: true,
25184     streetViewControl: false,
25185     radius: 0,
25186     locationName: '',
25187     draggable: true,
25188     enableAutocomplete: false,
25189     enableReverseGeocode: true,
25190     markerTitle: '',
25191     
25192     getAutoCreate: function()
25193     {
25194
25195         var cfg = {
25196             tag: 'div',
25197             cls: 'roo-location-picker'
25198         };
25199         
25200         return cfg
25201     },
25202     
25203     initEvents: function(ct, position)
25204     {       
25205         if(!this.el.getWidth() || this.isApplied()){
25206             return;
25207         }
25208         
25209         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25210         
25211         this.initial();
25212     },
25213     
25214     initial: function()
25215     {
25216         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
25217             this.fireEvent('loadexception', this);
25218             return;
25219         }
25220         
25221         if(!this.mapTypeId){
25222             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
25223         }
25224         
25225         this.gMapContext = this.GMapContext();
25226         
25227         this.initOverlayView();
25228         
25229         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
25230         
25231         var _this = this;
25232                 
25233         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
25234             _this.setPosition(_this.gMapContext.marker.position);
25235         });
25236         
25237         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
25238             _this.fireEvent('mapClick', this, event);
25239             
25240         });
25241
25242         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
25243             _this.fireEvent('mapRightClick', this, event);
25244             
25245         });
25246         
25247         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
25248             _this.fireEvent('markerClick', this, event);
25249             
25250         });
25251
25252         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
25253             _this.fireEvent('markerRightClick', this, event);
25254             
25255         });
25256         
25257         this.setPosition(this.gMapContext.location);
25258         
25259         this.fireEvent('initial', this, this.gMapContext.location);
25260     },
25261     
25262     initOverlayView: function()
25263     {
25264         var _this = this;
25265         
25266         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
25267             
25268             draw: function()
25269             {
25270                 _this.fireEvent('OverlayViewDraw', _this);
25271             },
25272             
25273             onAdd: function()
25274             {
25275                 _this.fireEvent('OverlayViewOnAdd', _this);
25276             },
25277             
25278             onRemove: function()
25279             {
25280                 _this.fireEvent('OverlayViewOnRemove', _this);
25281             },
25282             
25283             show: function(cpx)
25284             {
25285                 _this.fireEvent('OverlayViewShow', _this, cpx);
25286             },
25287             
25288             hide: function()
25289             {
25290                 _this.fireEvent('OverlayViewHide', _this);
25291             }
25292             
25293         });
25294     },
25295     
25296     fromLatLngToContainerPixel: function(event)
25297     {
25298         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
25299     },
25300     
25301     isApplied: function() 
25302     {
25303         return this.getGmapContext() == false ? false : true;
25304     },
25305     
25306     getGmapContext: function() 
25307     {
25308         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
25309     },
25310     
25311     GMapContext: function() 
25312     {
25313         var position = new google.maps.LatLng(this.latitude, this.longitude);
25314         
25315         var _map = new google.maps.Map(this.el.dom, {
25316             center: position,
25317             zoom: this.zoom,
25318             mapTypeId: this.mapTypeId,
25319             mapTypeControl: this.mapTypeControl,
25320             disableDoubleClickZoom: this.disableDoubleClickZoom,
25321             scrollwheel: this.scrollwheel,
25322             streetViewControl: this.streetViewControl,
25323             locationName: this.locationName,
25324             draggable: this.draggable,
25325             enableAutocomplete: this.enableAutocomplete,
25326             enableReverseGeocode: this.enableReverseGeocode
25327         });
25328         
25329         var _marker = new google.maps.Marker({
25330             position: position,
25331             map: _map,
25332             title: this.markerTitle,
25333             draggable: this.draggable
25334         });
25335         
25336         return {
25337             map: _map,
25338             marker: _marker,
25339             circle: null,
25340             location: position,
25341             radius: this.radius,
25342             locationName: this.locationName,
25343             addressComponents: {
25344                 formatted_address: null,
25345                 addressLine1: null,
25346                 addressLine2: null,
25347                 streetName: null,
25348                 streetNumber: null,
25349                 city: null,
25350                 district: null,
25351                 state: null,
25352                 stateOrProvince: null
25353             },
25354             settings: this,
25355             domContainer: this.el.dom,
25356             geodecoder: new google.maps.Geocoder()
25357         };
25358     },
25359     
25360     drawCircle: function(center, radius, options) 
25361     {
25362         if (this.gMapContext.circle != null) {
25363             this.gMapContext.circle.setMap(null);
25364         }
25365         if (radius > 0) {
25366             radius *= 1;
25367             options = Roo.apply({}, options, {
25368                 strokeColor: "#0000FF",
25369                 strokeOpacity: .35,
25370                 strokeWeight: 2,
25371                 fillColor: "#0000FF",
25372                 fillOpacity: .2
25373             });
25374             
25375             options.map = this.gMapContext.map;
25376             options.radius = radius;
25377             options.center = center;
25378             this.gMapContext.circle = new google.maps.Circle(options);
25379             return this.gMapContext.circle;
25380         }
25381         
25382         return null;
25383     },
25384     
25385     setPosition: function(location) 
25386     {
25387         this.gMapContext.location = location;
25388         this.gMapContext.marker.setPosition(location);
25389         this.gMapContext.map.panTo(location);
25390         this.drawCircle(location, this.gMapContext.radius, {});
25391         
25392         var _this = this;
25393         
25394         if (this.gMapContext.settings.enableReverseGeocode) {
25395             this.gMapContext.geodecoder.geocode({
25396                 latLng: this.gMapContext.location
25397             }, function(results, status) {
25398                 
25399                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
25400                     _this.gMapContext.locationName = results[0].formatted_address;
25401                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
25402                     
25403                     _this.fireEvent('positionchanged', this, location);
25404                 }
25405             });
25406             
25407             return;
25408         }
25409         
25410         this.fireEvent('positionchanged', this, location);
25411     },
25412     
25413     resize: function()
25414     {
25415         google.maps.event.trigger(this.gMapContext.map, "resize");
25416         
25417         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
25418         
25419         this.fireEvent('resize', this);
25420     },
25421     
25422     setPositionByLatLng: function(latitude, longitude)
25423     {
25424         this.setPosition(new google.maps.LatLng(latitude, longitude));
25425     },
25426     
25427     getCurrentPosition: function() 
25428     {
25429         return {
25430             latitude: this.gMapContext.location.lat(),
25431             longitude: this.gMapContext.location.lng()
25432         };
25433     },
25434     
25435     getAddressName: function() 
25436     {
25437         return this.gMapContext.locationName;
25438     },
25439     
25440     getAddressComponents: function() 
25441     {
25442         return this.gMapContext.addressComponents;
25443     },
25444     
25445     address_component_from_google_geocode: function(address_components) 
25446     {
25447         var result = {};
25448         
25449         for (var i = 0; i < address_components.length; i++) {
25450             var component = address_components[i];
25451             if (component.types.indexOf("postal_code") >= 0) {
25452                 result.postalCode = component.short_name;
25453             } else if (component.types.indexOf("street_number") >= 0) {
25454                 result.streetNumber = component.short_name;
25455             } else if (component.types.indexOf("route") >= 0) {
25456                 result.streetName = component.short_name;
25457             } else if (component.types.indexOf("neighborhood") >= 0) {
25458                 result.city = component.short_name;
25459             } else if (component.types.indexOf("locality") >= 0) {
25460                 result.city = component.short_name;
25461             } else if (component.types.indexOf("sublocality") >= 0) {
25462                 result.district = component.short_name;
25463             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
25464                 result.stateOrProvince = component.short_name;
25465             } else if (component.types.indexOf("country") >= 0) {
25466                 result.country = component.short_name;
25467             }
25468         }
25469         
25470         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
25471         result.addressLine2 = "";
25472         return result;
25473     },
25474     
25475     setZoomLevel: function(zoom)
25476     {
25477         this.gMapContext.map.setZoom(zoom);
25478     },
25479     
25480     show: function()
25481     {
25482         if(!this.el){
25483             return;
25484         }
25485         
25486         this.el.show();
25487         
25488         this.resize();
25489         
25490         this.fireEvent('show', this);
25491     },
25492     
25493     hide: function()
25494     {
25495         if(!this.el){
25496             return;
25497         }
25498         
25499         this.el.hide();
25500         
25501         this.fireEvent('hide', this);
25502     }
25503     
25504 });
25505
25506 Roo.apply(Roo.bootstrap.LocationPicker, {
25507     
25508     OverlayView : function(map, options)
25509     {
25510         options = options || {};
25511         
25512         this.setMap(map);
25513     }
25514     
25515     
25516 });/*
25517  * - LGPL
25518  *
25519  * Alert
25520  * 
25521  */
25522
25523 /**
25524  * @class Roo.bootstrap.Alert
25525  * @extends Roo.bootstrap.Component
25526  * Bootstrap Alert class
25527  * @cfg {String} title The title of alert
25528  * @cfg {String} html The content of alert
25529  * @cfg {String} weight (  success | info | warning | danger )
25530  * @cfg {String} faicon font-awesomeicon
25531  * 
25532  * @constructor
25533  * Create a new alert
25534  * @param {Object} config The config object
25535  */
25536
25537
25538 Roo.bootstrap.Alert = function(config){
25539     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
25540     
25541 };
25542
25543 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
25544     
25545     title: '',
25546     html: '',
25547     weight: false,
25548     faicon: false,
25549     
25550     getAutoCreate : function()
25551     {
25552         
25553         var cfg = {
25554             tag : 'div',
25555             cls : 'alert',
25556             cn : [
25557                 {
25558                     tag : 'i',
25559                     cls : 'roo-alert-icon'
25560                     
25561                 },
25562                 {
25563                     tag : 'b',
25564                     cls : 'roo-alert-title',
25565                     html : this.title
25566                 },
25567                 {
25568                     tag : 'span',
25569                     cls : 'roo-alert-text',
25570                     html : this.html
25571                 }
25572             ]
25573         };
25574         
25575         if(this.faicon){
25576             cfg.cn[0].cls += ' fa ' + this.faicon;
25577         }
25578         
25579         if(this.weight){
25580             cfg.cls += ' alert-' + this.weight;
25581         }
25582         
25583         return cfg;
25584     },
25585     
25586     initEvents: function() 
25587     {
25588         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25589     },
25590     
25591     setTitle : function(str)
25592     {
25593         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
25594     },
25595     
25596     setText : function(str)
25597     {
25598         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
25599     },
25600     
25601     setWeight : function(weight)
25602     {
25603         if(this.weight){
25604             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
25605         }
25606         
25607         this.weight = weight;
25608         
25609         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
25610     },
25611     
25612     setIcon : function(icon)
25613     {
25614         if(this.faicon){
25615             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
25616         }
25617         
25618         this.faicon = icon;
25619         
25620         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
25621     },
25622     
25623     hide: function() 
25624     {
25625         this.el.hide();   
25626     },
25627     
25628     show: function() 
25629     {  
25630         this.el.show();   
25631     }
25632     
25633 });
25634
25635  
25636 /*
25637 * Licence: LGPL
25638 */
25639
25640 /**
25641  * @class Roo.bootstrap.UploadCropbox
25642  * @extends Roo.bootstrap.Component
25643  * Bootstrap UploadCropbox class
25644  * @cfg {String} emptyText show when image has been loaded
25645  * @cfg {String} rotateNotify show when image too small to rotate
25646  * @cfg {Number} errorTimeout default 3000
25647  * @cfg {Number} minWidth default 300
25648  * @cfg {Number} minHeight default 300
25649  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
25650  * @cfg {Boolean} isDocument (true|false) default false
25651  * @cfg {String} url action url
25652  * @cfg {String} paramName default 'imageUpload'
25653  * @cfg {String} method default POST
25654  * @cfg {Boolean} loadMask (true|false) default true
25655  * @cfg {Boolean} loadingText default 'Loading...'
25656  * 
25657  * @constructor
25658  * Create a new UploadCropbox
25659  * @param {Object} config The config object
25660  */
25661
25662 Roo.bootstrap.UploadCropbox = function(config){
25663     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
25664     
25665     this.addEvents({
25666         /**
25667          * @event beforeselectfile
25668          * Fire before select file
25669          * @param {Roo.bootstrap.UploadCropbox} this
25670          */
25671         "beforeselectfile" : true,
25672         /**
25673          * @event initial
25674          * Fire after initEvent
25675          * @param {Roo.bootstrap.UploadCropbox} this
25676          */
25677         "initial" : true,
25678         /**
25679          * @event crop
25680          * Fire after initEvent
25681          * @param {Roo.bootstrap.UploadCropbox} this
25682          * @param {String} data
25683          */
25684         "crop" : true,
25685         /**
25686          * @event prepare
25687          * Fire when preparing the file data
25688          * @param {Roo.bootstrap.UploadCropbox} this
25689          * @param {Object} file
25690          */
25691         "prepare" : true,
25692         /**
25693          * @event exception
25694          * Fire when get exception
25695          * @param {Roo.bootstrap.UploadCropbox} this
25696          * @param {XMLHttpRequest} xhr
25697          */
25698         "exception" : true,
25699         /**
25700          * @event beforeloadcanvas
25701          * Fire before load the canvas
25702          * @param {Roo.bootstrap.UploadCropbox} this
25703          * @param {String} src
25704          */
25705         "beforeloadcanvas" : true,
25706         /**
25707          * @event trash
25708          * Fire when trash image
25709          * @param {Roo.bootstrap.UploadCropbox} this
25710          */
25711         "trash" : true,
25712         /**
25713          * @event download
25714          * Fire when download the image
25715          * @param {Roo.bootstrap.UploadCropbox} this
25716          */
25717         "download" : true,
25718         /**
25719          * @event footerbuttonclick
25720          * Fire when footerbuttonclick
25721          * @param {Roo.bootstrap.UploadCropbox} this
25722          * @param {String} type
25723          */
25724         "footerbuttonclick" : true,
25725         /**
25726          * @event resize
25727          * Fire when resize
25728          * @param {Roo.bootstrap.UploadCropbox} this
25729          */
25730         "resize" : true,
25731         /**
25732          * @event rotate
25733          * Fire when rotate the image
25734          * @param {Roo.bootstrap.UploadCropbox} this
25735          * @param {String} pos
25736          */
25737         "rotate" : true,
25738         /**
25739          * @event inspect
25740          * Fire when inspect the file
25741          * @param {Roo.bootstrap.UploadCropbox} this
25742          * @param {Object} file
25743          */
25744         "inspect" : true,
25745         /**
25746          * @event upload
25747          * Fire when xhr upload the file
25748          * @param {Roo.bootstrap.UploadCropbox} this
25749          * @param {Object} data
25750          */
25751         "upload" : true,
25752         /**
25753          * @event arrange
25754          * Fire when arrange the file data
25755          * @param {Roo.bootstrap.UploadCropbox} this
25756          * @param {Object} formData
25757          */
25758         "arrange" : true
25759     });
25760     
25761     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25762 };
25763
25764 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
25765     
25766     emptyText : 'Click to upload image',
25767     rotateNotify : 'Image is too small to rotate',
25768     errorTimeout : 3000,
25769     scale : 0,
25770     baseScale : 1,
25771     rotate : 0,
25772     dragable : false,
25773     pinching : false,
25774     mouseX : 0,
25775     mouseY : 0,
25776     cropData : false,
25777     minWidth : 300,
25778     minHeight : 300,
25779     file : false,
25780     exif : {},
25781     baseRotate : 1,
25782     cropType : 'image/jpeg',
25783     buttons : false,
25784     canvasLoaded : false,
25785     isDocument : false,
25786     method : 'POST',
25787     paramName : 'imageUpload',
25788     loadMask : true,
25789     loadingText : 'Loading...',
25790     maskEl : false,
25791     
25792     getAutoCreate : function()
25793     {
25794         var cfg = {
25795             tag : 'div',
25796             cls : 'roo-upload-cropbox',
25797             cn : [
25798                 {
25799                     tag : 'input',
25800                     cls : 'roo-upload-cropbox-selector',
25801                     type : 'file'
25802                 },
25803                 {
25804                     tag : 'div',
25805                     cls : 'roo-upload-cropbox-body',
25806                     style : 'cursor:pointer',
25807                     cn : [
25808                         {
25809                             tag : 'div',
25810                             cls : 'roo-upload-cropbox-preview'
25811                         },
25812                         {
25813                             tag : 'div',
25814                             cls : 'roo-upload-cropbox-thumb'
25815                         },
25816                         {
25817                             tag : 'div',
25818                             cls : 'roo-upload-cropbox-empty-notify',
25819                             html : this.emptyText
25820                         },
25821                         {
25822                             tag : 'div',
25823                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25824                             html : this.rotateNotify
25825                         }
25826                     ]
25827                 },
25828                 {
25829                     tag : 'div',
25830                     cls : 'roo-upload-cropbox-footer',
25831                     cn : {
25832                         tag : 'div',
25833                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25834                         cn : []
25835                     }
25836                 }
25837             ]
25838         };
25839         
25840         return cfg;
25841     },
25842     
25843     onRender : function(ct, position)
25844     {
25845         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25846         
25847         if (this.buttons.length) {
25848             
25849             Roo.each(this.buttons, function(bb) {
25850                 
25851                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25852                 
25853                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25854                 
25855             }, this);
25856         }
25857         
25858         if(this.loadMask){
25859             this.maskEl = this.el;
25860         }
25861     },
25862     
25863     initEvents : function()
25864     {
25865         this.urlAPI = (window.createObjectURL && window) || 
25866                                 (window.URL && URL.revokeObjectURL && URL) || 
25867                                 (window.webkitURL && webkitURL);
25868                         
25869         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25870         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25871         
25872         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25873         this.selectorEl.hide();
25874         
25875         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25876         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25877         
25878         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25879         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25880         this.thumbEl.hide();
25881         
25882         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25883         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25884         
25885         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25886         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25887         this.errorEl.hide();
25888         
25889         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25890         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25891         this.footerEl.hide();
25892         
25893         this.setThumbBoxSize();
25894         
25895         this.bind();
25896         
25897         this.resize();
25898         
25899         this.fireEvent('initial', this);
25900     },
25901
25902     bind : function()
25903     {
25904         var _this = this;
25905         
25906         window.addEventListener("resize", function() { _this.resize(); } );
25907         
25908         this.bodyEl.on('click', this.beforeSelectFile, this);
25909         
25910         if(Roo.isTouch){
25911             this.bodyEl.on('touchstart', this.onTouchStart, this);
25912             this.bodyEl.on('touchmove', this.onTouchMove, this);
25913             this.bodyEl.on('touchend', this.onTouchEnd, this);
25914         }
25915         
25916         if(!Roo.isTouch){
25917             this.bodyEl.on('mousedown', this.onMouseDown, this);
25918             this.bodyEl.on('mousemove', this.onMouseMove, this);
25919             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25920             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25921             Roo.get(document).on('mouseup', this.onMouseUp, this);
25922         }
25923         
25924         this.selectorEl.on('change', this.onFileSelected, this);
25925     },
25926     
25927     reset : function()
25928     {    
25929         this.scale = 0;
25930         this.baseScale = 1;
25931         this.rotate = 0;
25932         this.baseRotate = 1;
25933         this.dragable = false;
25934         this.pinching = false;
25935         this.mouseX = 0;
25936         this.mouseY = 0;
25937         this.cropData = false;
25938         this.notifyEl.dom.innerHTML = this.emptyText;
25939         
25940         this.selectorEl.dom.value = '';
25941         
25942     },
25943     
25944     resize : function()
25945     {
25946         if(this.fireEvent('resize', this) != false){
25947             this.setThumbBoxPosition();
25948             this.setCanvasPosition();
25949         }
25950     },
25951     
25952     onFooterButtonClick : function(e, el, o, type)
25953     {
25954         switch (type) {
25955             case 'rotate-left' :
25956                 this.onRotateLeft(e);
25957                 break;
25958             case 'rotate-right' :
25959                 this.onRotateRight(e);
25960                 break;
25961             case 'picture' :
25962                 this.beforeSelectFile(e);
25963                 break;
25964             case 'trash' :
25965                 this.trash(e);
25966                 break;
25967             case 'crop' :
25968                 this.crop(e);
25969                 break;
25970             case 'download' :
25971                 this.download(e);
25972                 break;
25973             default :
25974                 break;
25975         }
25976         
25977         this.fireEvent('footerbuttonclick', this, type);
25978     },
25979     
25980     beforeSelectFile : function(e)
25981     {
25982         e.preventDefault();
25983         
25984         if(this.fireEvent('beforeselectfile', this) != false){
25985             this.selectorEl.dom.click();
25986         }
25987     },
25988     
25989     onFileSelected : function(e)
25990     {
25991         e.preventDefault();
25992         
25993         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25994             return;
25995         }
25996         
25997         var file = this.selectorEl.dom.files[0];
25998         
25999         if(this.fireEvent('inspect', this, file) != false){
26000             this.prepare(file);
26001         }
26002         
26003     },
26004     
26005     trash : function(e)
26006     {
26007         this.fireEvent('trash', this);
26008     },
26009     
26010     download : function(e)
26011     {
26012         this.fireEvent('download', this);
26013     },
26014     
26015     loadCanvas : function(src)
26016     {   
26017         if(this.fireEvent('beforeloadcanvas', this, src) != false){
26018             
26019             this.reset();
26020             
26021             this.imageEl = document.createElement('img');
26022             
26023             var _this = this;
26024             
26025             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26026             
26027             this.imageEl.src = src;
26028         }
26029     },
26030     
26031     onLoadCanvas : function()
26032     {   
26033         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26034         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26035         
26036         this.bodyEl.un('click', this.beforeSelectFile, this);
26037         
26038         this.notifyEl.hide();
26039         this.thumbEl.show();
26040         this.footerEl.show();
26041         
26042         this.baseRotateLevel();
26043         
26044         if(this.isDocument){
26045             this.setThumbBoxSize();
26046         }
26047         
26048         this.setThumbBoxPosition();
26049         
26050         this.baseScaleLevel();
26051         
26052         this.draw();
26053         
26054         this.resize();
26055         
26056         this.canvasLoaded = true;
26057         
26058         if(this.loadMask){
26059             this.maskEl.unmask();
26060         }
26061         
26062     },
26063     
26064     setCanvasPosition : function()
26065     {   
26066         if(!this.canvasEl){
26067             return;
26068         }
26069         
26070         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26071         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26072         
26073         this.previewEl.setLeft(pw);
26074         this.previewEl.setTop(ph);
26075         
26076     },
26077     
26078     onMouseDown : function(e)
26079     {   
26080         e.stopEvent();
26081         
26082         this.dragable = true;
26083         this.pinching = false;
26084         
26085         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26086             this.dragable = false;
26087             return;
26088         }
26089         
26090         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26091         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26092         
26093     },
26094     
26095     onMouseMove : function(e)
26096     {   
26097         e.stopEvent();
26098         
26099         if(!this.canvasLoaded){
26100             return;
26101         }
26102         
26103         if (!this.dragable){
26104             return;
26105         }
26106         
26107         var minX = Math.ceil(this.thumbEl.getLeft(true));
26108         var minY = Math.ceil(this.thumbEl.getTop(true));
26109         
26110         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
26111         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
26112         
26113         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26114         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26115         
26116         x = x - this.mouseX;
26117         y = y - this.mouseY;
26118         
26119         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
26120         var bgY = Math.ceil(y + this.previewEl.getTop(true));
26121         
26122         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
26123         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
26124         
26125         this.previewEl.setLeft(bgX);
26126         this.previewEl.setTop(bgY);
26127         
26128         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26129         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26130     },
26131     
26132     onMouseUp : function(e)
26133     {   
26134         e.stopEvent();
26135         
26136         this.dragable = false;
26137     },
26138     
26139     onMouseWheel : function(e)
26140     {   
26141         e.stopEvent();
26142         
26143         this.startScale = this.scale;
26144         
26145         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
26146         
26147         if(!this.zoomable()){
26148             this.scale = this.startScale;
26149             return;
26150         }
26151         
26152         this.draw();
26153         
26154         return;
26155     },
26156     
26157     zoomable : function()
26158     {
26159         var minScale = this.thumbEl.getWidth() / this.minWidth;
26160         
26161         if(this.minWidth < this.minHeight){
26162             minScale = this.thumbEl.getHeight() / this.minHeight;
26163         }
26164         
26165         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
26166         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
26167         
26168         if(
26169                 this.isDocument &&
26170                 (this.rotate == 0 || this.rotate == 180) && 
26171                 (
26172                     width > this.imageEl.OriginWidth || 
26173                     height > this.imageEl.OriginHeight ||
26174                     (width < this.minWidth && height < this.minHeight)
26175                 )
26176         ){
26177             return false;
26178         }
26179         
26180         if(
26181                 this.isDocument &&
26182                 (this.rotate == 90 || this.rotate == 270) && 
26183                 (
26184                     width > this.imageEl.OriginWidth || 
26185                     height > this.imageEl.OriginHeight ||
26186                     (width < this.minHeight && height < this.minWidth)
26187                 )
26188         ){
26189             return false;
26190         }
26191         
26192         if(
26193                 !this.isDocument &&
26194                 (this.rotate == 0 || this.rotate == 180) && 
26195                 (
26196                     width < this.minWidth || 
26197                     width > this.imageEl.OriginWidth || 
26198                     height < this.minHeight || 
26199                     height > this.imageEl.OriginHeight
26200                 )
26201         ){
26202             return false;
26203         }
26204         
26205         if(
26206                 !this.isDocument &&
26207                 (this.rotate == 90 || this.rotate == 270) && 
26208                 (
26209                     width < this.minHeight || 
26210                     width > this.imageEl.OriginWidth || 
26211                     height < this.minWidth || 
26212                     height > this.imageEl.OriginHeight
26213                 )
26214         ){
26215             return false;
26216         }
26217         
26218         return true;
26219         
26220     },
26221     
26222     onRotateLeft : function(e)
26223     {   
26224         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26225             
26226             var minScale = this.thumbEl.getWidth() / this.minWidth;
26227             
26228             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26229             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26230             
26231             this.startScale = this.scale;
26232             
26233             while (this.getScaleLevel() < minScale){
26234             
26235                 this.scale = this.scale + 1;
26236                 
26237                 if(!this.zoomable()){
26238                     break;
26239                 }
26240                 
26241                 if(
26242                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26243                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26244                 ){
26245                     continue;
26246                 }
26247                 
26248                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26249
26250                 this.draw();
26251                 
26252                 return;
26253             }
26254             
26255             this.scale = this.startScale;
26256             
26257             this.onRotateFail();
26258             
26259             return false;
26260         }
26261         
26262         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26263
26264         if(this.isDocument){
26265             this.setThumbBoxSize();
26266             this.setThumbBoxPosition();
26267             this.setCanvasPosition();
26268         }
26269         
26270         this.draw();
26271         
26272         this.fireEvent('rotate', this, 'left');
26273         
26274     },
26275     
26276     onRotateRight : function(e)
26277     {
26278         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26279             
26280             var minScale = this.thumbEl.getWidth() / this.minWidth;
26281         
26282             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26283             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26284             
26285             this.startScale = this.scale;
26286             
26287             while (this.getScaleLevel() < minScale){
26288             
26289                 this.scale = this.scale + 1;
26290                 
26291                 if(!this.zoomable()){
26292                     break;
26293                 }
26294                 
26295                 if(
26296                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26297                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26298                 ){
26299                     continue;
26300                 }
26301                 
26302                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26303
26304                 this.draw();
26305                 
26306                 return;
26307             }
26308             
26309             this.scale = this.startScale;
26310             
26311             this.onRotateFail();
26312             
26313             return false;
26314         }
26315         
26316         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26317
26318         if(this.isDocument){
26319             this.setThumbBoxSize();
26320             this.setThumbBoxPosition();
26321             this.setCanvasPosition();
26322         }
26323         
26324         this.draw();
26325         
26326         this.fireEvent('rotate', this, 'right');
26327     },
26328     
26329     onRotateFail : function()
26330     {
26331         this.errorEl.show(true);
26332         
26333         var _this = this;
26334         
26335         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
26336     },
26337     
26338     draw : function()
26339     {
26340         this.previewEl.dom.innerHTML = '';
26341         
26342         var canvasEl = document.createElement("canvas");
26343         
26344         var contextEl = canvasEl.getContext("2d");
26345         
26346         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26347         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26348         var center = this.imageEl.OriginWidth / 2;
26349         
26350         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
26351             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26352             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26353             center = this.imageEl.OriginHeight / 2;
26354         }
26355         
26356         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
26357         
26358         contextEl.translate(center, center);
26359         contextEl.rotate(this.rotate * Math.PI / 180);
26360
26361         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26362         
26363         this.canvasEl = document.createElement("canvas");
26364         
26365         this.contextEl = this.canvasEl.getContext("2d");
26366         
26367         switch (this.rotate) {
26368             case 0 :
26369                 
26370                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26371                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26372                 
26373                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26374                 
26375                 break;
26376             case 90 : 
26377                 
26378                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26379                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26380                 
26381                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26382                     this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26383                     break;
26384                 }
26385                 
26386                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26387                 
26388                 break;
26389             case 180 :
26390                 
26391                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26392                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26393                 
26394                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26395                     this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26396                     break;
26397                 }
26398                 
26399                 this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26400                 
26401                 break;
26402             case 270 :
26403                 
26404                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26405                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26406         
26407                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26408                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26409                     break;
26410                 }
26411                 
26412                 this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26413                 
26414                 break;
26415             default : 
26416                 break;
26417         }
26418         
26419         this.previewEl.appendChild(this.canvasEl);
26420         
26421         this.setCanvasPosition();
26422     },
26423     
26424     crop : function()
26425     {
26426         if(!this.canvasLoaded){
26427             return;
26428         }
26429         
26430         var imageCanvas = document.createElement("canvas");
26431         
26432         var imageContext = imageCanvas.getContext("2d");
26433         
26434         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26435         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26436         
26437         var center = imageCanvas.width / 2;
26438         
26439         imageContext.translate(center, center);
26440         
26441         imageContext.rotate(this.rotate * Math.PI / 180);
26442         
26443         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26444         
26445         var canvas = document.createElement("canvas");
26446         
26447         var context = canvas.getContext("2d");
26448                 
26449         canvas.width = this.minWidth;
26450         canvas.height = this.minHeight;
26451
26452         switch (this.rotate) {
26453             case 0 :
26454                 
26455                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26456                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26457                 
26458                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26459                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26460                 
26461                 var targetWidth = this.minWidth - 2 * x;
26462                 var targetHeight = this.minHeight - 2 * y;
26463                 
26464                 var scale = 1;
26465                 
26466                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26467                     scale = targetWidth / width;
26468                 }
26469                 
26470                 if(x > 0 && y == 0){
26471                     scale = targetHeight / height;
26472                 }
26473                 
26474                 if(x > 0 && y > 0){
26475                     scale = targetWidth / width;
26476                     
26477                     if(width < height){
26478                         scale = targetHeight / height;
26479                     }
26480                 }
26481                 
26482                 context.scale(scale, scale);
26483                 
26484                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26485                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26486
26487                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26488                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26489
26490                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26491                 
26492                 break;
26493             case 90 : 
26494                 
26495                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26496                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26497                 
26498                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26499                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26500                 
26501                 var targetWidth = this.minWidth - 2 * x;
26502                 var targetHeight = this.minHeight - 2 * y;
26503                 
26504                 var scale = 1;
26505                 
26506                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26507                     scale = targetWidth / width;
26508                 }
26509                 
26510                 if(x > 0 && y == 0){
26511                     scale = targetHeight / height;
26512                 }
26513                 
26514                 if(x > 0 && y > 0){
26515                     scale = targetWidth / width;
26516                     
26517                     if(width < height){
26518                         scale = targetHeight / height;
26519                     }
26520                 }
26521                 
26522                 context.scale(scale, scale);
26523                 
26524                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26525                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26526
26527                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26528                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26529                 
26530                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26531                 
26532                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26533                 
26534                 break;
26535             case 180 :
26536                 
26537                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26538                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26539                 
26540                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26541                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26542                 
26543                 var targetWidth = this.minWidth - 2 * x;
26544                 var targetHeight = this.minHeight - 2 * y;
26545                 
26546                 var scale = 1;
26547                 
26548                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26549                     scale = targetWidth / width;
26550                 }
26551                 
26552                 if(x > 0 && y == 0){
26553                     scale = targetHeight / height;
26554                 }
26555                 
26556                 if(x > 0 && y > 0){
26557                     scale = targetWidth / width;
26558                     
26559                     if(width < height){
26560                         scale = targetHeight / height;
26561                     }
26562                 }
26563                 
26564                 context.scale(scale, scale);
26565                 
26566                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26567                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26568
26569                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26570                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26571
26572                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26573                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26574                 
26575                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26576                 
26577                 break;
26578             case 270 :
26579                 
26580                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26581                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26582                 
26583                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26584                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26585                 
26586                 var targetWidth = this.minWidth - 2 * x;
26587                 var targetHeight = this.minHeight - 2 * y;
26588                 
26589                 var scale = 1;
26590                 
26591                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26592                     scale = targetWidth / width;
26593                 }
26594                 
26595                 if(x > 0 && y == 0){
26596                     scale = targetHeight / height;
26597                 }
26598                 
26599                 if(x > 0 && y > 0){
26600                     scale = targetWidth / width;
26601                     
26602                     if(width < height){
26603                         scale = targetHeight / height;
26604                     }
26605                 }
26606                 
26607                 context.scale(scale, scale);
26608                 
26609                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26610                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26611
26612                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26613                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26614                 
26615                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26616                 
26617                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26618                 
26619                 break;
26620             default : 
26621                 break;
26622         }
26623         
26624         this.cropData = canvas.toDataURL(this.cropType);
26625         
26626         if(this.fireEvent('crop', this, this.cropData) !== false){
26627             this.process(this.file, this.cropData);
26628         }
26629         
26630         return;
26631         
26632     },
26633     
26634     setThumbBoxSize : function()
26635     {
26636         var width, height;
26637         
26638         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
26639             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
26640             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
26641             
26642             this.minWidth = width;
26643             this.minHeight = height;
26644             
26645             if(this.rotate == 90 || this.rotate == 270){
26646                 this.minWidth = height;
26647                 this.minHeight = width;
26648             }
26649         }
26650         
26651         height = 300;
26652         width = Math.ceil(this.minWidth * height / this.minHeight);
26653         
26654         if(this.minWidth > this.minHeight){
26655             width = 300;
26656             height = Math.ceil(this.minHeight * width / this.minWidth);
26657         }
26658         
26659         this.thumbEl.setStyle({
26660             width : width + 'px',
26661             height : height + 'px'
26662         });
26663
26664         return;
26665             
26666     },
26667     
26668     setThumbBoxPosition : function()
26669     {
26670         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
26671         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
26672         
26673         this.thumbEl.setLeft(x);
26674         this.thumbEl.setTop(y);
26675         
26676     },
26677     
26678     baseRotateLevel : function()
26679     {
26680         this.baseRotate = 1;
26681         
26682         if(
26683                 typeof(this.exif) != 'undefined' &&
26684                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
26685                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
26686         ){
26687             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
26688         }
26689         
26690         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
26691         
26692     },
26693     
26694     baseScaleLevel : function()
26695     {
26696         var width, height;
26697         
26698         if(this.isDocument){
26699             
26700             if(this.baseRotate == 6 || this.baseRotate == 8){
26701             
26702                 height = this.thumbEl.getHeight();
26703                 this.baseScale = height / this.imageEl.OriginWidth;
26704
26705                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
26706                     width = this.thumbEl.getWidth();
26707                     this.baseScale = width / this.imageEl.OriginHeight;
26708                 }
26709
26710                 return;
26711             }
26712
26713             height = this.thumbEl.getHeight();
26714             this.baseScale = height / this.imageEl.OriginHeight;
26715
26716             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
26717                 width = this.thumbEl.getWidth();
26718                 this.baseScale = width / this.imageEl.OriginWidth;
26719             }
26720
26721             return;
26722         }
26723         
26724         if(this.baseRotate == 6 || this.baseRotate == 8){
26725             
26726             width = this.thumbEl.getHeight();
26727             this.baseScale = width / this.imageEl.OriginHeight;
26728             
26729             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
26730                 height = this.thumbEl.getWidth();
26731                 this.baseScale = height / this.imageEl.OriginHeight;
26732             }
26733             
26734             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26735                 height = this.thumbEl.getWidth();
26736                 this.baseScale = height / this.imageEl.OriginHeight;
26737                 
26738                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
26739                     width = this.thumbEl.getHeight();
26740                     this.baseScale = width / this.imageEl.OriginWidth;
26741                 }
26742             }
26743             
26744             return;
26745         }
26746         
26747         width = this.thumbEl.getWidth();
26748         this.baseScale = width / this.imageEl.OriginWidth;
26749         
26750         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
26751             height = this.thumbEl.getHeight();
26752             this.baseScale = height / this.imageEl.OriginHeight;
26753         }
26754         
26755         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26756             
26757             height = this.thumbEl.getHeight();
26758             this.baseScale = height / this.imageEl.OriginHeight;
26759             
26760             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26761                 width = this.thumbEl.getWidth();
26762                 this.baseScale = width / this.imageEl.OriginWidth;
26763             }
26764             
26765         }
26766         
26767         return;
26768     },
26769     
26770     getScaleLevel : function()
26771     {
26772         return this.baseScale * Math.pow(1.1, this.scale);
26773     },
26774     
26775     onTouchStart : function(e)
26776     {
26777         if(!this.canvasLoaded){
26778             this.beforeSelectFile(e);
26779             return;
26780         }
26781         
26782         var touches = e.browserEvent.touches;
26783         
26784         if(!touches){
26785             return;
26786         }
26787         
26788         if(touches.length == 1){
26789             this.onMouseDown(e);
26790             return;
26791         }
26792         
26793         if(touches.length != 2){
26794             return;
26795         }
26796         
26797         var coords = [];
26798         
26799         for(var i = 0, finger; finger = touches[i]; i++){
26800             coords.push(finger.pageX, finger.pageY);
26801         }
26802         
26803         var x = Math.pow(coords[0] - coords[2], 2);
26804         var y = Math.pow(coords[1] - coords[3], 2);
26805         
26806         this.startDistance = Math.sqrt(x + y);
26807         
26808         this.startScale = this.scale;
26809         
26810         this.pinching = true;
26811         this.dragable = false;
26812         
26813     },
26814     
26815     onTouchMove : function(e)
26816     {
26817         if(!this.pinching && !this.dragable){
26818             return;
26819         }
26820         
26821         var touches = e.browserEvent.touches;
26822         
26823         if(!touches){
26824             return;
26825         }
26826         
26827         if(this.dragable){
26828             this.onMouseMove(e);
26829             return;
26830         }
26831         
26832         var coords = [];
26833         
26834         for(var i = 0, finger; finger = touches[i]; i++){
26835             coords.push(finger.pageX, finger.pageY);
26836         }
26837         
26838         var x = Math.pow(coords[0] - coords[2], 2);
26839         var y = Math.pow(coords[1] - coords[3], 2);
26840         
26841         this.endDistance = Math.sqrt(x + y);
26842         
26843         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26844         
26845         if(!this.zoomable()){
26846             this.scale = this.startScale;
26847             return;
26848         }
26849         
26850         this.draw();
26851         
26852     },
26853     
26854     onTouchEnd : function(e)
26855     {
26856         this.pinching = false;
26857         this.dragable = false;
26858         
26859     },
26860     
26861     process : function(file, crop)
26862     {
26863         if(this.loadMask){
26864             this.maskEl.mask(this.loadingText);
26865         }
26866         
26867         this.xhr = new XMLHttpRequest();
26868         
26869         file.xhr = this.xhr;
26870
26871         this.xhr.open(this.method, this.url, true);
26872         
26873         var headers = {
26874             "Accept": "application/json",
26875             "Cache-Control": "no-cache",
26876             "X-Requested-With": "XMLHttpRequest"
26877         };
26878         
26879         for (var headerName in headers) {
26880             var headerValue = headers[headerName];
26881             if (headerValue) {
26882                 this.xhr.setRequestHeader(headerName, headerValue);
26883             }
26884         }
26885         
26886         var _this = this;
26887         
26888         this.xhr.onload = function()
26889         {
26890             _this.xhrOnLoad(_this.xhr);
26891         }
26892         
26893         this.xhr.onerror = function()
26894         {
26895             _this.xhrOnError(_this.xhr);
26896         }
26897         
26898         var formData = new FormData();
26899
26900         formData.append('returnHTML', 'NO');
26901         
26902         if(crop){
26903             formData.append('crop', crop);
26904         }
26905         
26906         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26907             formData.append(this.paramName, file, file.name);
26908         }
26909         
26910         if(typeof(file.filename) != 'undefined'){
26911             formData.append('filename', file.filename);
26912         }
26913         
26914         if(typeof(file.mimetype) != 'undefined'){
26915             formData.append('mimetype', file.mimetype);
26916         }
26917         
26918         if(this.fireEvent('arrange', this, formData) != false){
26919             this.xhr.send(formData);
26920         };
26921     },
26922     
26923     xhrOnLoad : function(xhr)
26924     {
26925         if(this.loadMask){
26926             this.maskEl.unmask();
26927         }
26928         
26929         if (xhr.readyState !== 4) {
26930             this.fireEvent('exception', this, xhr);
26931             return;
26932         }
26933
26934         var response = Roo.decode(xhr.responseText);
26935         
26936         if(!response.success){
26937             this.fireEvent('exception', this, xhr);
26938             return;
26939         }
26940         
26941         var response = Roo.decode(xhr.responseText);
26942         
26943         this.fireEvent('upload', this, response);
26944         
26945     },
26946     
26947     xhrOnError : function()
26948     {
26949         if(this.loadMask){
26950             this.maskEl.unmask();
26951         }
26952         
26953         Roo.log('xhr on error');
26954         
26955         var response = Roo.decode(xhr.responseText);
26956           
26957         Roo.log(response);
26958         
26959     },
26960     
26961     prepare : function(file)
26962     {   
26963         if(this.loadMask){
26964             this.maskEl.mask(this.loadingText);
26965         }
26966         
26967         this.file = false;
26968         this.exif = {};
26969         
26970         if(typeof(file) === 'string'){
26971             this.loadCanvas(file);
26972             return;
26973         }
26974         
26975         if(!file || !this.urlAPI){
26976             return;
26977         }
26978         
26979         this.file = file;
26980         this.cropType = file.type;
26981         
26982         var _this = this;
26983         
26984         if(this.fireEvent('prepare', this, this.file) != false){
26985             
26986             var reader = new FileReader();
26987             
26988             reader.onload = function (e) {
26989                 if (e.target.error) {
26990                     Roo.log(e.target.error);
26991                     return;
26992                 }
26993                 
26994                 var buffer = e.target.result,
26995                     dataView = new DataView(buffer),
26996                     offset = 2,
26997                     maxOffset = dataView.byteLength - 4,
26998                     markerBytes,
26999                     markerLength;
27000                 
27001                 if (dataView.getUint16(0) === 0xffd8) {
27002                     while (offset < maxOffset) {
27003                         markerBytes = dataView.getUint16(offset);
27004                         
27005                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27006                             markerLength = dataView.getUint16(offset + 2) + 2;
27007                             if (offset + markerLength > dataView.byteLength) {
27008                                 Roo.log('Invalid meta data: Invalid segment size.');
27009                                 break;
27010                             }
27011                             
27012                             if(markerBytes == 0xffe1){
27013                                 _this.parseExifData(
27014                                     dataView,
27015                                     offset,
27016                                     markerLength
27017                                 );
27018                             }
27019                             
27020                             offset += markerLength;
27021                             
27022                             continue;
27023                         }
27024                         
27025                         break;
27026                     }
27027                     
27028                 }
27029                 
27030                 var url = _this.urlAPI.createObjectURL(_this.file);
27031                 
27032                 _this.loadCanvas(url);
27033                 
27034                 return;
27035             }
27036             
27037             reader.readAsArrayBuffer(this.file);
27038             
27039         }
27040         
27041     },
27042     
27043     parseExifData : function(dataView, offset, length)
27044     {
27045         var tiffOffset = offset + 10,
27046             littleEndian,
27047             dirOffset;
27048     
27049         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27050             // No Exif data, might be XMP data instead
27051             return;
27052         }
27053         
27054         // Check for the ASCII code for "Exif" (0x45786966):
27055         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27056             // No Exif data, might be XMP data instead
27057             return;
27058         }
27059         if (tiffOffset + 8 > dataView.byteLength) {
27060             Roo.log('Invalid Exif data: Invalid segment size.');
27061             return;
27062         }
27063         // Check for the two null bytes:
27064         if (dataView.getUint16(offset + 8) !== 0x0000) {
27065             Roo.log('Invalid Exif data: Missing byte alignment offset.');
27066             return;
27067         }
27068         // Check the byte alignment:
27069         switch (dataView.getUint16(tiffOffset)) {
27070         case 0x4949:
27071             littleEndian = true;
27072             break;
27073         case 0x4D4D:
27074             littleEndian = false;
27075             break;
27076         default:
27077             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27078             return;
27079         }
27080         // Check for the TIFF tag marker (0x002A):
27081         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27082             Roo.log('Invalid Exif data: Missing TIFF marker.');
27083             return;
27084         }
27085         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27086         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27087         
27088         this.parseExifTags(
27089             dataView,
27090             tiffOffset,
27091             tiffOffset + dirOffset,
27092             littleEndian
27093         );
27094     },
27095     
27096     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27097     {
27098         var tagsNumber,
27099             dirEndOffset,
27100             i;
27101         if (dirOffset + 6 > dataView.byteLength) {
27102             Roo.log('Invalid Exif data: Invalid directory offset.');
27103             return;
27104         }
27105         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
27106         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
27107         if (dirEndOffset + 4 > dataView.byteLength) {
27108             Roo.log('Invalid Exif data: Invalid directory size.');
27109             return;
27110         }
27111         for (i = 0; i < tagsNumber; i += 1) {
27112             this.parseExifTag(
27113                 dataView,
27114                 tiffOffset,
27115                 dirOffset + 2 + 12 * i, // tag offset
27116                 littleEndian
27117             );
27118         }
27119         // Return the offset to the next directory:
27120         return dataView.getUint32(dirEndOffset, littleEndian);
27121     },
27122     
27123     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
27124     {
27125         var tag = dataView.getUint16(offset, littleEndian);
27126         
27127         this.exif[tag] = this.getExifValue(
27128             dataView,
27129             tiffOffset,
27130             offset,
27131             dataView.getUint16(offset + 2, littleEndian), // tag type
27132             dataView.getUint32(offset + 4, littleEndian), // tag length
27133             littleEndian
27134         );
27135     },
27136     
27137     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
27138     {
27139         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
27140             tagSize,
27141             dataOffset,
27142             values,
27143             i,
27144             str,
27145             c;
27146     
27147         if (!tagType) {
27148             Roo.log('Invalid Exif data: Invalid tag type.');
27149             return;
27150         }
27151         
27152         tagSize = tagType.size * length;
27153         // Determine if the value is contained in the dataOffset bytes,
27154         // or if the value at the dataOffset is a pointer to the actual data:
27155         dataOffset = tagSize > 4 ?
27156                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
27157         if (dataOffset + tagSize > dataView.byteLength) {
27158             Roo.log('Invalid Exif data: Invalid data offset.');
27159             return;
27160         }
27161         if (length === 1) {
27162             return tagType.getValue(dataView, dataOffset, littleEndian);
27163         }
27164         values = [];
27165         for (i = 0; i < length; i += 1) {
27166             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
27167         }
27168         
27169         if (tagType.ascii) {
27170             str = '';
27171             // Concatenate the chars:
27172             for (i = 0; i < values.length; i += 1) {
27173                 c = values[i];
27174                 // Ignore the terminating NULL byte(s):
27175                 if (c === '\u0000') {
27176                     break;
27177                 }
27178                 str += c;
27179             }
27180             return str;
27181         }
27182         return values;
27183     }
27184     
27185 });
27186
27187 Roo.apply(Roo.bootstrap.UploadCropbox, {
27188     tags : {
27189         'Orientation': 0x0112
27190     },
27191     
27192     Orientation: {
27193             1: 0, //'top-left',
27194 //            2: 'top-right',
27195             3: 180, //'bottom-right',
27196 //            4: 'bottom-left',
27197 //            5: 'left-top',
27198             6: 90, //'right-top',
27199 //            7: 'right-bottom',
27200             8: 270 //'left-bottom'
27201     },
27202     
27203     exifTagTypes : {
27204         // byte, 8-bit unsigned int:
27205         1: {
27206             getValue: function (dataView, dataOffset) {
27207                 return dataView.getUint8(dataOffset);
27208             },
27209             size: 1
27210         },
27211         // ascii, 8-bit byte:
27212         2: {
27213             getValue: function (dataView, dataOffset) {
27214                 return String.fromCharCode(dataView.getUint8(dataOffset));
27215             },
27216             size: 1,
27217             ascii: true
27218         },
27219         // short, 16 bit int:
27220         3: {
27221             getValue: function (dataView, dataOffset, littleEndian) {
27222                 return dataView.getUint16(dataOffset, littleEndian);
27223             },
27224             size: 2
27225         },
27226         // long, 32 bit int:
27227         4: {
27228             getValue: function (dataView, dataOffset, littleEndian) {
27229                 return dataView.getUint32(dataOffset, littleEndian);
27230             },
27231             size: 4
27232         },
27233         // rational = two long values, first is numerator, second is denominator:
27234         5: {
27235             getValue: function (dataView, dataOffset, littleEndian) {
27236                 return dataView.getUint32(dataOffset, littleEndian) /
27237                     dataView.getUint32(dataOffset + 4, littleEndian);
27238             },
27239             size: 8
27240         },
27241         // slong, 32 bit signed int:
27242         9: {
27243             getValue: function (dataView, dataOffset, littleEndian) {
27244                 return dataView.getInt32(dataOffset, littleEndian);
27245             },
27246             size: 4
27247         },
27248         // srational, two slongs, first is numerator, second is denominator:
27249         10: {
27250             getValue: function (dataView, dataOffset, littleEndian) {
27251                 return dataView.getInt32(dataOffset, littleEndian) /
27252                     dataView.getInt32(dataOffset + 4, littleEndian);
27253             },
27254             size: 8
27255         }
27256     },
27257     
27258     footer : {
27259         STANDARD : [
27260             {
27261                 tag : 'div',
27262                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27263                 action : 'rotate-left',
27264                 cn : [
27265                     {
27266                         tag : 'button',
27267                         cls : 'btn btn-default',
27268                         html : '<i class="fa fa-undo"></i>'
27269                     }
27270                 ]
27271             },
27272             {
27273                 tag : 'div',
27274                 cls : 'btn-group roo-upload-cropbox-picture',
27275                 action : 'picture',
27276                 cn : [
27277                     {
27278                         tag : 'button',
27279                         cls : 'btn btn-default',
27280                         html : '<i class="fa fa-picture-o"></i>'
27281                     }
27282                 ]
27283             },
27284             {
27285                 tag : 'div',
27286                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27287                 action : 'rotate-right',
27288                 cn : [
27289                     {
27290                         tag : 'button',
27291                         cls : 'btn btn-default',
27292                         html : '<i class="fa fa-repeat"></i>'
27293                     }
27294                 ]
27295             }
27296         ],
27297         DOCUMENT : [
27298             {
27299                 tag : 'div',
27300                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27301                 action : 'rotate-left',
27302                 cn : [
27303                     {
27304                         tag : 'button',
27305                         cls : 'btn btn-default',
27306                         html : '<i class="fa fa-undo"></i>'
27307                     }
27308                 ]
27309             },
27310             {
27311                 tag : 'div',
27312                 cls : 'btn-group roo-upload-cropbox-download',
27313                 action : 'download',
27314                 cn : [
27315                     {
27316                         tag : 'button',
27317                         cls : 'btn btn-default',
27318                         html : '<i class="fa fa-download"></i>'
27319                     }
27320                 ]
27321             },
27322             {
27323                 tag : 'div',
27324                 cls : 'btn-group roo-upload-cropbox-crop',
27325                 action : 'crop',
27326                 cn : [
27327                     {
27328                         tag : 'button',
27329                         cls : 'btn btn-default',
27330                         html : '<i class="fa fa-crop"></i>'
27331                     }
27332                 ]
27333             },
27334             {
27335                 tag : 'div',
27336                 cls : 'btn-group roo-upload-cropbox-trash',
27337                 action : 'trash',
27338                 cn : [
27339                     {
27340                         tag : 'button',
27341                         cls : 'btn btn-default',
27342                         html : '<i class="fa fa-trash"></i>'
27343                     }
27344                 ]
27345             },
27346             {
27347                 tag : 'div',
27348                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27349                 action : 'rotate-right',
27350                 cn : [
27351                     {
27352                         tag : 'button',
27353                         cls : 'btn btn-default',
27354                         html : '<i class="fa fa-repeat"></i>'
27355                     }
27356                 ]
27357             }
27358         ],
27359         ROTATOR : [
27360             {
27361                 tag : 'div',
27362                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27363                 action : 'rotate-left',
27364                 cn : [
27365                     {
27366                         tag : 'button',
27367                         cls : 'btn btn-default',
27368                         html : '<i class="fa fa-undo"></i>'
27369                     }
27370                 ]
27371             },
27372             {
27373                 tag : 'div',
27374                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27375                 action : 'rotate-right',
27376                 cn : [
27377                     {
27378                         tag : 'button',
27379                         cls : 'btn btn-default',
27380                         html : '<i class="fa fa-repeat"></i>'
27381                     }
27382                 ]
27383             }
27384         ]
27385     }
27386 });
27387
27388 /*
27389 * Licence: LGPL
27390 */
27391
27392 /**
27393  * @class Roo.bootstrap.DocumentManager
27394  * @extends Roo.bootstrap.Component
27395  * Bootstrap DocumentManager class
27396  * @cfg {String} paramName default 'imageUpload'
27397  * @cfg {String} method default POST
27398  * @cfg {String} url action url
27399  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
27400  * @cfg {Boolean} multiple multiple upload default true
27401  * @cfg {Number} thumbSize default 300
27402  * @cfg {String} fieldLabel
27403  * @cfg {Number} labelWidth default 4
27404  * @cfg {String} labelAlign (left|top) default left
27405  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
27406  * 
27407  * @constructor
27408  * Create a new DocumentManager
27409  * @param {Object} config The config object
27410  */
27411
27412 Roo.bootstrap.DocumentManager = function(config){
27413     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
27414     
27415     this.files = [];
27416     this.delegates = [];
27417     
27418     this.addEvents({
27419         /**
27420          * @event initial
27421          * Fire when initial the DocumentManager
27422          * @param {Roo.bootstrap.DocumentManager} this
27423          */
27424         "initial" : true,
27425         /**
27426          * @event inspect
27427          * inspect selected file
27428          * @param {Roo.bootstrap.DocumentManager} this
27429          * @param {File} file
27430          */
27431         "inspect" : true,
27432         /**
27433          * @event exception
27434          * Fire when xhr load exception
27435          * @param {Roo.bootstrap.DocumentManager} this
27436          * @param {XMLHttpRequest} xhr
27437          */
27438         "exception" : true,
27439         /**
27440          * @event afterupload
27441          * Fire when xhr load exception
27442          * @param {Roo.bootstrap.DocumentManager} this
27443          * @param {XMLHttpRequest} xhr
27444          */
27445         "afterupload" : true,
27446         /**
27447          * @event prepare
27448          * prepare the form data
27449          * @param {Roo.bootstrap.DocumentManager} this
27450          * @param {Object} formData
27451          */
27452         "prepare" : true,
27453         /**
27454          * @event remove
27455          * Fire when remove the file
27456          * @param {Roo.bootstrap.DocumentManager} this
27457          * @param {Object} file
27458          */
27459         "remove" : true,
27460         /**
27461          * @event refresh
27462          * Fire after refresh the file
27463          * @param {Roo.bootstrap.DocumentManager} this
27464          */
27465         "refresh" : true,
27466         /**
27467          * @event click
27468          * Fire after click the image
27469          * @param {Roo.bootstrap.DocumentManager} this
27470          * @param {Object} file
27471          */
27472         "click" : true,
27473         /**
27474          * @event edit
27475          * Fire when upload a image and editable set to true
27476          * @param {Roo.bootstrap.DocumentManager} this
27477          * @param {Object} file
27478          */
27479         "edit" : true,
27480         /**
27481          * @event beforeselectfile
27482          * Fire before select file
27483          * @param {Roo.bootstrap.DocumentManager} this
27484          */
27485         "beforeselectfile" : true,
27486         /**
27487          * @event process
27488          * Fire before process file
27489          * @param {Roo.bootstrap.DocumentManager} this
27490          * @param {Object} file
27491          */
27492         "process" : true
27493         
27494     });
27495 };
27496
27497 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
27498     
27499     boxes : 0,
27500     inputName : '',
27501     thumbSize : 300,
27502     multiple : true,
27503     files : false,
27504     method : 'POST',
27505     url : '',
27506     paramName : 'imageUpload',
27507     fieldLabel : '',
27508     labelWidth : 4,
27509     labelAlign : 'left',
27510     editable : true,
27511     delegates : false,
27512     
27513     
27514     xhr : false, 
27515     
27516     getAutoCreate : function()
27517     {   
27518         var managerWidget = {
27519             tag : 'div',
27520             cls : 'roo-document-manager',
27521             cn : [
27522                 {
27523                     tag : 'input',
27524                     cls : 'roo-document-manager-selector',
27525                     type : 'file'
27526                 },
27527                 {
27528                     tag : 'div',
27529                     cls : 'roo-document-manager-uploader',
27530                     cn : [
27531                         {
27532                             tag : 'div',
27533                             cls : 'roo-document-manager-upload-btn',
27534                             html : '<i class="fa fa-plus"></i>'
27535                         }
27536                     ]
27537                     
27538                 }
27539             ]
27540         };
27541         
27542         var content = [
27543             {
27544                 tag : 'div',
27545                 cls : 'column col-md-12',
27546                 cn : managerWidget
27547             }
27548         ];
27549         
27550         if(this.fieldLabel.length){
27551             
27552             content = [
27553                 {
27554                     tag : 'div',
27555                     cls : 'column col-md-12',
27556                     html : this.fieldLabel
27557                 },
27558                 {
27559                     tag : 'div',
27560                     cls : 'column col-md-12',
27561                     cn : managerWidget
27562                 }
27563             ];
27564
27565             if(this.labelAlign == 'left'){
27566                 content = [
27567                     {
27568                         tag : 'div',
27569                         cls : 'column col-md-' + this.labelWidth,
27570                         html : this.fieldLabel
27571                     },
27572                     {
27573                         tag : 'div',
27574                         cls : 'column col-md-' + (12 - this.labelWidth),
27575                         cn : managerWidget
27576                     }
27577                 ];
27578                 
27579             }
27580         }
27581         
27582         var cfg = {
27583             tag : 'div',
27584             cls : 'row clearfix',
27585             cn : content
27586         };
27587         
27588         return cfg;
27589         
27590     },
27591     
27592     initEvents : function()
27593     {
27594         this.managerEl = this.el.select('.roo-document-manager', true).first();
27595         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27596         
27597         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
27598         this.selectorEl.hide();
27599         
27600         if(this.multiple){
27601             this.selectorEl.attr('multiple', 'multiple');
27602         }
27603         
27604         this.selectorEl.on('change', this.onFileSelected, this);
27605         
27606         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
27607         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27608         
27609         this.uploader.on('click', this.onUploaderClick, this);
27610         
27611         this.renderProgressDialog();
27612         
27613         var _this = this;
27614         
27615         window.addEventListener("resize", function() { _this.refresh(); } );
27616         
27617         this.fireEvent('initial', this);
27618     },
27619     
27620     renderProgressDialog : function()
27621     {
27622         var _this = this;
27623         
27624         this.progressDialog = new Roo.bootstrap.Modal({
27625             cls : 'roo-document-manager-progress-dialog',
27626             allow_close : false,
27627             title : '',
27628             buttons : [
27629                 {
27630                     name  :'cancel',
27631                     weight : 'danger',
27632                     html : 'Cancel'
27633                 }
27634             ], 
27635             listeners : { 
27636                 btnclick : function() {
27637                     _this.uploadCancel();
27638                     this.hide();
27639                 }
27640             }
27641         });
27642          
27643         this.progressDialog.render(Roo.get(document.body));
27644          
27645         this.progress = new Roo.bootstrap.Progress({
27646             cls : 'roo-document-manager-progress',
27647             active : true,
27648             striped : true
27649         });
27650         
27651         this.progress.render(this.progressDialog.getChildContainer());
27652         
27653         this.progressBar = new Roo.bootstrap.ProgressBar({
27654             cls : 'roo-document-manager-progress-bar',
27655             aria_valuenow : 0,
27656             aria_valuemin : 0,
27657             aria_valuemax : 12,
27658             panel : 'success'
27659         });
27660         
27661         this.progressBar.render(this.progress.getChildContainer());
27662     },
27663     
27664     onUploaderClick : function(e)
27665     {
27666         e.preventDefault();
27667      
27668         if(this.fireEvent('beforeselectfile', this) != false){
27669             this.selectorEl.dom.click();
27670         }
27671         
27672     },
27673     
27674     onFileSelected : function(e)
27675     {
27676         e.preventDefault();
27677         
27678         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27679             return;
27680         }
27681         
27682         Roo.each(this.selectorEl.dom.files, function(file){
27683             if(this.fireEvent('inspect', this, file) != false){
27684                 this.files.push(file);
27685             }
27686         }, this);
27687         
27688         this.queue();
27689         
27690     },
27691     
27692     queue : function()
27693     {
27694         this.selectorEl.dom.value = '';
27695         
27696         if(!this.files.length){
27697             return;
27698         }
27699         
27700         if(this.boxes > 0 && this.files.length > this.boxes){
27701             this.files = this.files.slice(0, this.boxes);
27702         }
27703         
27704         this.uploader.show();
27705         
27706         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27707             this.uploader.hide();
27708         }
27709         
27710         var _this = this;
27711         
27712         var files = [];
27713         
27714         var docs = [];
27715         
27716         Roo.each(this.files, function(file){
27717             
27718             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27719                 var f = this.renderPreview(file);
27720                 files.push(f);
27721                 return;
27722             }
27723             
27724             if(file.type.indexOf('image') != -1){
27725                 this.delegates.push(
27726                     (function(){
27727                         _this.process(file);
27728                     }).createDelegate(this)
27729                 );
27730         
27731                 return;
27732             }
27733             
27734             docs.push(
27735                 (function(){
27736                     _this.process(file);
27737                 }).createDelegate(this)
27738             );
27739             
27740         }, this);
27741         
27742         this.files = files;
27743         
27744         this.delegates = this.delegates.concat(docs);
27745         
27746         if(!this.delegates.length){
27747             this.refresh();
27748             return;
27749         }
27750         
27751         this.progressBar.aria_valuemax = this.delegates.length;
27752         
27753         this.arrange();
27754         
27755         return;
27756     },
27757     
27758     arrange : function()
27759     {
27760         if(!this.delegates.length){
27761             this.progressDialog.hide();
27762             this.refresh();
27763             return;
27764         }
27765         
27766         var delegate = this.delegates.shift();
27767         
27768         this.progressDialog.show();
27769         
27770         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27771         
27772         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27773         
27774         delegate();
27775     },
27776     
27777     refresh : function()
27778     {
27779         this.uploader.show();
27780         
27781         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27782             this.uploader.hide();
27783         }
27784         
27785         Roo.isTouch ? this.closable(false) : this.closable(true);
27786         
27787         this.fireEvent('refresh', this);
27788     },
27789     
27790     onRemove : function(e, el, o)
27791     {
27792         e.preventDefault();
27793         
27794         this.fireEvent('remove', this, o);
27795         
27796     },
27797     
27798     remove : function(o)
27799     {
27800         var files = [];
27801         
27802         Roo.each(this.files, function(file){
27803             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27804                 files.push(file);
27805                 return;
27806             }
27807
27808             o.target.remove();
27809
27810         }, this);
27811         
27812         this.files = files;
27813         
27814         this.refresh();
27815     },
27816     
27817     clear : function()
27818     {
27819         Roo.each(this.files, function(file){
27820             if(!file.target){
27821                 return;
27822             }
27823             
27824             file.target.remove();
27825
27826         }, this);
27827         
27828         this.files = [];
27829         
27830         this.refresh();
27831     },
27832     
27833     onClick : function(e, el, o)
27834     {
27835         e.preventDefault();
27836         
27837         this.fireEvent('click', this, o);
27838         
27839     },
27840     
27841     closable : function(closable)
27842     {
27843         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27844             
27845             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27846             
27847             if(closable){
27848                 el.show();
27849                 return;
27850             }
27851             
27852             el.hide();
27853             
27854         }, this);
27855     },
27856     
27857     xhrOnLoad : function(xhr)
27858     {
27859         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27860             el.remove();
27861         }, this);
27862         
27863         if (xhr.readyState !== 4) {
27864             this.arrange();
27865             this.fireEvent('exception', this, xhr);
27866             return;
27867         }
27868
27869         var response = Roo.decode(xhr.responseText);
27870         
27871         if(!response.success){
27872             this.arrange();
27873             this.fireEvent('exception', this, xhr);
27874             return;
27875         }
27876         
27877         var file = this.renderPreview(response.data);
27878         
27879         this.files.push(file);
27880         
27881         this.arrange();
27882         
27883         this.fireEvent('afterupload', this, xhr);
27884         
27885     },
27886     
27887     xhrOnError : function(xhr)
27888     {
27889         Roo.log('xhr on error');
27890         
27891         var response = Roo.decode(xhr.responseText);
27892           
27893         Roo.log(response);
27894         
27895         this.arrange();
27896     },
27897     
27898     process : function(file)
27899     {
27900         if(this.fireEvent('process', this, file) !== false){
27901             if(this.editable && file.type.indexOf('image') != -1){
27902                 this.fireEvent('edit', this, file);
27903                 return;
27904             }
27905
27906             this.uploadStart(file, false);
27907
27908             return;
27909         }
27910         
27911     },
27912     
27913     uploadStart : function(file, crop)
27914     {
27915         this.xhr = new XMLHttpRequest();
27916         
27917         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27918             this.arrange();
27919             return;
27920         }
27921         
27922         file.xhr = this.xhr;
27923             
27924         this.managerEl.createChild({
27925             tag : 'div',
27926             cls : 'roo-document-manager-loading',
27927             cn : [
27928                 {
27929                     tag : 'div',
27930                     tooltip : file.name,
27931                     cls : 'roo-document-manager-thumb',
27932                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27933                 }
27934             ]
27935
27936         });
27937
27938         this.xhr.open(this.method, this.url, true);
27939         
27940         var headers = {
27941             "Accept": "application/json",
27942             "Cache-Control": "no-cache",
27943             "X-Requested-With": "XMLHttpRequest"
27944         };
27945         
27946         for (var headerName in headers) {
27947             var headerValue = headers[headerName];
27948             if (headerValue) {
27949                 this.xhr.setRequestHeader(headerName, headerValue);
27950             }
27951         }
27952         
27953         var _this = this;
27954         
27955         this.xhr.onload = function()
27956         {
27957             _this.xhrOnLoad(_this.xhr);
27958         }
27959         
27960         this.xhr.onerror = function()
27961         {
27962             _this.xhrOnError(_this.xhr);
27963         }
27964         
27965         var formData = new FormData();
27966
27967         formData.append('returnHTML', 'NO');
27968         
27969         if(crop){
27970             formData.append('crop', crop);
27971         }
27972         
27973         formData.append(this.paramName, file, file.name);
27974         
27975         if(this.fireEvent('prepare', this, formData, file) != false){
27976             this.xhr.send(formData);
27977             return;
27978         };
27979         
27980         this.uploadCancel();
27981     },
27982     
27983     uploadCancel : function()
27984     {
27985         if (this.xhr) {
27986             this.xhr.abort();
27987         }
27988         
27989         this.delegates = [];
27990         
27991         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27992             el.remove();
27993         }, this);
27994         
27995         this.arrange();
27996     },
27997     
27998     renderPreview : function(file)
27999     {
28000         if(typeof(file.target) != 'undefined' && file.target){
28001             return file;
28002         }
28003         
28004         var previewEl = this.managerEl.createChild({
28005             tag : 'div',
28006             cls : 'roo-document-manager-preview',
28007             cn : [
28008                 {
28009                     tag : 'div',
28010                     tooltip : file.filename,
28011                     cls : 'roo-document-manager-thumb',
28012                     html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
28013                 },
28014                 {
28015                     tag : 'button',
28016                     cls : 'close',
28017                     html : '<i class="fa fa-times-circle"></i>'
28018                 }
28019             ]
28020         });
28021
28022         var close = previewEl.select('button.close', true).first();
28023
28024         close.on('click', this.onRemove, this, file);
28025
28026         file.target = previewEl;
28027
28028         var image = previewEl.select('img', true).first();
28029         
28030         var _this = this;
28031         
28032         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28033         
28034         image.on('click', this.onClick, this, file);
28035         
28036         return file;
28037         
28038     },
28039     
28040     onPreviewLoad : function(file, image)
28041     {
28042         if(typeof(file.target) == 'undefined' || !file.target){
28043             return;
28044         }
28045         
28046         var width = image.dom.naturalWidth || image.dom.width;
28047         var height = image.dom.naturalHeight || image.dom.height;
28048         
28049         if(width > height){
28050             file.target.addClass('wide');
28051             return;
28052         }
28053         
28054         file.target.addClass('tall');
28055         return;
28056         
28057     },
28058     
28059     uploadFromSource : function(file, crop)
28060     {
28061         this.xhr = new XMLHttpRequest();
28062         
28063         this.managerEl.createChild({
28064             tag : 'div',
28065             cls : 'roo-document-manager-loading',
28066             cn : [
28067                 {
28068                     tag : 'div',
28069                     tooltip : file.name,
28070                     cls : 'roo-document-manager-thumb',
28071                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28072                 }
28073             ]
28074
28075         });
28076
28077         this.xhr.open(this.method, this.url, true);
28078         
28079         var headers = {
28080             "Accept": "application/json",
28081             "Cache-Control": "no-cache",
28082             "X-Requested-With": "XMLHttpRequest"
28083         };
28084         
28085         for (var headerName in headers) {
28086             var headerValue = headers[headerName];
28087             if (headerValue) {
28088                 this.xhr.setRequestHeader(headerName, headerValue);
28089             }
28090         }
28091         
28092         var _this = this;
28093         
28094         this.xhr.onload = function()
28095         {
28096             _this.xhrOnLoad(_this.xhr);
28097         }
28098         
28099         this.xhr.onerror = function()
28100         {
28101             _this.xhrOnError(_this.xhr);
28102         }
28103         
28104         var formData = new FormData();
28105
28106         formData.append('returnHTML', 'NO');
28107         
28108         formData.append('crop', crop);
28109         
28110         if(typeof(file.filename) != 'undefined'){
28111             formData.append('filename', file.filename);
28112         }
28113         
28114         if(typeof(file.mimetype) != 'undefined'){
28115             formData.append('mimetype', file.mimetype);
28116         }
28117         
28118         if(this.fireEvent('prepare', this, formData) != false){
28119             this.xhr.send(formData);
28120         };
28121     }
28122 });
28123
28124 /*
28125 * Licence: LGPL
28126 */
28127
28128 /**
28129  * @class Roo.bootstrap.DocumentViewer
28130  * @extends Roo.bootstrap.Component
28131  * Bootstrap DocumentViewer class
28132  * @cfg {Boolean} showDownload (true|false) show download button (default true)
28133  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
28134  * 
28135  * @constructor
28136  * Create a new DocumentViewer
28137  * @param {Object} config The config object
28138  */
28139
28140 Roo.bootstrap.DocumentViewer = function(config){
28141     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
28142     
28143     this.addEvents({
28144         /**
28145          * @event initial
28146          * Fire after initEvent
28147          * @param {Roo.bootstrap.DocumentViewer} this
28148          */
28149         "initial" : true,
28150         /**
28151          * @event click
28152          * Fire after click
28153          * @param {Roo.bootstrap.DocumentViewer} this
28154          */
28155         "click" : true,
28156         /**
28157          * @event download
28158          * Fire after download button
28159          * @param {Roo.bootstrap.DocumentViewer} this
28160          */
28161         "download" : true,
28162         /**
28163          * @event trash
28164          * Fire after trash button
28165          * @param {Roo.bootstrap.DocumentViewer} this
28166          */
28167         "trash" : true
28168         
28169     });
28170 };
28171
28172 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
28173     
28174     showDownload : true,
28175     
28176     showTrash : true,
28177     
28178     getAutoCreate : function()
28179     {
28180         var cfg = {
28181             tag : 'div',
28182             cls : 'roo-document-viewer',
28183             cn : [
28184                 {
28185                     tag : 'div',
28186                     cls : 'roo-document-viewer-body',
28187                     cn : [
28188                         {
28189                             tag : 'div',
28190                             cls : 'roo-document-viewer-thumb',
28191                             cn : [
28192                                 {
28193                                     tag : 'img',
28194                                     cls : 'roo-document-viewer-image'
28195                                 }
28196                             ]
28197                         }
28198                     ]
28199                 },
28200                 {
28201                     tag : 'div',
28202                     cls : 'roo-document-viewer-footer',
28203                     cn : {
28204                         tag : 'div',
28205                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
28206                         cn : [
28207                             {
28208                                 tag : 'div',
28209                                 cls : 'btn-group roo-document-viewer-download',
28210                                 cn : [
28211                                     {
28212                                         tag : 'button',
28213                                         cls : 'btn btn-default',
28214                                         html : '<i class="fa fa-download"></i>'
28215                                     }
28216                                 ]
28217                             },
28218                             {
28219                                 tag : 'div',
28220                                 cls : 'btn-group roo-document-viewer-trash',
28221                                 cn : [
28222                                     {
28223                                         tag : 'button',
28224                                         cls : 'btn btn-default',
28225                                         html : '<i class="fa fa-trash"></i>'
28226                                     }
28227                                 ]
28228                             }
28229                         ]
28230                     }
28231                 }
28232             ]
28233         };
28234         
28235         return cfg;
28236     },
28237     
28238     initEvents : function()
28239     {
28240         
28241         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
28242         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
28243         
28244         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
28245         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
28246         
28247         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
28248         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
28249         
28250         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
28251         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
28252         
28253         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
28254         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
28255         
28256         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
28257         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
28258         
28259         this.bodyEl.on('click', this.onClick, this);
28260         this.downloadBtn.on('click', this.onDownload, this);
28261         this.trashBtn.on('click', this.onTrash, this);
28262         
28263         this.downloadBtn.hide();
28264         this.trashBtn.hide();
28265         
28266         if(this.showDownload){
28267             this.downloadBtn.show();
28268         }
28269         
28270         if(this.showTrash){
28271             this.trashBtn.show();
28272         }
28273         
28274         if(!this.showDownload && !this.showTrash) {
28275             this.footerEl.hide();
28276         }
28277         
28278     },
28279     
28280     initial : function()
28281     {
28282 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
28283         
28284         
28285         this.fireEvent('initial', this);
28286         
28287     },
28288     
28289     onClick : function(e)
28290     {
28291         e.preventDefault();
28292         
28293         this.fireEvent('click', this);
28294     },
28295     
28296     onDownload : function(e)
28297     {
28298         e.preventDefault();
28299         
28300         this.fireEvent('download', this);
28301     },
28302     
28303     onTrash : function(e)
28304     {
28305         e.preventDefault();
28306         
28307         this.fireEvent('trash', this);
28308     }
28309     
28310 });
28311 /*
28312  * - LGPL
28313  *
28314  * nav progress bar
28315  * 
28316  */
28317
28318 /**
28319  * @class Roo.bootstrap.NavProgressBar
28320  * @extends Roo.bootstrap.Component
28321  * Bootstrap NavProgressBar class
28322  * 
28323  * @constructor
28324  * Create a new nav progress bar
28325  * @param {Object} config The config object
28326  */
28327
28328 Roo.bootstrap.NavProgressBar = function(config){
28329     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
28330
28331     this.bullets = this.bullets || [];
28332    
28333 //    Roo.bootstrap.NavProgressBar.register(this);
28334      this.addEvents({
28335         /**
28336              * @event changed
28337              * Fires when the active item changes
28338              * @param {Roo.bootstrap.NavProgressBar} this
28339              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
28340              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
28341          */
28342         'changed': true
28343      });
28344     
28345 };
28346
28347 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
28348     
28349     bullets : [],
28350     barItems : [],
28351     
28352     getAutoCreate : function()
28353     {
28354         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
28355         
28356         cfg = {
28357             tag : 'div',
28358             cls : 'roo-navigation-bar-group',
28359             cn : [
28360                 {
28361                     tag : 'div',
28362                     cls : 'roo-navigation-top-bar'
28363                 },
28364                 {
28365                     tag : 'div',
28366                     cls : 'roo-navigation-bullets-bar',
28367                     cn : [
28368                         {
28369                             tag : 'ul',
28370                             cls : 'roo-navigation-bar'
28371                         }
28372                     ]
28373                 },
28374                 
28375                 {
28376                     tag : 'div',
28377                     cls : 'roo-navigation-bottom-bar'
28378                 }
28379             ]
28380             
28381         };
28382         
28383         return cfg;
28384         
28385     },
28386     
28387     initEvents: function() 
28388     {
28389         
28390     },
28391     
28392     onRender : function(ct, position) 
28393     {
28394         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28395         
28396         if(this.bullets.length){
28397             Roo.each(this.bullets, function(b){
28398                this.addItem(b);
28399             }, this);
28400         }
28401         
28402         this.format();
28403         
28404     },
28405     
28406     addItem : function(cfg)
28407     {
28408         var item = new Roo.bootstrap.NavProgressItem(cfg);
28409         
28410         item.parentId = this.id;
28411         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
28412         
28413         if(cfg.html){
28414             var top = new Roo.bootstrap.Element({
28415                 tag : 'div',
28416                 cls : 'roo-navigation-bar-text'
28417             });
28418             
28419             var bottom = new Roo.bootstrap.Element({
28420                 tag : 'div',
28421                 cls : 'roo-navigation-bar-text'
28422             });
28423             
28424             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
28425             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
28426             
28427             var topText = new Roo.bootstrap.Element({
28428                 tag : 'span',
28429                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
28430             });
28431             
28432             var bottomText = new Roo.bootstrap.Element({
28433                 tag : 'span',
28434                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
28435             });
28436             
28437             topText.onRender(top.el, null);
28438             bottomText.onRender(bottom.el, null);
28439             
28440             item.topEl = top;
28441             item.bottomEl = bottom;
28442         }
28443         
28444         this.barItems.push(item);
28445         
28446         return item;
28447     },
28448     
28449     getActive : function()
28450     {
28451         var active = false;
28452         
28453         Roo.each(this.barItems, function(v){
28454             
28455             if (!v.isActive()) {
28456                 return;
28457             }
28458             
28459             active = v;
28460             return false;
28461             
28462         });
28463         
28464         return active;
28465     },
28466     
28467     setActiveItem : function(item)
28468     {
28469         var prev = false;
28470         
28471         Roo.each(this.barItems, function(v){
28472             if (v.rid == item.rid) {
28473                 return ;
28474             }
28475             
28476             if (v.isActive()) {
28477                 v.setActive(false);
28478                 prev = v;
28479             }
28480         });
28481
28482         item.setActive(true);
28483         
28484         this.fireEvent('changed', this, item, prev);
28485     },
28486     
28487     getBarItem: function(rid)
28488     {
28489         var ret = false;
28490         
28491         Roo.each(this.barItems, function(e) {
28492             if (e.rid != rid) {
28493                 return;
28494             }
28495             
28496             ret =  e;
28497             return false;
28498         });
28499         
28500         return ret;
28501     },
28502     
28503     indexOfItem : function(item)
28504     {
28505         var index = false;
28506         
28507         Roo.each(this.barItems, function(v, i){
28508             
28509             if (v.rid != item.rid) {
28510                 return;
28511             }
28512             
28513             index = i;
28514             return false
28515         });
28516         
28517         return index;
28518     },
28519     
28520     setActiveNext : function()
28521     {
28522         var i = this.indexOfItem(this.getActive());
28523         
28524         if (i > this.barItems.length) {
28525             return;
28526         }
28527         
28528         this.setActiveItem(this.barItems[i+1]);
28529     },
28530     
28531     setActivePrev : function()
28532     {
28533         var i = this.indexOfItem(this.getActive());
28534         
28535         if (i  < 1) {
28536             return;
28537         }
28538         
28539         this.setActiveItem(this.barItems[i-1]);
28540     },
28541     
28542     format : function()
28543     {
28544         if(!this.barItems.length){
28545             return;
28546         }
28547      
28548         var width = 100 / this.barItems.length;
28549         
28550         Roo.each(this.barItems, function(i){
28551             i.el.setStyle('width', width + '%');
28552             i.topEl.el.setStyle('width', width + '%');
28553             i.bottomEl.el.setStyle('width', width + '%');
28554         }, this);
28555         
28556     }
28557     
28558 });
28559 /*
28560  * - LGPL
28561  *
28562  * Nav Progress Item
28563  * 
28564  */
28565
28566 /**
28567  * @class Roo.bootstrap.NavProgressItem
28568  * @extends Roo.bootstrap.Component
28569  * Bootstrap NavProgressItem class
28570  * @cfg {String} rid the reference id
28571  * @cfg {Boolean} active (true|false) Is item active default false
28572  * @cfg {Boolean} disabled (true|false) Is item active default false
28573  * @cfg {String} html
28574  * @cfg {String} position (top|bottom) text position default bottom
28575  * @cfg {String} icon show icon instead of number
28576  * 
28577  * @constructor
28578  * Create a new NavProgressItem
28579  * @param {Object} config The config object
28580  */
28581 Roo.bootstrap.NavProgressItem = function(config){
28582     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
28583     this.addEvents({
28584         // raw events
28585         /**
28586          * @event click
28587          * The raw click event for the entire grid.
28588          * @param {Roo.bootstrap.NavProgressItem} this
28589          * @param {Roo.EventObject} e
28590          */
28591         "click" : true
28592     });
28593    
28594 };
28595
28596 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
28597     
28598     rid : '',
28599     active : false,
28600     disabled : false,
28601     html : '',
28602     position : 'bottom',
28603     icon : false,
28604     
28605     getAutoCreate : function()
28606     {
28607         var iconCls = 'roo-navigation-bar-item-icon';
28608         
28609         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
28610         
28611         var cfg = {
28612             tag: 'li',
28613             cls: 'roo-navigation-bar-item',
28614             cn : [
28615                 {
28616                     tag : 'i',
28617                     cls : iconCls
28618                 }
28619             ]
28620         };
28621         
28622         if(this.active){
28623             cfg.cls += ' active';
28624         }
28625         if(this.disabled){
28626             cfg.cls += ' disabled';
28627         }
28628         
28629         return cfg;
28630     },
28631     
28632     disable : function()
28633     {
28634         this.setDisabled(true);
28635     },
28636     
28637     enable : function()
28638     {
28639         this.setDisabled(false);
28640     },
28641     
28642     initEvents: function() 
28643     {
28644         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
28645         
28646         this.iconEl.on('click', this.onClick, this);
28647     },
28648     
28649     onClick : function(e)
28650     {
28651         e.preventDefault();
28652         
28653         if(this.disabled){
28654             return;
28655         }
28656         
28657         if(this.fireEvent('click', this, e) === false){
28658             return;
28659         };
28660         
28661         this.parent().setActiveItem(this);
28662     },
28663     
28664     isActive: function () 
28665     {
28666         return this.active;
28667     },
28668     
28669     setActive : function(state)
28670     {
28671         if(this.active == state){
28672             return;
28673         }
28674         
28675         this.active = state;
28676         
28677         if (state) {
28678             this.el.addClass('active');
28679             return;
28680         }
28681         
28682         this.el.removeClass('active');
28683         
28684         return;
28685     },
28686     
28687     setDisabled : function(state)
28688     {
28689         if(this.disabled == state){
28690             return;
28691         }
28692         
28693         this.disabled = state;
28694         
28695         if (state) {
28696             this.el.addClass('disabled');
28697             return;
28698         }
28699         
28700         this.el.removeClass('disabled');
28701     },
28702     
28703     tooltipEl : function()
28704     {
28705         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
28706     }
28707 });
28708  
28709
28710  /*
28711  * - LGPL
28712  *
28713  * FieldLabel
28714  * 
28715  */
28716
28717 /**
28718  * @class Roo.bootstrap.FieldLabel
28719  * @extends Roo.bootstrap.Component
28720  * Bootstrap FieldLabel class
28721  * @cfg {String} html contents of the element
28722  * @cfg {String} tag tag of the element default label
28723  * @cfg {String} cls class of the element
28724  * @cfg {String} target label target 
28725  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
28726  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
28727  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
28728  * @cfg {String} iconTooltip default "This field is required"
28729  * 
28730  * @constructor
28731  * Create a new FieldLabel
28732  * @param {Object} config The config object
28733  */
28734
28735 Roo.bootstrap.FieldLabel = function(config){
28736     Roo.bootstrap.Element.superclass.constructor.call(this, config);
28737     
28738     this.addEvents({
28739             /**
28740              * @event invalid
28741              * Fires after the field has been marked as invalid.
28742              * @param {Roo.form.FieldLabel} this
28743              * @param {String} msg The validation message
28744              */
28745             invalid : true,
28746             /**
28747              * @event valid
28748              * Fires after the field has been validated with no errors.
28749              * @param {Roo.form.FieldLabel} this
28750              */
28751             valid : true
28752         });
28753 };
28754
28755 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
28756     
28757     tag: 'label',
28758     cls: '',
28759     html: '',
28760     target: '',
28761     allowBlank : true,
28762     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
28763     validClass : 'text-success fa fa-lg fa-check',
28764     iconTooltip : 'This field is required',
28765     
28766     getAutoCreate : function(){
28767         
28768         var cfg = {
28769             tag : this.tag,
28770             cls : 'roo-bootstrap-field-label ' + this.cls,
28771             for : this.target,
28772             cn : [
28773                 {
28774                     tag : 'i',
28775                     cls : '',
28776                     tooltip : this.iconTooltip
28777                 },
28778                 {
28779                     tag : 'span',
28780                     html : this.html
28781                 }
28782             ] 
28783         };
28784         
28785         return cfg;
28786     },
28787     
28788     initEvents: function() 
28789     {
28790         Roo.bootstrap.Element.superclass.initEvents.call(this);
28791         
28792         this.iconEl = this.el.select('i', true).first();
28793         
28794         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
28795         
28796         Roo.bootstrap.FieldLabel.register(this);
28797     },
28798     
28799     /**
28800      * Mark this field as valid
28801      */
28802     markValid : function()
28803     {
28804         this.iconEl.show();
28805         
28806         this.iconEl.removeClass(this.invalidClass);
28807         
28808         this.iconEl.addClass(this.validClass);
28809         
28810         this.fireEvent('valid', this);
28811     },
28812     
28813     /**
28814      * Mark this field as invalid
28815      * @param {String} msg The validation message
28816      */
28817     markInvalid : function(msg)
28818     {
28819         this.iconEl.show();
28820         
28821         this.iconEl.removeClass(this.validClass);
28822         
28823         this.iconEl.addClass(this.invalidClass);
28824         
28825         this.fireEvent('invalid', this, msg);
28826     }
28827     
28828    
28829 });
28830
28831 Roo.apply(Roo.bootstrap.FieldLabel, {
28832     
28833     groups: {},
28834     
28835      /**
28836     * register a FieldLabel Group
28837     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28838     */
28839     register : function(label)
28840     {
28841         if(this.groups.hasOwnProperty(label.target)){
28842             return;
28843         }
28844      
28845         this.groups[label.target] = label;
28846         
28847     },
28848     /**
28849     * fetch a FieldLabel Group based on the target
28850     * @param {string} target
28851     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28852     */
28853     get: function(target) {
28854         if (typeof(this.groups[target]) == 'undefined') {
28855             return false;
28856         }
28857         
28858         return this.groups[target] ;
28859     }
28860 });
28861
28862  
28863
28864  /*
28865  * - LGPL
28866  *
28867  * page DateSplitField.
28868  * 
28869  */
28870
28871
28872 /**
28873  * @class Roo.bootstrap.DateSplitField
28874  * @extends Roo.bootstrap.Component
28875  * Bootstrap DateSplitField class
28876  * @cfg {string} fieldLabel - the label associated
28877  * @cfg {Number} labelWidth set the width of label (0-12)
28878  * @cfg {String} labelAlign (top|left)
28879  * @cfg {Boolean} dayAllowBlank (true|false) default false
28880  * @cfg {Boolean} monthAllowBlank (true|false) default false
28881  * @cfg {Boolean} yearAllowBlank (true|false) default false
28882  * @cfg {string} dayPlaceholder 
28883  * @cfg {string} monthPlaceholder
28884  * @cfg {string} yearPlaceholder
28885  * @cfg {string} dayFormat default 'd'
28886  * @cfg {string} monthFormat default 'm'
28887  * @cfg {string} yearFormat default 'Y'
28888
28889  *     
28890  * @constructor
28891  * Create a new DateSplitField
28892  * @param {Object} config The config object
28893  */
28894
28895 Roo.bootstrap.DateSplitField = function(config){
28896     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28897     
28898     this.addEvents({
28899         // raw events
28900          /**
28901          * @event years
28902          * getting the data of years
28903          * @param {Roo.bootstrap.DateSplitField} this
28904          * @param {Object} years
28905          */
28906         "years" : true,
28907         /**
28908          * @event days
28909          * getting the data of days
28910          * @param {Roo.bootstrap.DateSplitField} this
28911          * @param {Object} days
28912          */
28913         "days" : true,
28914         /**
28915          * @event invalid
28916          * Fires after the field has been marked as invalid.
28917          * @param {Roo.form.Field} this
28918          * @param {String} msg The validation message
28919          */
28920         invalid : true,
28921        /**
28922          * @event valid
28923          * Fires after the field has been validated with no errors.
28924          * @param {Roo.form.Field} this
28925          */
28926         valid : true
28927     });
28928 };
28929
28930 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
28931     
28932     fieldLabel : '',
28933     labelAlign : 'top',
28934     labelWidth : 3,
28935     dayAllowBlank : false,
28936     monthAllowBlank : false,
28937     yearAllowBlank : false,
28938     dayPlaceholder : '',
28939     monthPlaceholder : '',
28940     yearPlaceholder : '',
28941     dayFormat : 'd',
28942     monthFormat : 'm',
28943     yearFormat : 'Y',
28944     isFormField : true,
28945     
28946     getAutoCreate : function()
28947     {
28948         var cfg = {
28949             tag : 'div',
28950             cls : 'row roo-date-split-field-group',
28951             cn : [
28952                 {
28953                     tag : 'input',
28954                     type : 'hidden',
28955                     cls : 'form-hidden-field roo-date-split-field-group-value',
28956                     name : this.name
28957                 }
28958             ]
28959         };
28960         
28961         if(this.fieldLabel){
28962             cfg.cn.push({
28963                 tag : 'div',
28964                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
28965                 cn : [
28966                     {
28967                         tag : 'label',
28968                         html : this.fieldLabel
28969                     }
28970                 ]
28971             });
28972         }
28973         
28974         Roo.each(['day', 'month', 'year'], function(t){
28975             cfg.cn.push({
28976                 tag : 'div',
28977                 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
28978             });
28979         }, this);
28980         
28981         return cfg;
28982     },
28983     
28984     inputEl: function ()
28985     {
28986         return this.el.select('.roo-date-split-field-group-value', true).first();
28987     },
28988     
28989     onRender : function(ct, position) 
28990     {
28991         var _this = this;
28992         
28993         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28994         
28995         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
28996         
28997         this.dayField = new Roo.bootstrap.ComboBox({
28998             allowBlank : this.dayAllowBlank,
28999             alwaysQuery : true,
29000             displayField : 'value',
29001             editable : false,
29002             fieldLabel : '',
29003             forceSelection : true,
29004             mode : 'local',
29005             placeholder : this.dayPlaceholder,
29006             selectOnFocus : true,
29007             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29008             triggerAction : 'all',
29009             typeAhead : true,
29010             valueField : 'value',
29011             store : new Roo.data.SimpleStore({
29012                 data : (function() {    
29013                     var days = [];
29014                     _this.fireEvent('days', _this, days);
29015                     return days;
29016                 })(),
29017                 fields : [ 'value' ]
29018             }),
29019             listeners : {
29020                 select : function (_self, record, index)
29021                 {
29022                     _this.setValue(_this.getValue());
29023                 }
29024             }
29025         });
29026
29027         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
29028         
29029         this.monthField = new Roo.bootstrap.MonthField({
29030             after : '<i class=\"fa fa-calendar\"></i>',
29031             allowBlank : this.monthAllowBlank,
29032             placeholder : this.monthPlaceholder,
29033             readOnly : true,
29034             listeners : {
29035                 render : function (_self)
29036                 {
29037                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
29038                         e.preventDefault();
29039                         _self.focus();
29040                     });
29041                 },
29042                 select : function (_self, oldvalue, newvalue)
29043                 {
29044                     _this.setValue(_this.getValue());
29045                 }
29046             }
29047         });
29048         
29049         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
29050         
29051         this.yearField = new Roo.bootstrap.ComboBox({
29052             allowBlank : this.yearAllowBlank,
29053             alwaysQuery : true,
29054             displayField : 'value',
29055             editable : false,
29056             fieldLabel : '',
29057             forceSelection : true,
29058             mode : 'local',
29059             placeholder : this.yearPlaceholder,
29060             selectOnFocus : true,
29061             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29062             triggerAction : 'all',
29063             typeAhead : true,
29064             valueField : 'value',
29065             store : new Roo.data.SimpleStore({
29066                 data : (function() {
29067                     var years = [];
29068                     _this.fireEvent('years', _this, years);
29069                     return years;
29070                 })(),
29071                 fields : [ 'value' ]
29072             }),
29073             listeners : {
29074                 select : function (_self, record, index)
29075                 {
29076                     _this.setValue(_this.getValue());
29077                 }
29078             }
29079         });
29080
29081         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
29082     },
29083     
29084     setValue : function(v, format)
29085     {
29086         this.inputEl.dom.value = v;
29087         
29088         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
29089         
29090         var d = Date.parseDate(v, f);
29091         
29092         if(!d){
29093             this.validate();
29094             return;
29095         }
29096         
29097         this.setDay(d.format(this.dayFormat));
29098         this.setMonth(d.format(this.monthFormat));
29099         this.setYear(d.format(this.yearFormat));
29100         
29101         this.validate();
29102         
29103         return;
29104     },
29105     
29106     setDay : function(v)
29107     {
29108         this.dayField.setValue(v);
29109         this.inputEl.dom.value = this.getValue();
29110         this.validate();
29111         return;
29112     },
29113     
29114     setMonth : function(v)
29115     {
29116         this.monthField.setValue(v, true);
29117         this.inputEl.dom.value = this.getValue();
29118         this.validate();
29119         return;
29120     },
29121     
29122     setYear : function(v)
29123     {
29124         this.yearField.setValue(v);
29125         this.inputEl.dom.value = this.getValue();
29126         this.validate();
29127         return;
29128     },
29129     
29130     getDay : function()
29131     {
29132         return this.dayField.getValue();
29133     },
29134     
29135     getMonth : function()
29136     {
29137         return this.monthField.getValue();
29138     },
29139     
29140     getYear : function()
29141     {
29142         return this.yearField.getValue();
29143     },
29144     
29145     getValue : function()
29146     {
29147         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
29148         
29149         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
29150         
29151         return date;
29152     },
29153     
29154     reset : function()
29155     {
29156         this.setDay('');
29157         this.setMonth('');
29158         this.setYear('');
29159         this.inputEl.dom.value = '';
29160         this.validate();
29161         return;
29162     },
29163     
29164     validate : function()
29165     {
29166         var d = this.dayField.validate();
29167         var m = this.monthField.validate();
29168         var y = this.yearField.validate();
29169         
29170         var valid = true;
29171         
29172         if(
29173                 (!this.dayAllowBlank && !d) ||
29174                 (!this.monthAllowBlank && !m) ||
29175                 (!this.yearAllowBlank && !y)
29176         ){
29177             valid = false;
29178         }
29179         
29180         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
29181             return valid;
29182         }
29183         
29184         if(valid){
29185             this.markValid();
29186             return valid;
29187         }
29188         
29189         this.markInvalid();
29190         
29191         return valid;
29192     },
29193     
29194     markValid : function()
29195     {
29196         
29197         var label = this.el.select('label', true).first();
29198         var icon = this.el.select('i.fa-star', true).first();
29199
29200         if(label && icon){
29201             icon.remove();
29202         }
29203         
29204         this.fireEvent('valid', this);
29205     },
29206     
29207      /**
29208      * Mark this field as invalid
29209      * @param {String} msg The validation message
29210      */
29211     markInvalid : function(msg)
29212     {
29213         
29214         var label = this.el.select('label', true).first();
29215         var icon = this.el.select('i.fa-star', true).first();
29216
29217         if(label && !icon){
29218             this.el.select('.roo-date-split-field-label', true).createChild({
29219                 tag : 'i',
29220                 cls : 'text-danger fa fa-lg fa-star',
29221                 tooltip : 'This field is required',
29222                 style : 'margin-right:5px;'
29223             }, label, true);
29224         }
29225         
29226         this.fireEvent('invalid', this, msg);
29227     },
29228     
29229     clearInvalid : function()
29230     {
29231         var label = this.el.select('label', true).first();
29232         var icon = this.el.select('i.fa-star', true).first();
29233
29234         if(label && icon){
29235             icon.remove();
29236         }
29237         
29238         this.fireEvent('valid', this);
29239     },
29240     
29241     getName: function()
29242     {
29243         return this.name;
29244     }
29245     
29246 });
29247
29248  /**
29249  *
29250  * This is based on 
29251  * http://masonry.desandro.com
29252  *
29253  * The idea is to render all the bricks based on vertical width...
29254  *
29255  * The original code extends 'outlayer' - we might need to use that....
29256  * 
29257  */
29258
29259
29260 /**
29261  * @class Roo.bootstrap.LayoutMasonry
29262  * @extends Roo.bootstrap.Component
29263  * Bootstrap Layout Masonry class
29264  * 
29265  * @constructor
29266  * Create a new Element
29267  * @param {Object} config The config object
29268  */
29269
29270 Roo.bootstrap.LayoutMasonry = function(config){
29271     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
29272     
29273     this.bricks = [];
29274     
29275 };
29276
29277 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
29278     
29279     /**
29280      * @cfg {Boolean} isLayoutInstant = no animation?
29281      */   
29282     isLayoutInstant : false, // needed?
29283    
29284     /**
29285      * @cfg {Number} boxWidth  width of the columns
29286      */   
29287     boxWidth : 450,
29288     
29289       /**
29290      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
29291      */   
29292     boxHeight : 0,
29293     
29294     /**
29295      * @cfg {Number} padWidth padding below box..
29296      */   
29297     padWidth : 10, 
29298     
29299     /**
29300      * @cfg {Number} gutter gutter width..
29301      */   
29302     gutter : 10,
29303     
29304      /**
29305      * @cfg {Number} maxCols maximum number of columns
29306      */   
29307     
29308     maxCols: 0,
29309     
29310     /**
29311      * @cfg {Boolean} isAutoInitial defalut true
29312      */   
29313     isAutoInitial : true, 
29314     
29315     containerWidth: 0,
29316     
29317     /**
29318      * @cfg {Boolean} isHorizontal defalut false
29319      */   
29320     isHorizontal : false, 
29321
29322     currentSize : null,
29323     
29324     tag: 'div',
29325     
29326     cls: '',
29327     
29328     bricks: null, //CompositeElement
29329     
29330     cols : 1,
29331     
29332     _isLayoutInited : false,
29333     
29334 //    isAlternative : false, // only use for vertical layout...
29335     
29336     /**
29337      * @cfg {Number} alternativePadWidth padding below box..
29338      */   
29339     alternativePadWidth : 50, 
29340     
29341     getAutoCreate : function(){
29342         
29343         var cfg = {
29344             tag: this.tag,
29345             cls: 'blog-masonary-wrapper ' + this.cls,
29346             cn : {
29347                 cls : 'mas-boxes masonary'
29348             }
29349         };
29350         
29351         return cfg;
29352     },
29353     
29354     getChildContainer: function( )
29355     {
29356         if (this.boxesEl) {
29357             return this.boxesEl;
29358         }
29359         
29360         this.boxesEl = this.el.select('.mas-boxes').first();
29361         
29362         return this.boxesEl;
29363     },
29364     
29365     
29366     initEvents : function()
29367     {
29368         var _this = this;
29369         
29370         if(this.isAutoInitial){
29371             Roo.log('hook children rendered');
29372             this.on('childrenrendered', function() {
29373                 Roo.log('children rendered');
29374                 _this.initial();
29375             } ,this);
29376         }
29377     },
29378     
29379     initial : function()
29380     {
29381         this.currentSize = this.el.getBox(true);
29382         
29383         Roo.EventManager.onWindowResize(this.resize, this); 
29384
29385         if(!this.isAutoInitial){
29386             this.layout();
29387             return;
29388         }
29389         
29390         this.layout();
29391         
29392         return;
29393         //this.layout.defer(500,this);
29394         
29395     },
29396     
29397     resize : function()
29398     {
29399         Roo.log('resize');
29400         
29401         var cs = this.el.getBox(true);
29402         
29403         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29404             Roo.log("no change in with or X");
29405             return;
29406         }
29407         
29408         this.currentSize = cs;
29409         
29410         this.layout();
29411         
29412     },
29413     
29414     layout : function()
29415     {   
29416         this._resetLayout();
29417         
29418         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29419         
29420         this.layoutItems( isInstant );
29421       
29422         this._isLayoutInited = true;
29423         
29424     },
29425     
29426     _resetLayout : function()
29427     {
29428         if(this.isHorizontal){
29429             this.horizontalMeasureColumns();
29430             return;
29431         }
29432         
29433         this.verticalMeasureColumns();
29434         
29435     },
29436     
29437     verticalMeasureColumns : function()
29438     {
29439         this.getContainerWidth();
29440         
29441 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29442 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
29443 //            return;
29444 //        }
29445         
29446         var boxWidth = this.boxWidth + this.padWidth;
29447         
29448         if(this.containerWidth < this.boxWidth){
29449             boxWidth = this.containerWidth
29450         }
29451         
29452         var containerWidth = this.containerWidth;
29453         
29454         var cols = Math.floor(containerWidth / boxWidth);
29455         
29456         this.cols = Math.max( cols, 1 );
29457         
29458         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
29459         
29460         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
29461         
29462         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
29463         
29464         this.colWidth = boxWidth + avail - this.padWidth;
29465         
29466         this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
29467         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
29468     },
29469     
29470     horizontalMeasureColumns : function()
29471     {
29472         this.getContainerWidth();
29473         
29474         var boxWidth = this.boxWidth;
29475         
29476         if(this.containerWidth < boxWidth){
29477             boxWidth = this.containerWidth;
29478         }
29479         
29480         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
29481         
29482         this.el.setHeight(boxWidth);
29483         
29484     },
29485     
29486     getContainerWidth : function()
29487     {
29488         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
29489     },
29490     
29491     layoutItems : function( isInstant )
29492     {
29493         var items = Roo.apply([], this.bricks);
29494         
29495         if(this.isHorizontal){
29496             this._horizontalLayoutItems( items , isInstant );
29497             return;
29498         }
29499         
29500 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29501 //            this._verticalAlternativeLayoutItems( items , isInstant );
29502 //            return;
29503 //        }
29504         
29505         this._verticalLayoutItems( items , isInstant );
29506         
29507     },
29508     
29509     _verticalLayoutItems : function ( items , isInstant)
29510     {
29511         if ( !items || !items.length ) {
29512             return;
29513         }
29514         
29515         var standard = [
29516             ['xs', 'xs', 'xs', 'tall'],
29517             ['xs', 'xs', 'tall'],
29518             ['xs', 'xs', 'sm'],
29519             ['xs', 'xs', 'xs'],
29520             ['xs', 'tall'],
29521             ['xs', 'sm'],
29522             ['xs', 'xs'],
29523             ['xs'],
29524             
29525             ['sm', 'xs', 'xs'],
29526             ['sm', 'xs'],
29527             ['sm'],
29528             
29529             ['tall', 'xs', 'xs', 'xs'],
29530             ['tall', 'xs', 'xs'],
29531             ['tall', 'xs'],
29532             ['tall']
29533             
29534         ];
29535         
29536         var queue = [];
29537         
29538         var boxes = [];
29539         
29540         var box = [];
29541         
29542         Roo.each(items, function(item, k){
29543             
29544             switch (item.size) {
29545                 // these layouts take up a full box,
29546                 case 'md' :
29547                 case 'md-left' :
29548                 case 'md-right' :
29549                 case 'wide' :
29550                     
29551                     if(box.length){
29552                         boxes.push(box);
29553                         box = [];
29554                     }
29555                     
29556                     boxes.push([item]);
29557                     
29558                     break;
29559                     
29560                 case 'xs' :
29561                 case 'sm' :
29562                 case 'tall' :
29563                     
29564                     box.push(item);
29565                     
29566                     break;
29567                 default :
29568                     break;
29569                     
29570             }
29571             
29572         }, this);
29573         
29574         if(box.length){
29575             boxes.push(box);
29576             box = [];
29577         }
29578         
29579         var filterPattern = function(box, length)
29580         {
29581             if(!box.length){
29582                 return;
29583             }
29584             
29585             var match = false;
29586             
29587             var pattern = box.slice(0, length);
29588             
29589             var format = [];
29590             
29591             Roo.each(pattern, function(i){
29592                 format.push(i.size);
29593             }, this);
29594             
29595             Roo.each(standard, function(s){
29596                 
29597                 if(String(s) != String(format)){
29598                     return;
29599                 }
29600                 
29601                 match = true;
29602                 return false;
29603                 
29604             }, this);
29605             
29606             if(!match && length == 1){
29607                 return;
29608             }
29609             
29610             if(!match){
29611                 filterPattern(box, length - 1);
29612                 return;
29613             }
29614                 
29615             queue.push(pattern);
29616
29617             box = box.slice(length, box.length);
29618
29619             filterPattern(box, 4);
29620
29621             return;
29622             
29623         }
29624         
29625         Roo.each(boxes, function(box, k){
29626             
29627             if(!box.length){
29628                 return;
29629             }
29630             
29631             if(box.length == 1){
29632                 queue.push(box);
29633                 return;
29634             }
29635             
29636             filterPattern(box, 4);
29637             
29638         }, this);
29639         
29640         this._processVerticalLayoutQueue( queue, isInstant );
29641         
29642     },
29643     
29644 //    _verticalAlternativeLayoutItems : function( items , isInstant )
29645 //    {
29646 //        if ( !items || !items.length ) {
29647 //            return;
29648 //        }
29649 //
29650 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
29651 //        
29652 //    },
29653     
29654     _horizontalLayoutItems : function ( items , isInstant)
29655     {
29656         if ( !items || !items.length || items.length < 3) {
29657             return;
29658         }
29659         
29660         items.reverse();
29661         
29662         var eItems = items.slice(0, 3);
29663         
29664         items = items.slice(3, items.length);
29665         
29666         var standard = [
29667             ['xs', 'xs', 'xs', 'wide'],
29668             ['xs', 'xs', 'wide'],
29669             ['xs', 'xs', 'sm'],
29670             ['xs', 'xs', 'xs'],
29671             ['xs', 'wide'],
29672             ['xs', 'sm'],
29673             ['xs', 'xs'],
29674             ['xs'],
29675             
29676             ['sm', 'xs', 'xs'],
29677             ['sm', 'xs'],
29678             ['sm'],
29679             
29680             ['wide', 'xs', 'xs', 'xs'],
29681             ['wide', 'xs', 'xs'],
29682             ['wide', 'xs'],
29683             ['wide'],
29684             
29685             ['wide-thin']
29686         ];
29687         
29688         var queue = [];
29689         
29690         var boxes = [];
29691         
29692         var box = [];
29693         
29694         Roo.each(items, function(item, k){
29695             
29696             switch (item.size) {
29697                 case 'md' :
29698                 case 'md-left' :
29699                 case 'md-right' :
29700                 case 'tall' :
29701                     
29702                     if(box.length){
29703                         boxes.push(box);
29704                         box = [];
29705                     }
29706                     
29707                     boxes.push([item]);
29708                     
29709                     break;
29710                     
29711                 case 'xs' :
29712                 case 'sm' :
29713                 case 'wide' :
29714                 case 'wide-thin' :
29715                     
29716                     box.push(item);
29717                     
29718                     break;
29719                 default :
29720                     break;
29721                     
29722             }
29723             
29724         }, this);
29725         
29726         if(box.length){
29727             boxes.push(box);
29728             box = [];
29729         }
29730         
29731         var filterPattern = function(box, length)
29732         {
29733             if(!box.length){
29734                 return;
29735             }
29736             
29737             var match = false;
29738             
29739             var pattern = box.slice(0, length);
29740             
29741             var format = [];
29742             
29743             Roo.each(pattern, function(i){
29744                 format.push(i.size);
29745             }, this);
29746             
29747             Roo.each(standard, function(s){
29748                 
29749                 if(String(s) != String(format)){
29750                     return;
29751                 }
29752                 
29753                 match = true;
29754                 return false;
29755                 
29756             }, this);
29757             
29758             if(!match && length == 1){
29759                 return;
29760             }
29761             
29762             if(!match){
29763                 filterPattern(box, length - 1);
29764                 return;
29765             }
29766                 
29767             queue.push(pattern);
29768
29769             box = box.slice(length, box.length);
29770
29771             filterPattern(box, 4);
29772
29773             return;
29774             
29775         }
29776         
29777         Roo.each(boxes, function(box, k){
29778             
29779             if(!box.length){
29780                 return;
29781             }
29782             
29783             if(box.length == 1){
29784                 queue.push(box);
29785                 return;
29786             }
29787             
29788             filterPattern(box, 4);
29789             
29790         }, this);
29791         
29792         
29793         var prune = [];
29794         
29795         var pos = this.el.getBox(true);
29796         
29797         var minX = pos.x;
29798         
29799         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29800         
29801         var hit_end = false;
29802         
29803         Roo.each(queue, function(box){
29804             
29805             if(hit_end){
29806                 
29807                 Roo.each(box, function(b){
29808                 
29809                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29810                     b.el.hide();
29811
29812                 }, this);
29813
29814                 return;
29815             }
29816             
29817             var mx = 0;
29818             
29819             Roo.each(box, function(b){
29820                 
29821                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29822                 b.el.show();
29823
29824                 mx = Math.max(mx, b.x);
29825                 
29826             }, this);
29827             
29828             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29829             
29830             if(maxX < minX){
29831                 
29832                 Roo.each(box, function(b){
29833                 
29834                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29835                     b.el.hide();
29836                     
29837                 }, this);
29838                 
29839                 hit_end = true;
29840                 
29841                 return;
29842             }
29843             
29844             prune.push(box);
29845             
29846         }, this);
29847         
29848         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29849     },
29850     
29851     /** Sets position of item in DOM
29852     * @param {Element} item
29853     * @param {Number} x - horizontal position
29854     * @param {Number} y - vertical position
29855     * @param {Boolean} isInstant - disables transitions
29856     */
29857     _processVerticalLayoutQueue : function( queue, isInstant )
29858     {
29859         var pos = this.el.getBox(true);
29860         var x = pos.x;
29861         var y = pos.y;
29862         var maxY = [];
29863         
29864         for (var i = 0; i < this.cols; i++){
29865             maxY[i] = pos.y;
29866         }
29867         
29868         Roo.each(queue, function(box, k){
29869             
29870             var col = k % this.cols;
29871             
29872             Roo.each(box, function(b,kk){
29873                 
29874                 b.el.position('absolute');
29875                 
29876                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29877                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29878                 
29879                 if(b.size == 'md-left' || b.size == 'md-right'){
29880                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29881                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29882                 }
29883                 
29884                 b.el.setWidth(width);
29885                 b.el.setHeight(height);
29886                 // iframe?
29887                 b.el.select('iframe',true).setSize(width,height);
29888                 
29889             }, this);
29890             
29891             for (var i = 0; i < this.cols; i++){
29892                 
29893                 if(maxY[i] < maxY[col]){
29894                     col = i;
29895                     continue;
29896                 }
29897                 
29898                 col = Math.min(col, i);
29899                 
29900             }
29901             
29902             x = pos.x + col * (this.colWidth + this.padWidth);
29903             
29904             y = maxY[col];
29905             
29906             var positions = [];
29907             
29908             switch (box.length){
29909                 case 1 :
29910                     positions = this.getVerticalOneBoxColPositions(x, y, box);
29911                     break;
29912                 case 2 :
29913                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
29914                     break;
29915                 case 3 :
29916                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
29917                     break;
29918                 case 4 :
29919                     positions = this.getVerticalFourBoxColPositions(x, y, box);
29920                     break;
29921                 default :
29922                     break;
29923             }
29924             
29925             Roo.each(box, function(b,kk){
29926                 
29927                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29928                 
29929                 var sz = b.el.getSize();
29930                 
29931                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29932                 
29933             }, this);
29934             
29935         }, this);
29936         
29937         var mY = 0;
29938         
29939         for (var i = 0; i < this.cols; i++){
29940             mY = Math.max(mY, maxY[i]);
29941         }
29942         
29943         this.el.setHeight(mY - pos.y);
29944         
29945     },
29946     
29947 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29948 //    {
29949 //        var pos = this.el.getBox(true);
29950 //        var x = pos.x;
29951 //        var y = pos.y;
29952 //        var maxX = pos.right;
29953 //        
29954 //        var maxHeight = 0;
29955 //        
29956 //        Roo.each(items, function(item, k){
29957 //            
29958 //            var c = k % 2;
29959 //            
29960 //            item.el.position('absolute');
29961 //                
29962 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29963 //
29964 //            item.el.setWidth(width);
29965 //
29966 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
29967 //
29968 //            item.el.setHeight(height);
29969 //            
29970 //            if(c == 0){
29971 //                item.el.setXY([x, y], isInstant ? false : true);
29972 //            } else {
29973 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
29974 //            }
29975 //            
29976 //            y = y + height + this.alternativePadWidth;
29977 //            
29978 //            maxHeight = maxHeight + height + this.alternativePadWidth;
29979 //            
29980 //        }, this);
29981 //        
29982 //        this.el.setHeight(maxHeight);
29983 //        
29984 //    },
29985     
29986     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
29987     {
29988         var pos = this.el.getBox(true);
29989         
29990         var minX = pos.x;
29991         var minY = pos.y;
29992         
29993         var maxX = pos.right;
29994         
29995         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
29996         
29997         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29998         
29999         Roo.each(queue, function(box, k){
30000             
30001             Roo.each(box, function(b, kk){
30002                 
30003                 b.el.position('absolute');
30004                 
30005                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30006                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30007                 
30008                 if(b.size == 'md-left' || b.size == 'md-right'){
30009                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30010                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30011                 }
30012                 
30013                 b.el.setWidth(width);
30014                 b.el.setHeight(height);
30015                 
30016             }, this);
30017             
30018             if(!box.length){
30019                 return;
30020             }
30021             
30022             var positions = [];
30023             
30024             switch (box.length){
30025                 case 1 :
30026                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
30027                     break;
30028                 case 2 :
30029                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
30030                     break;
30031                 case 3 :
30032                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
30033                     break;
30034                 case 4 :
30035                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
30036                     break;
30037                 default :
30038                     break;
30039             }
30040             
30041             Roo.each(box, function(b,kk){
30042                 
30043                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30044                 
30045                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
30046                 
30047             }, this);
30048             
30049         }, this);
30050         
30051     },
30052     
30053     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
30054     {
30055         Roo.each(eItems, function(b,k){
30056             
30057             b.size = (k == 0) ? 'sm' : 'xs';
30058             b.x = (k == 0) ? 2 : 1;
30059             b.y = (k == 0) ? 2 : 1;
30060             
30061             b.el.position('absolute');
30062             
30063             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30064                 
30065             b.el.setWidth(width);
30066             
30067             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30068             
30069             b.el.setHeight(height);
30070             
30071         }, this);
30072
30073         var positions = [];
30074         
30075         positions.push({
30076             x : maxX - this.unitWidth * 2 - this.gutter,
30077             y : minY
30078         });
30079         
30080         positions.push({
30081             x : maxX - this.unitWidth,
30082             y : minY + (this.unitWidth + this.gutter) * 2
30083         });
30084         
30085         positions.push({
30086             x : maxX - this.unitWidth * 3 - this.gutter * 2,
30087             y : minY
30088         });
30089         
30090         Roo.each(eItems, function(b,k){
30091             
30092             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
30093
30094         }, this);
30095         
30096     },
30097     
30098     getVerticalOneBoxColPositions : function(x, y, box)
30099     {
30100         var pos = [];
30101         
30102         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
30103         
30104         if(box[0].size == 'md-left'){
30105             rand = 0;
30106         }
30107         
30108         if(box[0].size == 'md-right'){
30109             rand = 1;
30110         }
30111         
30112         pos.push({
30113             x : x + (this.unitWidth + this.gutter) * rand,
30114             y : y
30115         });
30116         
30117         return pos;
30118     },
30119     
30120     getVerticalTwoBoxColPositions : function(x, y, box)
30121     {
30122         var pos = [];
30123         
30124         if(box[0].size == 'xs'){
30125             
30126             pos.push({
30127                 x : x,
30128                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
30129             });
30130
30131             pos.push({
30132                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
30133                 y : y
30134             });
30135             
30136             return pos;
30137             
30138         }
30139         
30140         pos.push({
30141             x : x,
30142             y : y
30143         });
30144
30145         pos.push({
30146             x : x + (this.unitWidth + this.gutter) * 2,
30147             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
30148         });
30149         
30150         return pos;
30151         
30152     },
30153     
30154     getVerticalThreeBoxColPositions : function(x, y, box)
30155     {
30156         var pos = [];
30157         
30158         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30159             
30160             pos.push({
30161                 x : x,
30162                 y : y
30163             });
30164
30165             pos.push({
30166                 x : x + (this.unitWidth + this.gutter) * 1,
30167                 y : y
30168             });
30169             
30170             pos.push({
30171                 x : x + (this.unitWidth + this.gutter) * 2,
30172                 y : y
30173             });
30174             
30175             return pos;
30176             
30177         }
30178         
30179         if(box[0].size == 'xs' && box[1].size == 'xs'){
30180             
30181             pos.push({
30182                 x : x,
30183                 y : y
30184             });
30185
30186             pos.push({
30187                 x : x,
30188                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
30189             });
30190             
30191             pos.push({
30192                 x : x + (this.unitWidth + this.gutter) * 1,
30193                 y : y
30194             });
30195             
30196             return pos;
30197             
30198         }
30199         
30200         pos.push({
30201             x : x,
30202             y : y
30203         });
30204
30205         pos.push({
30206             x : x + (this.unitWidth + this.gutter) * 2,
30207             y : y
30208         });
30209
30210         pos.push({
30211             x : x + (this.unitWidth + this.gutter) * 2,
30212             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
30213         });
30214             
30215         return pos;
30216         
30217     },
30218     
30219     getVerticalFourBoxColPositions : function(x, y, box)
30220     {
30221         var pos = [];
30222         
30223         if(box[0].size == 'xs'){
30224             
30225             pos.push({
30226                 x : x,
30227                 y : y
30228             });
30229
30230             pos.push({
30231                 x : x,
30232                 y : y + (this.unitHeight + this.gutter) * 1
30233             });
30234             
30235             pos.push({
30236                 x : x,
30237                 y : y + (this.unitHeight + this.gutter) * 2
30238             });
30239             
30240             pos.push({
30241                 x : x + (this.unitWidth + this.gutter) * 1,
30242                 y : y
30243             });
30244             
30245             return pos;
30246             
30247         }
30248         
30249         pos.push({
30250             x : x,
30251             y : y
30252         });
30253
30254         pos.push({
30255             x : x + (this.unitWidth + this.gutter) * 2,
30256             y : y
30257         });
30258
30259         pos.push({
30260             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
30261             y : y + (this.unitHeight + this.gutter) * 1
30262         });
30263
30264         pos.push({
30265             x : x + (this.unitWidth + this.gutter) * 2,
30266             y : y + (this.unitWidth + this.gutter) * 2
30267         });
30268
30269         return pos;
30270         
30271     },
30272     
30273     getHorizontalOneBoxColPositions : function(maxX, minY, box)
30274     {
30275         var pos = [];
30276         
30277         if(box[0].size == 'md-left'){
30278             pos.push({
30279                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30280                 y : minY
30281             });
30282             
30283             return pos;
30284         }
30285         
30286         if(box[0].size == 'md-right'){
30287             pos.push({
30288                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30289                 y : minY + (this.unitWidth + this.gutter) * 1
30290             });
30291             
30292             return pos;
30293         }
30294         
30295         var rand = Math.floor(Math.random() * (4 - box[0].y));
30296         
30297         pos.push({
30298             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30299             y : minY + (this.unitWidth + this.gutter) * rand
30300         });
30301         
30302         return pos;
30303         
30304     },
30305     
30306     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
30307     {
30308         var pos = [];
30309         
30310         if(box[0].size == 'xs'){
30311             
30312             pos.push({
30313                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30314                 y : minY
30315             });
30316
30317             pos.push({
30318                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30319                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
30320             });
30321             
30322             return pos;
30323             
30324         }
30325         
30326         pos.push({
30327             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30328             y : minY
30329         });
30330
30331         pos.push({
30332             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30333             y : minY + (this.unitWidth + this.gutter) * 2
30334         });
30335         
30336         return pos;
30337         
30338     },
30339     
30340     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
30341     {
30342         var pos = [];
30343         
30344         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30345             
30346             pos.push({
30347                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30348                 y : minY
30349             });
30350
30351             pos.push({
30352                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30353                 y : minY + (this.unitWidth + this.gutter) * 1
30354             });
30355             
30356             pos.push({
30357                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30358                 y : minY + (this.unitWidth + this.gutter) * 2
30359             });
30360             
30361             return pos;
30362             
30363         }
30364         
30365         if(box[0].size == 'xs' && box[1].size == 'xs'){
30366             
30367             pos.push({
30368                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30369                 y : minY
30370             });
30371
30372             pos.push({
30373                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30374                 y : minY
30375             });
30376             
30377             pos.push({
30378                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30379                 y : minY + (this.unitWidth + this.gutter) * 1
30380             });
30381             
30382             return pos;
30383             
30384         }
30385         
30386         pos.push({
30387             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30388             y : minY
30389         });
30390
30391         pos.push({
30392             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30393             y : minY + (this.unitWidth + this.gutter) * 2
30394         });
30395
30396         pos.push({
30397             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30398             y : minY + (this.unitWidth + this.gutter) * 2
30399         });
30400             
30401         return pos;
30402         
30403     },
30404     
30405     getHorizontalFourBoxColPositions : function(maxX, minY, box)
30406     {
30407         var pos = [];
30408         
30409         if(box[0].size == 'xs'){
30410             
30411             pos.push({
30412                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30413                 y : minY
30414             });
30415
30416             pos.push({
30417                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30418                 y : minY
30419             });
30420             
30421             pos.push({
30422                 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),
30423                 y : minY
30424             });
30425             
30426             pos.push({
30427                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
30428                 y : minY + (this.unitWidth + this.gutter) * 1
30429             });
30430             
30431             return pos;
30432             
30433         }
30434         
30435         pos.push({
30436             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30437             y : minY
30438         });
30439         
30440         pos.push({
30441             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30442             y : minY + (this.unitWidth + this.gutter) * 2
30443         });
30444         
30445         pos.push({
30446             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30447             y : minY + (this.unitWidth + this.gutter) * 2
30448         });
30449         
30450         pos.push({
30451             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),
30452             y : minY + (this.unitWidth + this.gutter) * 2
30453         });
30454
30455         return pos;
30456         
30457     }
30458     
30459 });
30460
30461  
30462
30463  /**
30464  *
30465  * This is based on 
30466  * http://masonry.desandro.com
30467  *
30468  * The idea is to render all the bricks based on vertical width...
30469  *
30470  * The original code extends 'outlayer' - we might need to use that....
30471  * 
30472  */
30473
30474
30475 /**
30476  * @class Roo.bootstrap.LayoutMasonryAuto
30477  * @extends Roo.bootstrap.Component
30478  * Bootstrap Layout Masonry class
30479  * 
30480  * @constructor
30481  * Create a new Element
30482  * @param {Object} config The config object
30483  */
30484
30485 Roo.bootstrap.LayoutMasonryAuto = function(config){
30486     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
30487 };
30488
30489 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
30490     
30491       /**
30492      * @cfg {Boolean} isFitWidth  - resize the width..
30493      */   
30494     isFitWidth : false,  // options..
30495     /**
30496      * @cfg {Boolean} isOriginLeft = left align?
30497      */   
30498     isOriginLeft : true,
30499     /**
30500      * @cfg {Boolean} isOriginTop = top align?
30501      */   
30502     isOriginTop : false,
30503     /**
30504      * @cfg {Boolean} isLayoutInstant = no animation?
30505      */   
30506     isLayoutInstant : false, // needed?
30507     /**
30508      * @cfg {Boolean} isResizingContainer = not sure if this is used..
30509      */   
30510     isResizingContainer : true,
30511     /**
30512      * @cfg {Number} columnWidth  width of the columns 
30513      */   
30514     
30515     columnWidth : 0,
30516     
30517     /**
30518      * @cfg {Number} maxCols maximum number of columns
30519      */   
30520     
30521     maxCols: 0,
30522     /**
30523      * @cfg {Number} padHeight padding below box..
30524      */   
30525     
30526     padHeight : 10, 
30527     
30528     /**
30529      * @cfg {Boolean} isAutoInitial defalut true
30530      */   
30531     
30532     isAutoInitial : true, 
30533     
30534     // private?
30535     gutter : 0,
30536     
30537     containerWidth: 0,
30538     initialColumnWidth : 0,
30539     currentSize : null,
30540     
30541     colYs : null, // array.
30542     maxY : 0,
30543     padWidth: 10,
30544     
30545     
30546     tag: 'div',
30547     cls: '',
30548     bricks: null, //CompositeElement
30549     cols : 0, // array?
30550     // element : null, // wrapped now this.el
30551     _isLayoutInited : null, 
30552     
30553     
30554     getAutoCreate : function(){
30555         
30556         var cfg = {
30557             tag: this.tag,
30558             cls: 'blog-masonary-wrapper ' + this.cls,
30559             cn : {
30560                 cls : 'mas-boxes masonary'
30561             }
30562         };
30563         
30564         return cfg;
30565     },
30566     
30567     getChildContainer: function( )
30568     {
30569         if (this.boxesEl) {
30570             return this.boxesEl;
30571         }
30572         
30573         this.boxesEl = this.el.select('.mas-boxes').first();
30574         
30575         return this.boxesEl;
30576     },
30577     
30578     
30579     initEvents : function()
30580     {
30581         var _this = this;
30582         
30583         if(this.isAutoInitial){
30584             Roo.log('hook children rendered');
30585             this.on('childrenrendered', function() {
30586                 Roo.log('children rendered');
30587                 _this.initial();
30588             } ,this);
30589         }
30590         
30591     },
30592     
30593     initial : function()
30594     {
30595         this.reloadItems();
30596
30597         this.currentSize = this.el.getBox(true);
30598
30599         /// was window resize... - let's see if this works..
30600         Roo.EventManager.onWindowResize(this.resize, this); 
30601
30602         if(!this.isAutoInitial){
30603             this.layout();
30604             return;
30605         }
30606         
30607         this.layout.defer(500,this);
30608     },
30609     
30610     reloadItems: function()
30611     {
30612         this.bricks = this.el.select('.masonry-brick', true);
30613         
30614         this.bricks.each(function(b) {
30615             //Roo.log(b.getSize());
30616             if (!b.attr('originalwidth')) {
30617                 b.attr('originalwidth',  b.getSize().width);
30618             }
30619             
30620         });
30621         
30622         Roo.log(this.bricks.elements.length);
30623     },
30624     
30625     resize : function()
30626     {
30627         Roo.log('resize');
30628         var cs = this.el.getBox(true);
30629         
30630         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
30631             Roo.log("no change in with or X");
30632             return;
30633         }
30634         this.currentSize = cs;
30635         this.layout();
30636     },
30637     
30638     layout : function()
30639     {
30640          Roo.log('layout');
30641         this._resetLayout();
30642         //this._manageStamps();
30643       
30644         // don't animate first layout
30645         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30646         this.layoutItems( isInstant );
30647       
30648         // flag for initalized
30649         this._isLayoutInited = true;
30650     },
30651     
30652     layoutItems : function( isInstant )
30653     {
30654         //var items = this._getItemsForLayout( this.items );
30655         // original code supports filtering layout items.. we just ignore it..
30656         
30657         this._layoutItems( this.bricks , isInstant );
30658       
30659         this._postLayout();
30660     },
30661     _layoutItems : function ( items , isInstant)
30662     {
30663        //this.fireEvent( 'layout', this, items );
30664     
30665
30666         if ( !items || !items.elements.length ) {
30667           // no items, emit event with empty array
30668             return;
30669         }
30670
30671         var queue = [];
30672         items.each(function(item) {
30673             Roo.log("layout item");
30674             Roo.log(item);
30675             // get x/y object from method
30676             var position = this._getItemLayoutPosition( item );
30677             // enqueue
30678             position.item = item;
30679             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
30680             queue.push( position );
30681         }, this);
30682       
30683         this._processLayoutQueue( queue );
30684     },
30685     /** Sets position of item in DOM
30686     * @param {Element} item
30687     * @param {Number} x - horizontal position
30688     * @param {Number} y - vertical position
30689     * @param {Boolean} isInstant - disables transitions
30690     */
30691     _processLayoutQueue : function( queue )
30692     {
30693         for ( var i=0, len = queue.length; i < len; i++ ) {
30694             var obj = queue[i];
30695             obj.item.position('absolute');
30696             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
30697         }
30698     },
30699       
30700     
30701     /**
30702     * Any logic you want to do after each layout,
30703     * i.e. size the container
30704     */
30705     _postLayout : function()
30706     {
30707         this.resizeContainer();
30708     },
30709     
30710     resizeContainer : function()
30711     {
30712         if ( !this.isResizingContainer ) {
30713             return;
30714         }
30715         var size = this._getContainerSize();
30716         if ( size ) {
30717             this.el.setSize(size.width,size.height);
30718             this.boxesEl.setSize(size.width,size.height);
30719         }
30720     },
30721     
30722     
30723     
30724     _resetLayout : function()
30725     {
30726         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
30727         this.colWidth = this.el.getWidth();
30728         //this.gutter = this.el.getWidth(); 
30729         
30730         this.measureColumns();
30731
30732         // reset column Y
30733         var i = this.cols;
30734         this.colYs = [];
30735         while (i--) {
30736             this.colYs.push( 0 );
30737         }
30738     
30739         this.maxY = 0;
30740     },
30741
30742     measureColumns : function()
30743     {
30744         this.getContainerWidth();
30745       // if columnWidth is 0, default to outerWidth of first item
30746         if ( !this.columnWidth ) {
30747             var firstItem = this.bricks.first();
30748             Roo.log(firstItem);
30749             this.columnWidth  = this.containerWidth;
30750             if (firstItem && firstItem.attr('originalwidth') ) {
30751                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
30752             }
30753             // columnWidth fall back to item of first element
30754             Roo.log("set column width?");
30755                         this.initialColumnWidth = this.columnWidth  ;
30756
30757             // if first elem has no width, default to size of container
30758             
30759         }
30760         
30761         
30762         if (this.initialColumnWidth) {
30763             this.columnWidth = this.initialColumnWidth;
30764         }
30765         
30766         
30767             
30768         // column width is fixed at the top - however if container width get's smaller we should
30769         // reduce it...
30770         
30771         // this bit calcs how man columns..
30772             
30773         var columnWidth = this.columnWidth += this.gutter;
30774       
30775         // calculate columns
30776         var containerWidth = this.containerWidth + this.gutter;
30777         
30778         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
30779         // fix rounding errors, typically with gutters
30780         var excess = columnWidth - containerWidth % columnWidth;
30781         
30782         
30783         // if overshoot is less than a pixel, round up, otherwise floor it
30784         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
30785         cols = Math[ mathMethod ]( cols );
30786         this.cols = Math.max( cols, 1 );
30787         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30788         
30789          // padding positioning..
30790         var totalColWidth = this.cols * this.columnWidth;
30791         var padavail = this.containerWidth - totalColWidth;
30792         // so for 2 columns - we need 3 'pads'
30793         
30794         var padNeeded = (1+this.cols) * this.padWidth;
30795         
30796         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
30797         
30798         this.columnWidth += padExtra
30799         //this.padWidth = Math.floor(padavail /  ( this.cols));
30800         
30801         // adjust colum width so that padding is fixed??
30802         
30803         // we have 3 columns ... total = width * 3
30804         // we have X left over... that should be used by 
30805         
30806         //if (this.expandC) {
30807             
30808         //}
30809         
30810         
30811         
30812     },
30813     
30814     getContainerWidth : function()
30815     {
30816        /* // container is parent if fit width
30817         var container = this.isFitWidth ? this.element.parentNode : this.element;
30818         // check that this.size and size are there
30819         // IE8 triggers resize on body size change, so they might not be
30820         
30821         var size = getSize( container );  //FIXME
30822         this.containerWidth = size && size.innerWidth; //FIXME
30823         */
30824          
30825         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30826         
30827     },
30828     
30829     _getItemLayoutPosition : function( item )  // what is item?
30830     {
30831         // we resize the item to our columnWidth..
30832       
30833         item.setWidth(this.columnWidth);
30834         item.autoBoxAdjust  = false;
30835         
30836         var sz = item.getSize();
30837  
30838         // how many columns does this brick span
30839         var remainder = this.containerWidth % this.columnWidth;
30840         
30841         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30842         // round if off by 1 pixel, otherwise use ceil
30843         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
30844         colSpan = Math.min( colSpan, this.cols );
30845         
30846         // normally this should be '1' as we dont' currently allow multi width columns..
30847         
30848         var colGroup = this._getColGroup( colSpan );
30849         // get the minimum Y value from the columns
30850         var minimumY = Math.min.apply( Math, colGroup );
30851         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30852         
30853         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
30854          
30855         // position the brick
30856         var position = {
30857             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30858             y: this.currentSize.y + minimumY + this.padHeight
30859         };
30860         
30861         Roo.log(position);
30862         // apply setHeight to necessary columns
30863         var setHeight = minimumY + sz.height + this.padHeight;
30864         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30865         
30866         var setSpan = this.cols + 1 - colGroup.length;
30867         for ( var i = 0; i < setSpan; i++ ) {
30868           this.colYs[ shortColIndex + i ] = setHeight ;
30869         }
30870       
30871         return position;
30872     },
30873     
30874     /**
30875      * @param {Number} colSpan - number of columns the element spans
30876      * @returns {Array} colGroup
30877      */
30878     _getColGroup : function( colSpan )
30879     {
30880         if ( colSpan < 2 ) {
30881           // if brick spans only one column, use all the column Ys
30882           return this.colYs;
30883         }
30884       
30885         var colGroup = [];
30886         // how many different places could this brick fit horizontally
30887         var groupCount = this.cols + 1 - colSpan;
30888         // for each group potential horizontal position
30889         for ( var i = 0; i < groupCount; i++ ) {
30890           // make an array of colY values for that one group
30891           var groupColYs = this.colYs.slice( i, i + colSpan );
30892           // and get the max value of the array
30893           colGroup[i] = Math.max.apply( Math, groupColYs );
30894         }
30895         return colGroup;
30896     },
30897     /*
30898     _manageStamp : function( stamp )
30899     {
30900         var stampSize =  stamp.getSize();
30901         var offset = stamp.getBox();
30902         // get the columns that this stamp affects
30903         var firstX = this.isOriginLeft ? offset.x : offset.right;
30904         var lastX = firstX + stampSize.width;
30905         var firstCol = Math.floor( firstX / this.columnWidth );
30906         firstCol = Math.max( 0, firstCol );
30907         
30908         var lastCol = Math.floor( lastX / this.columnWidth );
30909         // lastCol should not go over if multiple of columnWidth #425
30910         lastCol -= lastX % this.columnWidth ? 0 : 1;
30911         lastCol = Math.min( this.cols - 1, lastCol );
30912         
30913         // set colYs to bottom of the stamp
30914         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30915             stampSize.height;
30916             
30917         for ( var i = firstCol; i <= lastCol; i++ ) {
30918           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30919         }
30920     },
30921     */
30922     
30923     _getContainerSize : function()
30924     {
30925         this.maxY = Math.max.apply( Math, this.colYs );
30926         var size = {
30927             height: this.maxY
30928         };
30929       
30930         if ( this.isFitWidth ) {
30931             size.width = this._getContainerFitWidth();
30932         }
30933       
30934         return size;
30935     },
30936     
30937     _getContainerFitWidth : function()
30938     {
30939         var unusedCols = 0;
30940         // count unused columns
30941         var i = this.cols;
30942         while ( --i ) {
30943           if ( this.colYs[i] !== 0 ) {
30944             break;
30945           }
30946           unusedCols++;
30947         }
30948         // fit container to columns that have been used
30949         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30950     },
30951     
30952     needsResizeLayout : function()
30953     {
30954         var previousWidth = this.containerWidth;
30955         this.getContainerWidth();
30956         return previousWidth !== this.containerWidth;
30957     }
30958  
30959 });
30960
30961  
30962
30963  /*
30964  * - LGPL
30965  *
30966  * element
30967  * 
30968  */
30969
30970 /**
30971  * @class Roo.bootstrap.MasonryBrick
30972  * @extends Roo.bootstrap.Component
30973  * Bootstrap MasonryBrick class
30974  * 
30975  * @constructor
30976  * Create a new MasonryBrick
30977  * @param {Object} config The config object
30978  */
30979
30980 Roo.bootstrap.MasonryBrick = function(config){
30981     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
30982     
30983     this.addEvents({
30984         // raw events
30985         /**
30986          * @event click
30987          * When a MasonryBrick is clcik
30988          * @param {Roo.bootstrap.MasonryBrick} this
30989          * @param {Roo.EventObject} e
30990          */
30991         "click" : true
30992     });
30993 };
30994
30995 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
30996     
30997     /**
30998      * @cfg {String} title
30999      */   
31000     title : '',
31001     /**
31002      * @cfg {String} html
31003      */   
31004     html : '',
31005     /**
31006      * @cfg {String} bgimage
31007      */   
31008     bgimage : '',
31009     /**
31010      * @cfg {String} videourl
31011      */   
31012     videourl : '',
31013     /**
31014      * @cfg {String} cls
31015      */   
31016     cls : '',
31017     /**
31018      * @cfg {String} href
31019      */   
31020     href : '',
31021     /**
31022      * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
31023      */   
31024     size : 'xs',
31025     
31026     /**
31027      * @cfg {String} (center|bottom) placetitle
31028      */   
31029     placetitle : '',
31030     
31031     /**
31032      * @cfg {Boolean} isFitContainer defalut true
31033      */   
31034     isFitContainer : true, 
31035     
31036     /**
31037      * @cfg {Boolean} preventDefault defalut false
31038      */   
31039     preventDefault : false, 
31040     
31041     getAutoCreate : function()
31042     {
31043         if(!this.isFitContainer){
31044             return this.getSplitAutoCreate();
31045         }
31046         
31047         var cls = 'masonry-brick masonry-brick-full';
31048         
31049         if(this.href.length){
31050             cls += ' masonry-brick-link';
31051         }
31052         
31053         if(this.bgimage.length){
31054             cls += ' masonry-brick-image';
31055         }
31056         
31057         if(!this.html.length){
31058             cls += ' enable-mask';
31059         }
31060         
31061         if(this.size){
31062             cls += ' masonry-' + this.size + '-brick';
31063         }
31064         
31065         if(this.placetitle.length){
31066             
31067             switch (this.placetitle) {
31068                 case 'center' :
31069                     cls += ' masonry-center-title';
31070                     break;
31071                 case 'bottom' :
31072                     cls += ' masonry-bottom-title';
31073                     break;
31074                 default:
31075                     break;
31076             }
31077             
31078         } else {
31079             if(!this.html.length && !this.bgimage.length){
31080                 cls += ' masonry-center-title';
31081             }
31082
31083             if(!this.html.length && this.bgimage.length){
31084                 cls += ' masonry-bottom-title';
31085             }
31086         }
31087         
31088         if(this.cls){
31089             cls += ' ' + this.cls;
31090         }
31091         
31092         var cfg = {
31093             tag: (this.href.length) ? 'a' : 'div',
31094             cls: cls,
31095             cn: [
31096                 {
31097                     tag: 'div',
31098                     cls: 'masonry-brick-paragraph',
31099                     cn: []
31100                 }
31101             ]
31102         };
31103         
31104         if(this.href.length){
31105             cfg.href = this.href;
31106         }
31107         
31108         var cn = cfg.cn[0].cn;
31109         
31110         if(this.title.length){
31111             cn.push({
31112                 tag: 'h4',
31113                 cls: 'masonry-brick-title',
31114                 html: this.title
31115             });
31116         }
31117         
31118         if(this.html.length){
31119             cn.push({
31120                 tag: 'p',
31121                 cls: 'masonry-brick-text',
31122                 html: this.html
31123             });
31124         }  
31125         if (!this.title.length && !this.html.length) {
31126             cfg.cn[0].cls += ' hide';
31127         }
31128         
31129         if(this.bgimage.length){
31130             cfg.cn.push({
31131                 tag: 'img',
31132                 cls: 'masonry-brick-image-view',
31133                 src: this.bgimage
31134             });
31135         }
31136         
31137         if(this.videourl.length){
31138             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31139             // youtube support only?
31140             cfg.cn.push({
31141                 tag: 'iframe',
31142                 cls: 'masonry-brick-image-view',
31143                 src: vurl,
31144                 frameborder : 0,
31145                 allowfullscreen : true
31146             });
31147             
31148             
31149         }
31150         
31151         cfg.cn.push({
31152             tag: 'div',
31153             cls: 'masonry-brick-mask'
31154         });
31155         
31156         return cfg;
31157         
31158     },
31159     
31160     getSplitAutoCreate : function()
31161     {
31162         var cls = 'masonry-brick masonry-brick-split';
31163         
31164         if(this.href.length){
31165             cls += ' masonry-brick-link';
31166         }
31167         
31168         if(this.bgimage.length){
31169             cls += ' masonry-brick-image';
31170         }
31171         
31172         if(this.size){
31173             cls += ' masonry-' + this.size + '-brick';
31174         }
31175         
31176         switch (this.placetitle) {
31177             case 'center' :
31178                 cls += ' masonry-center-title';
31179                 break;
31180             case 'bottom' :
31181                 cls += ' masonry-bottom-title';
31182                 break;
31183             default:
31184                 if(!this.bgimage.length){
31185                     cls += ' masonry-center-title';
31186                 }
31187
31188                 if(this.bgimage.length){
31189                     cls += ' masonry-bottom-title';
31190                 }
31191                 break;
31192         }
31193         
31194         if(this.cls){
31195             cls += ' ' + this.cls;
31196         }
31197         
31198         var cfg = {
31199             tag: (this.href.length) ? 'a' : 'div',
31200             cls: cls,
31201             cn: [
31202                 {
31203                     tag: 'div',
31204                     cls: 'masonry-brick-split-head',
31205                     cn: [
31206                         {
31207                             tag: 'div',
31208                             cls: 'masonry-brick-paragraph',
31209                             cn: []
31210                         }
31211                     ]
31212                 },
31213                 {
31214                     tag: 'div',
31215                     cls: 'masonry-brick-split-body',
31216                     cn: []
31217                 }
31218             ]
31219         };
31220         
31221         if(this.href.length){
31222             cfg.href = this.href;
31223         }
31224         
31225         if(this.title.length){
31226             cfg.cn[0].cn[0].cn.push({
31227                 tag: 'h4',
31228                 cls: 'masonry-brick-title',
31229                 html: this.title
31230             });
31231         }
31232         
31233         if(this.html.length){
31234             cfg.cn[1].cn.push({
31235                 tag: 'p',
31236                 cls: 'masonry-brick-text',
31237                 html: this.html
31238             });
31239         }
31240
31241         if(this.bgimage.length){
31242             cfg.cn[0].cn.push({
31243                 tag: 'img',
31244                 cls: 'masonry-brick-image-view',
31245                 src: this.bgimage
31246             });
31247         }
31248         
31249         if(this.videourl.length){
31250             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31251             // youtube support only?
31252             cfg.cn[0].cn.cn.push({
31253                 tag: 'iframe',
31254                 cls: 'masonry-brick-image-view',
31255                 src: vurl,
31256                 frameborder : 0,
31257                 allowfullscreen : true
31258             });
31259         }
31260         
31261         return cfg;
31262     },
31263     
31264     initEvents: function() 
31265     {
31266         switch (this.size) {
31267             case 'xs' :
31268                 this.x = 1;
31269                 this.y = 1;
31270                 break;
31271             case 'sm' :
31272                 this.x = 2;
31273                 this.y = 2;
31274                 break;
31275             case 'md' :
31276             case 'md-left' :
31277             case 'md-right' :
31278                 this.x = 3;
31279                 this.y = 3;
31280                 break;
31281             case 'tall' :
31282                 this.x = 2;
31283                 this.y = 3;
31284                 break;
31285             case 'wide' :
31286                 this.x = 3;
31287                 this.y = 2;
31288                 break;
31289             case 'wide-thin' :
31290                 this.x = 3;
31291                 this.y = 1;
31292                 break;
31293                         
31294             default :
31295                 break;
31296         }
31297         
31298         if(Roo.isTouch){
31299             this.el.on('touchstart', this.onTouchStart, this);
31300             this.el.on('touchmove', this.onTouchMove, this);
31301             this.el.on('touchend', this.onTouchEnd, this);
31302             this.el.on('contextmenu', this.onContextMenu, this);
31303         } else {
31304             this.el.on('mouseenter'  ,this.enter, this);
31305             this.el.on('mouseleave', this.leave, this);
31306             this.el.on('click', this.onClick, this);
31307         }
31308         
31309         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
31310             this.parent().bricks.push(this);   
31311         }
31312         
31313     },
31314     
31315     onClick: function(e, el)
31316     {
31317         var time = this.endTimer - this.startTimer;
31318         
31319         if(Roo.isTouch){
31320             if(time > 1000){
31321                 e.preventDefault();
31322                 return;
31323             }
31324         }
31325         
31326         if(!this.preventDefault){
31327             return;
31328         }
31329         
31330         e.preventDefault();
31331         this.fireEvent('click', this);
31332     },
31333     
31334     enter: function(e, el)
31335     {
31336         e.preventDefault();
31337         
31338         if(!this.isFitContainer){
31339             return;
31340         }
31341         
31342         if(this.bgimage.length && this.html.length){
31343             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31344         }
31345     },
31346     
31347     leave: function(e, el)
31348     {
31349         e.preventDefault();
31350         
31351         if(!this.isFitContainer){
31352             return;
31353         }
31354         
31355         if(this.bgimage.length && this.html.length){
31356             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31357         }
31358     },
31359     
31360     onTouchStart: function(e, el)
31361     {
31362 //        e.preventDefault();
31363         
31364         this.touchmoved = false;
31365         
31366         if(!this.isFitContainer){
31367             return;
31368         }
31369         
31370         if(!this.bgimage.length || !this.html.length){
31371             return;
31372         }
31373         
31374         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31375         
31376         this.timer = new Date().getTime();
31377         
31378     },
31379     
31380     onTouchMove: function(e, el)
31381     {
31382         this.touchmoved = true;
31383     },
31384     
31385     onContextMenu : function(e,el)
31386     {
31387         e.preventDefault();
31388         e.stopPropagation();
31389         return false;
31390     },
31391     
31392     onTouchEnd: function(e, el)
31393     {
31394 //        e.preventDefault();
31395         
31396         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
31397         
31398             this.leave(e,el);
31399             
31400             return;
31401         }
31402         
31403         if(!this.bgimage.length || !this.html.length){
31404             
31405             if(this.href.length){
31406                 window.location.href = this.href;
31407             }
31408             
31409             return;
31410         }
31411         
31412         if(!this.isFitContainer){
31413             return;
31414         }
31415         
31416         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31417         
31418         window.location.href = this.href;
31419     }
31420     
31421 });
31422
31423  
31424
31425  /*
31426  * - LGPL
31427  *
31428  * element
31429  * 
31430  */
31431
31432 /**
31433  * @class Roo.bootstrap.Brick
31434  * @extends Roo.bootstrap.Component
31435  * Bootstrap Brick class
31436  * 
31437  * @constructor
31438  * Create a new Brick
31439  * @param {Object} config The config object
31440  */
31441
31442 Roo.bootstrap.Brick = function(config){
31443     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
31444     
31445     this.addEvents({
31446         // raw events
31447         /**
31448          * @event click
31449          * When a Brick is click
31450          * @param {Roo.bootstrap.Brick} this
31451          * @param {Roo.EventObject} e
31452          */
31453         "click" : true
31454     });
31455 };
31456
31457 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
31458     
31459     /**
31460      * @cfg {String} title
31461      */   
31462     title : '',
31463     /**
31464      * @cfg {String} html
31465      */   
31466     html : '',
31467     /**
31468      * @cfg {String} bgimage
31469      */   
31470     bgimage : '',
31471     /**
31472      * @cfg {String} cls
31473      */   
31474     cls : '',
31475     /**
31476      * @cfg {String} href
31477      */   
31478     href : '',
31479     /**
31480      * @cfg {String} video
31481      */   
31482     video : '',
31483     /**
31484      * @cfg {Boolean} square
31485      */   
31486     square : true,
31487     
31488     getAutoCreate : function()
31489     {
31490         var cls = 'roo-brick';
31491         
31492         if(this.href.length){
31493             cls += ' roo-brick-link';
31494         }
31495         
31496         if(this.bgimage.length){
31497             cls += ' roo-brick-image';
31498         }
31499         
31500         if(!this.html.length && !this.bgimage.length){
31501             cls += ' roo-brick-center-title';
31502         }
31503         
31504         if(!this.html.length && this.bgimage.length){
31505             cls += ' roo-brick-bottom-title';
31506         }
31507         
31508         if(this.cls){
31509             cls += ' ' + this.cls;
31510         }
31511         
31512         var cfg = {
31513             tag: (this.href.length) ? 'a' : 'div',
31514             cls: cls,
31515             cn: [
31516                 {
31517                     tag: 'div',
31518                     cls: 'roo-brick-paragraph',
31519                     cn: []
31520                 }
31521             ]
31522         };
31523         
31524         if(this.href.length){
31525             cfg.href = this.href;
31526         }
31527         
31528         var cn = cfg.cn[0].cn;
31529         
31530         if(this.title.length){
31531             cn.push({
31532                 tag: 'h4',
31533                 cls: 'roo-brick-title',
31534                 html: this.title
31535             });
31536         }
31537         
31538         if(this.html.length){
31539             cn.push({
31540                 tag: 'p',
31541                 cls: 'roo-brick-text',
31542                 html: this.html
31543             });
31544         } else {
31545             cn.cls += ' hide';
31546         }
31547         
31548         if(this.bgimage.length){
31549             cfg.cn.push({
31550                 tag: 'img',
31551                 cls: 'roo-brick-image-view',
31552                 src: this.bgimage
31553             });
31554         }
31555         
31556         return cfg;
31557     },
31558     
31559     initEvents: function() 
31560     {
31561         if(this.title.length || this.html.length){
31562             this.el.on('mouseenter'  ,this.enter, this);
31563             this.el.on('mouseleave', this.leave, this);
31564         }
31565         
31566         
31567         Roo.EventManager.onWindowResize(this.resize, this); 
31568         
31569         this.resize();
31570     },
31571     
31572     resize : function()
31573     {
31574         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
31575         
31576         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
31577         
31578         if(this.bgimage.length){
31579             var image = this.el.select('.roo-brick-image-view', true).first();
31580             image.setWidth(paragraph.getWidth());
31581             image.setHeight(paragraph.getWidth());
31582             
31583             this.el.setHeight(paragraph.getWidth());
31584             
31585         }
31586         
31587     },
31588     
31589     enter: function(e, el)
31590     {
31591         e.preventDefault();
31592         
31593         if(this.bgimage.length){
31594             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
31595             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
31596         }
31597     },
31598     
31599     leave: function(e, el)
31600     {
31601         e.preventDefault();
31602         
31603         if(this.bgimage.length){
31604             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
31605             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
31606         }
31607     }
31608     
31609 });
31610
31611  
31612
31613  /*
31614  * Based on:
31615  * Ext JS Library 1.1.1
31616  * Copyright(c) 2006-2007, Ext JS, LLC.
31617  *
31618  * Originally Released Under LGPL - original licence link has changed is not relivant.
31619  *
31620  * Fork - LGPL
31621  * <script type="text/javascript">
31622  */
31623
31624
31625 /**
31626  * @class Roo.bootstrap.SplitBar
31627  * @extends Roo.util.Observable
31628  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
31629  * <br><br>
31630  * Usage:
31631  * <pre><code>
31632 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
31633                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
31634 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
31635 split.minSize = 100;
31636 split.maxSize = 600;
31637 split.animate = true;
31638 split.on('moved', splitterMoved);
31639 </code></pre>
31640  * @constructor
31641  * Create a new SplitBar
31642  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
31643  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
31644  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31645  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
31646                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
31647                         position of the SplitBar).
31648  */
31649 Roo.bootstrap.SplitBar = function(cfg){
31650     
31651     /** @private */
31652     
31653     //{
31654     //  dragElement : elm
31655     //  resizingElement: el,
31656         // optional..
31657     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
31658     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
31659         // existingProxy ???
31660     //}
31661     
31662     this.el = Roo.get(cfg.dragElement, true);
31663     this.el.dom.unselectable = "on";
31664     /** @private */
31665     this.resizingEl = Roo.get(cfg.resizingElement, true);
31666
31667     /**
31668      * @private
31669      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31670      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
31671      * @type Number
31672      */
31673     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
31674     
31675     /**
31676      * The minimum size of the resizing element. (Defaults to 0)
31677      * @type Number
31678      */
31679     this.minSize = 0;
31680     
31681     /**
31682      * The maximum size of the resizing element. (Defaults to 2000)
31683      * @type Number
31684      */
31685     this.maxSize = 2000;
31686     
31687     /**
31688      * Whether to animate the transition to the new size
31689      * @type Boolean
31690      */
31691     this.animate = false;
31692     
31693     /**
31694      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
31695      * @type Boolean
31696      */
31697     this.useShim = false;
31698     
31699     /** @private */
31700     this.shim = null;
31701     
31702     if(!cfg.existingProxy){
31703         /** @private */
31704         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
31705     }else{
31706         this.proxy = Roo.get(cfg.existingProxy).dom;
31707     }
31708     /** @private */
31709     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
31710     
31711     /** @private */
31712     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
31713     
31714     /** @private */
31715     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
31716     
31717     /** @private */
31718     this.dragSpecs = {};
31719     
31720     /**
31721      * @private The adapter to use to positon and resize elements
31722      */
31723     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31724     this.adapter.init(this);
31725     
31726     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31727         /** @private */
31728         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
31729         this.el.addClass("roo-splitbar-h");
31730     }else{
31731         /** @private */
31732         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
31733         this.el.addClass("roo-splitbar-v");
31734     }
31735     
31736     this.addEvents({
31737         /**
31738          * @event resize
31739          * Fires when the splitter is moved (alias for {@link #event-moved})
31740          * @param {Roo.bootstrap.SplitBar} this
31741          * @param {Number} newSize the new width or height
31742          */
31743         "resize" : true,
31744         /**
31745          * @event moved
31746          * Fires when the splitter is moved
31747          * @param {Roo.bootstrap.SplitBar} this
31748          * @param {Number} newSize the new width or height
31749          */
31750         "moved" : true,
31751         /**
31752          * @event beforeresize
31753          * Fires before the splitter is dragged
31754          * @param {Roo.bootstrap.SplitBar} this
31755          */
31756         "beforeresize" : true,
31757
31758         "beforeapply" : true
31759     });
31760
31761     Roo.util.Observable.call(this);
31762 };
31763
31764 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
31765     onStartProxyDrag : function(x, y){
31766         this.fireEvent("beforeresize", this);
31767         if(!this.overlay){
31768             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
31769             o.unselectable();
31770             o.enableDisplayMode("block");
31771             // all splitbars share the same overlay
31772             Roo.bootstrap.SplitBar.prototype.overlay = o;
31773         }
31774         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
31775         this.overlay.show();
31776         Roo.get(this.proxy).setDisplayed("block");
31777         var size = this.adapter.getElementSize(this);
31778         this.activeMinSize = this.getMinimumSize();;
31779         this.activeMaxSize = this.getMaximumSize();;
31780         var c1 = size - this.activeMinSize;
31781         var c2 = Math.max(this.activeMaxSize - size, 0);
31782         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31783             this.dd.resetConstraints();
31784             this.dd.setXConstraint(
31785                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
31786                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
31787             );
31788             this.dd.setYConstraint(0, 0);
31789         }else{
31790             this.dd.resetConstraints();
31791             this.dd.setXConstraint(0, 0);
31792             this.dd.setYConstraint(
31793                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
31794                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
31795             );
31796          }
31797         this.dragSpecs.startSize = size;
31798         this.dragSpecs.startPoint = [x, y];
31799         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
31800     },
31801     
31802     /** 
31803      * @private Called after the drag operation by the DDProxy
31804      */
31805     onEndProxyDrag : function(e){
31806         Roo.get(this.proxy).setDisplayed(false);
31807         var endPoint = Roo.lib.Event.getXY(e);
31808         if(this.overlay){
31809             this.overlay.hide();
31810         }
31811         var newSize;
31812         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31813             newSize = this.dragSpecs.startSize + 
31814                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
31815                     endPoint[0] - this.dragSpecs.startPoint[0] :
31816                     this.dragSpecs.startPoint[0] - endPoint[0]
31817                 );
31818         }else{
31819             newSize = this.dragSpecs.startSize + 
31820                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
31821                     endPoint[1] - this.dragSpecs.startPoint[1] :
31822                     this.dragSpecs.startPoint[1] - endPoint[1]
31823                 );
31824         }
31825         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
31826         if(newSize != this.dragSpecs.startSize){
31827             if(this.fireEvent('beforeapply', this, newSize) !== false){
31828                 this.adapter.setElementSize(this, newSize);
31829                 this.fireEvent("moved", this, newSize);
31830                 this.fireEvent("resize", this, newSize);
31831             }
31832         }
31833     },
31834     
31835     /**
31836      * Get the adapter this SplitBar uses
31837      * @return The adapter object
31838      */
31839     getAdapter : function(){
31840         return this.adapter;
31841     },
31842     
31843     /**
31844      * Set the adapter this SplitBar uses
31845      * @param {Object} adapter A SplitBar adapter object
31846      */
31847     setAdapter : function(adapter){
31848         this.adapter = adapter;
31849         this.adapter.init(this);
31850     },
31851     
31852     /**
31853      * Gets the minimum size for the resizing element
31854      * @return {Number} The minimum size
31855      */
31856     getMinimumSize : function(){
31857         return this.minSize;
31858     },
31859     
31860     /**
31861      * Sets the minimum size for the resizing element
31862      * @param {Number} minSize The minimum size
31863      */
31864     setMinimumSize : function(minSize){
31865         this.minSize = minSize;
31866     },
31867     
31868     /**
31869      * Gets the maximum size for the resizing element
31870      * @return {Number} The maximum size
31871      */
31872     getMaximumSize : function(){
31873         return this.maxSize;
31874     },
31875     
31876     /**
31877      * Sets the maximum size for the resizing element
31878      * @param {Number} maxSize The maximum size
31879      */
31880     setMaximumSize : function(maxSize){
31881         this.maxSize = maxSize;
31882     },
31883     
31884     /**
31885      * Sets the initialize size for the resizing element
31886      * @param {Number} size The initial size
31887      */
31888     setCurrentSize : function(size){
31889         var oldAnimate = this.animate;
31890         this.animate = false;
31891         this.adapter.setElementSize(this, size);
31892         this.animate = oldAnimate;
31893     },
31894     
31895     /**
31896      * Destroy this splitbar. 
31897      * @param {Boolean} removeEl True to remove the element
31898      */
31899     destroy : function(removeEl){
31900         if(this.shim){
31901             this.shim.remove();
31902         }
31903         this.dd.unreg();
31904         this.proxy.parentNode.removeChild(this.proxy);
31905         if(removeEl){
31906             this.el.remove();
31907         }
31908     }
31909 });
31910
31911 /**
31912  * @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.
31913  */
31914 Roo.bootstrap.SplitBar.createProxy = function(dir){
31915     var proxy = new Roo.Element(document.createElement("div"));
31916     proxy.unselectable();
31917     var cls = 'roo-splitbar-proxy';
31918     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
31919     document.body.appendChild(proxy.dom);
31920     return proxy.dom;
31921 };
31922
31923 /** 
31924  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
31925  * Default Adapter. It assumes the splitter and resizing element are not positioned
31926  * elements and only gets/sets the width of the element. Generally used for table based layouts.
31927  */
31928 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
31929 };
31930
31931 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
31932     // do nothing for now
31933     init : function(s){
31934     
31935     },
31936     /**
31937      * Called before drag operations to get the current size of the resizing element. 
31938      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31939      */
31940      getElementSize : function(s){
31941         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31942             return s.resizingEl.getWidth();
31943         }else{
31944             return s.resizingEl.getHeight();
31945         }
31946     },
31947     
31948     /**
31949      * Called after drag operations to set the size of the resizing element.
31950      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31951      * @param {Number} newSize The new size to set
31952      * @param {Function} onComplete A function to be invoked when resizing is complete
31953      */
31954     setElementSize : function(s, newSize, onComplete){
31955         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31956             if(!s.animate){
31957                 s.resizingEl.setWidth(newSize);
31958                 if(onComplete){
31959                     onComplete(s, newSize);
31960                 }
31961             }else{
31962                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
31963             }
31964         }else{
31965             
31966             if(!s.animate){
31967                 s.resizingEl.setHeight(newSize);
31968                 if(onComplete){
31969                     onComplete(s, newSize);
31970                 }
31971             }else{
31972                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
31973             }
31974         }
31975     }
31976 };
31977
31978 /** 
31979  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
31980  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
31981  * Adapter that  moves the splitter element to align with the resized sizing element. 
31982  * Used with an absolute positioned SplitBar.
31983  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
31984  * document.body, make sure you assign an id to the body element.
31985  */
31986 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
31987     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31988     this.container = Roo.get(container);
31989 };
31990
31991 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
31992     init : function(s){
31993         this.basic.init(s);
31994     },
31995     
31996     getElementSize : function(s){
31997         return this.basic.getElementSize(s);
31998     },
31999     
32000     setElementSize : function(s, newSize, onComplete){
32001         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
32002     },
32003     
32004     moveSplitter : function(s){
32005         var yes = Roo.bootstrap.SplitBar;
32006         switch(s.placement){
32007             case yes.LEFT:
32008                 s.el.setX(s.resizingEl.getRight());
32009                 break;
32010             case yes.RIGHT:
32011                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
32012                 break;
32013             case yes.TOP:
32014                 s.el.setY(s.resizingEl.getBottom());
32015                 break;
32016             case yes.BOTTOM:
32017                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
32018                 break;
32019         }
32020     }
32021 };
32022
32023 /**
32024  * Orientation constant - Create a vertical SplitBar
32025  * @static
32026  * @type Number
32027  */
32028 Roo.bootstrap.SplitBar.VERTICAL = 1;
32029
32030 /**
32031  * Orientation constant - Create a horizontal SplitBar
32032  * @static
32033  * @type Number
32034  */
32035 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
32036
32037 /**
32038  * Placement constant - The resizing element is to the left of the splitter element
32039  * @static
32040  * @type Number
32041  */
32042 Roo.bootstrap.SplitBar.LEFT = 1;
32043
32044 /**
32045  * Placement constant - The resizing element is to the right of the splitter element
32046  * @static
32047  * @type Number
32048  */
32049 Roo.bootstrap.SplitBar.RIGHT = 2;
32050
32051 /**
32052  * Placement constant - The resizing element is positioned above the splitter element
32053  * @static
32054  * @type Number
32055  */
32056 Roo.bootstrap.SplitBar.TOP = 3;
32057
32058 /**
32059  * Placement constant - The resizing element is positioned under splitter element
32060  * @static
32061  * @type Number
32062  */
32063 Roo.bootstrap.SplitBar.BOTTOM = 4;
32064 Roo.namespace("Roo.bootstrap.layout");/*
32065  * Based on:
32066  * Ext JS Library 1.1.1
32067  * Copyright(c) 2006-2007, Ext JS, LLC.
32068  *
32069  * Originally Released Under LGPL - original licence link has changed is not relivant.
32070  *
32071  * Fork - LGPL
32072  * <script type="text/javascript">
32073  */
32074
32075 /**
32076  * @class Roo.bootstrap.layout.Manager
32077  * @extends Roo.bootstrap.Component
32078  * Base class for layout managers.
32079  */
32080 Roo.bootstrap.layout.Manager = function(config)
32081 {
32082     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
32083
32084
32085
32086
32087
32088     /** false to disable window resize monitoring @type Boolean */
32089     this.monitorWindowResize = true;
32090     this.regions = {};
32091     this.addEvents({
32092         /**
32093          * @event layout
32094          * Fires when a layout is performed.
32095          * @param {Roo.LayoutManager} this
32096          */
32097         "layout" : true,
32098         /**
32099          * @event regionresized
32100          * Fires when the user resizes a region.
32101          * @param {Roo.LayoutRegion} region The resized region
32102          * @param {Number} newSize The new size (width for east/west, height for north/south)
32103          */
32104         "regionresized" : true,
32105         /**
32106          * @event regioncollapsed
32107          * Fires when a region is collapsed.
32108          * @param {Roo.LayoutRegion} region The collapsed region
32109          */
32110         "regioncollapsed" : true,
32111         /**
32112          * @event regionexpanded
32113          * Fires when a region is expanded.
32114          * @param {Roo.LayoutRegion} region The expanded region
32115          */
32116         "regionexpanded" : true
32117     });
32118     this.updating = false;
32119
32120     if (config.el) {
32121         this.el = Roo.get(config.el);
32122         this.initEvents();
32123     }
32124
32125 };
32126
32127 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
32128
32129
32130     regions : null,
32131
32132     monitorWindowResize : true,
32133
32134
32135     updating : false,
32136
32137
32138     onRender : function(ct, position)
32139     {
32140         if(!this.el){
32141             this.el = Roo.get(ct);
32142             this.initEvents();
32143         }
32144         //this.fireEvent('render',this);
32145     },
32146
32147
32148     initEvents: function()
32149     {
32150
32151
32152         // ie scrollbar fix
32153         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32154             document.body.scroll = "no";
32155         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32156             this.el.position('relative');
32157         }
32158         this.id = this.el.id;
32159         this.el.addClass("roo-layout-container");
32160         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32161         if(this.el.dom != document.body ) {
32162             this.el.on('resize', this.layout,this);
32163             this.el.on('show', this.layout,this);
32164         }
32165
32166     },
32167
32168     /**
32169      * Returns true if this layout is currently being updated
32170      * @return {Boolean}
32171      */
32172     isUpdating : function(){
32173         return this.updating;
32174     },
32175
32176     /**
32177      * Suspend the LayoutManager from doing auto-layouts while
32178      * making multiple add or remove calls
32179      */
32180     beginUpdate : function(){
32181         this.updating = true;
32182     },
32183
32184     /**
32185      * Restore auto-layouts and optionally disable the manager from performing a layout
32186      * @param {Boolean} noLayout true to disable a layout update
32187      */
32188     endUpdate : function(noLayout){
32189         this.updating = false;
32190         if(!noLayout){
32191             this.layout();
32192         }
32193     },
32194
32195     layout: function(){
32196         // abstract...
32197     },
32198
32199     onRegionResized : function(region, newSize){
32200         this.fireEvent("regionresized", region, newSize);
32201         this.layout();
32202     },
32203
32204     onRegionCollapsed : function(region){
32205         this.fireEvent("regioncollapsed", region);
32206     },
32207
32208     onRegionExpanded : function(region){
32209         this.fireEvent("regionexpanded", region);
32210     },
32211
32212     /**
32213      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32214      * performs box-model adjustments.
32215      * @return {Object} The size as an object {width: (the width), height: (the height)}
32216      */
32217     getViewSize : function()
32218     {
32219         var size;
32220         if(this.el.dom != document.body){
32221             size = this.el.getSize();
32222         }else{
32223             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32224         }
32225         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32226         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32227         return size;
32228     },
32229
32230     /**
32231      * Returns the Element this layout is bound to.
32232      * @return {Roo.Element}
32233      */
32234     getEl : function(){
32235         return this.el;
32236     },
32237
32238     /**
32239      * Returns the specified region.
32240      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32241      * @return {Roo.LayoutRegion}
32242      */
32243     getRegion : function(target){
32244         return this.regions[target.toLowerCase()];
32245     },
32246
32247     onWindowResize : function(){
32248         if(this.monitorWindowResize){
32249             this.layout();
32250         }
32251     }
32252 });
32253 /*
32254  * Based on:
32255  * Ext JS Library 1.1.1
32256  * Copyright(c) 2006-2007, Ext JS, LLC.
32257  *
32258  * Originally Released Under LGPL - original licence link has changed is not relivant.
32259  *
32260  * Fork - LGPL
32261  * <script type="text/javascript">
32262  */
32263 /**
32264  * @class Roo.bootstrap.layout.Border
32265  * @extends Roo.bootstrap.layout.Manager
32266  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32267  * please see: examples/bootstrap/nested.html<br><br>
32268  
32269 <b>The container the layout is rendered into can be either the body element or any other element.
32270 If it is not the body element, the container needs to either be an absolute positioned element,
32271 or you will need to add "position:relative" to the css of the container.  You will also need to specify
32272 the container size if it is not the body element.</b>
32273
32274 * @constructor
32275 * Create a new Border
32276 * @param {Object} config Configuration options
32277  */
32278 Roo.bootstrap.layout.Border = function(config){
32279     config = config || {};
32280     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
32281     
32282     
32283     
32284     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
32285         if(config[region]){
32286             config[region].region = region;
32287             this.addRegion(config[region]);
32288         }
32289     },this);
32290     
32291 };
32292
32293 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
32294
32295 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
32296     /**
32297      * Creates and adds a new region if it doesn't already exist.
32298      * @param {String} target The target region key (north, south, east, west or center).
32299      * @param {Object} config The regions config object
32300      * @return {BorderLayoutRegion} The new region
32301      */
32302     addRegion : function(config)
32303     {
32304         if(!this.regions[config.region]){
32305             var r = this.factory(config);
32306             this.bindRegion(r);
32307         }
32308         return this.regions[config.region];
32309     },
32310
32311     // private (kinda)
32312     bindRegion : function(r){
32313         this.regions[r.config.region] = r;
32314         
32315         r.on("visibilitychange",    this.layout, this);
32316         r.on("paneladded",          this.layout, this);
32317         r.on("panelremoved",        this.layout, this);
32318         r.on("invalidated",         this.layout, this);
32319         r.on("resized",             this.onRegionResized, this);
32320         r.on("collapsed",           this.onRegionCollapsed, this);
32321         r.on("expanded",            this.onRegionExpanded, this);
32322     },
32323
32324     /**
32325      * Performs a layout update.
32326      */
32327     layout : function()
32328     {
32329         if(this.updating) {
32330             return;
32331         }
32332         
32333         // render all the rebions if they have not been done alreayd?
32334         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
32335             if(this.regions[region] && !this.regions[region].bodyEl){
32336                 this.regions[region].onRender(this.el)
32337             }
32338         },this);
32339         
32340         var size = this.getViewSize();
32341         var w = size.width;
32342         var h = size.height;
32343         var centerW = w;
32344         var centerH = h;
32345         var centerY = 0;
32346         var centerX = 0;
32347         //var x = 0, y = 0;
32348
32349         var rs = this.regions;
32350         var north = rs["north"];
32351         var south = rs["south"]; 
32352         var west = rs["west"];
32353         var east = rs["east"];
32354         var center = rs["center"];
32355         //if(this.hideOnLayout){ // not supported anymore
32356             //c.el.setStyle("display", "none");
32357         //}
32358         if(north && north.isVisible()){
32359             var b = north.getBox();
32360             var m = north.getMargins();
32361             b.width = w - (m.left+m.right);
32362             b.x = m.left;
32363             b.y = m.top;
32364             centerY = b.height + b.y + m.bottom;
32365             centerH -= centerY;
32366             north.updateBox(this.safeBox(b));
32367         }
32368         if(south && south.isVisible()){
32369             var b = south.getBox();
32370             var m = south.getMargins();
32371             b.width = w - (m.left+m.right);
32372             b.x = m.left;
32373             var totalHeight = (b.height + m.top + m.bottom);
32374             b.y = h - totalHeight + m.top;
32375             centerH -= totalHeight;
32376             south.updateBox(this.safeBox(b));
32377         }
32378         if(west && west.isVisible()){
32379             var b = west.getBox();
32380             var m = west.getMargins();
32381             b.height = centerH - (m.top+m.bottom);
32382             b.x = m.left;
32383             b.y = centerY + m.top;
32384             var totalWidth = (b.width + m.left + m.right);
32385             centerX += totalWidth;
32386             centerW -= totalWidth;
32387             west.updateBox(this.safeBox(b));
32388         }
32389         if(east && east.isVisible()){
32390             var b = east.getBox();
32391             var m = east.getMargins();
32392             b.height = centerH - (m.top+m.bottom);
32393             var totalWidth = (b.width + m.left + m.right);
32394             b.x = w - totalWidth + m.left;
32395             b.y = centerY + m.top;
32396             centerW -= totalWidth;
32397             east.updateBox(this.safeBox(b));
32398         }
32399         if(center){
32400             var m = center.getMargins();
32401             var centerBox = {
32402                 x: centerX + m.left,
32403                 y: centerY + m.top,
32404                 width: centerW - (m.left+m.right),
32405                 height: centerH - (m.top+m.bottom)
32406             };
32407             //if(this.hideOnLayout){
32408                 //center.el.setStyle("display", "block");
32409             //}
32410             center.updateBox(this.safeBox(centerBox));
32411         }
32412         this.el.repaint();
32413         this.fireEvent("layout", this);
32414     },
32415
32416     // private
32417     safeBox : function(box){
32418         box.width = Math.max(0, box.width);
32419         box.height = Math.max(0, box.height);
32420         return box;
32421     },
32422
32423     /**
32424      * Adds a ContentPanel (or subclass) to this layout.
32425      * @param {String} target The target region key (north, south, east, west or center).
32426      * @param {Roo.ContentPanel} panel The panel to add
32427      * @return {Roo.ContentPanel} The added panel
32428      */
32429     add : function(target, panel){
32430          
32431         target = target.toLowerCase();
32432         return this.regions[target].add(panel);
32433     },
32434
32435     /**
32436      * Remove a ContentPanel (or subclass) to this layout.
32437      * @param {String} target The target region key (north, south, east, west or center).
32438      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
32439      * @return {Roo.ContentPanel} The removed panel
32440      */
32441     remove : function(target, panel){
32442         target = target.toLowerCase();
32443         return this.regions[target].remove(panel);
32444     },
32445
32446     /**
32447      * Searches all regions for a panel with the specified id
32448      * @param {String} panelId
32449      * @return {Roo.ContentPanel} The panel or null if it wasn't found
32450      */
32451     findPanel : function(panelId){
32452         var rs = this.regions;
32453         for(var target in rs){
32454             if(typeof rs[target] != "function"){
32455                 var p = rs[target].getPanel(panelId);
32456                 if(p){
32457                     return p;
32458                 }
32459             }
32460         }
32461         return null;
32462     },
32463
32464     /**
32465      * Searches all regions for a panel with the specified id and activates (shows) it.
32466      * @param {String/ContentPanel} panelId The panels id or the panel itself
32467      * @return {Roo.ContentPanel} The shown panel or null
32468      */
32469     showPanel : function(panelId) {
32470       var rs = this.regions;
32471       for(var target in rs){
32472          var r = rs[target];
32473          if(typeof r != "function"){
32474             if(r.hasPanel(panelId)){
32475                return r.showPanel(panelId);
32476             }
32477          }
32478       }
32479       return null;
32480    },
32481
32482    /**
32483      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
32484      * @param {Roo.state.Provider} provider (optional) An alternate state provider
32485      */
32486    /*
32487     restoreState : function(provider){
32488         if(!provider){
32489             provider = Roo.state.Manager;
32490         }
32491         var sm = new Roo.LayoutStateManager();
32492         sm.init(this, provider);
32493     },
32494 */
32495  
32496  
32497     /**
32498      * Adds a xtype elements to the layout.
32499      * <pre><code>
32500
32501 layout.addxtype({
32502        xtype : 'ContentPanel',
32503        region: 'west',
32504        items: [ .... ]
32505    }
32506 );
32507
32508 layout.addxtype({
32509         xtype : 'NestedLayoutPanel',
32510         region: 'west',
32511         layout: {
32512            center: { },
32513            west: { }   
32514         },
32515         items : [ ... list of content panels or nested layout panels.. ]
32516    }
32517 );
32518 </code></pre>
32519      * @param {Object} cfg Xtype definition of item to add.
32520      */
32521     addxtype : function(cfg)
32522     {
32523         // basically accepts a pannel...
32524         // can accept a layout region..!?!?
32525         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
32526         
32527         
32528         // theory?  children can only be panels??
32529         
32530         //if (!cfg.xtype.match(/Panel$/)) {
32531         //    return false;
32532         //}
32533         var ret = false;
32534         
32535         if (typeof(cfg.region) == 'undefined') {
32536             Roo.log("Failed to add Panel, region was not set");
32537             Roo.log(cfg);
32538             return false;
32539         }
32540         var region = cfg.region;
32541         delete cfg.region;
32542         
32543           
32544         var xitems = [];
32545         if (cfg.items) {
32546             xitems = cfg.items;
32547             delete cfg.items;
32548         }
32549         var nb = false;
32550         
32551         switch(cfg.xtype) 
32552         {
32553             case 'Content':  // ContentPanel (el, cfg)
32554             case 'Scroll':  // ContentPanel (el, cfg)
32555             case 'View': 
32556                 cfg.autoCreate = true;
32557                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32558                 //} else {
32559                 //    var el = this.el.createChild();
32560                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
32561                 //}
32562                 
32563                 this.add(region, ret);
32564                 break;
32565             
32566             /*
32567             case 'TreePanel': // our new panel!
32568                 cfg.el = this.el.createChild();
32569                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32570                 this.add(region, ret);
32571                 break;
32572             */
32573             
32574             case 'Nest': 
32575                 // create a new Layout (which is  a Border Layout...
32576                 
32577                 var clayout = cfg.layout;
32578                 clayout.el  = this.el.createChild();
32579                 clayout.items   = clayout.items  || [];
32580                 
32581                 delete cfg.layout;
32582                 
32583                 // replace this exitems with the clayout ones..
32584                 xitems = clayout.items;
32585                  
32586                 // force background off if it's in center...
32587                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
32588                     cfg.background = false;
32589                 }
32590                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
32591                 
32592                 
32593                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32594                 //console.log('adding nested layout panel '  + cfg.toSource());
32595                 this.add(region, ret);
32596                 nb = {}; /// find first...
32597                 break;
32598             
32599             case 'Grid':
32600                 
32601                 // needs grid and region
32602                 
32603                 //var el = this.getRegion(region).el.createChild();
32604                 /*
32605                  *var el = this.el.createChild();
32606                 // create the grid first...
32607                 cfg.grid.container = el;
32608                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
32609                 */
32610                 
32611                 if (region == 'center' && this.active ) {
32612                     cfg.background = false;
32613                 }
32614                 
32615                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32616                 
32617                 this.add(region, ret);
32618                 /*
32619                 if (cfg.background) {
32620                     // render grid on panel activation (if panel background)
32621                     ret.on('activate', function(gp) {
32622                         if (!gp.grid.rendered) {
32623                     //        gp.grid.render(el);
32624                         }
32625                     });
32626                 } else {
32627                   //  cfg.grid.render(el);
32628                 }
32629                 */
32630                 break;
32631            
32632            
32633             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
32634                 // it was the old xcomponent building that caused this before.
32635                 // espeically if border is the top element in the tree.
32636                 ret = this;
32637                 break; 
32638                 
32639                     
32640                 
32641                 
32642                 
32643             default:
32644                 /*
32645                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
32646                     
32647                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32648                     this.add(region, ret);
32649                 } else {
32650                 */
32651                     Roo.log(cfg);
32652                     throw "Can not add '" + cfg.xtype + "' to Border";
32653                     return null;
32654              
32655                                 
32656              
32657         }
32658         this.beginUpdate();
32659         // add children..
32660         var region = '';
32661         var abn = {};
32662         Roo.each(xitems, function(i)  {
32663             region = nb && i.region ? i.region : false;
32664             
32665             var add = ret.addxtype(i);
32666            
32667             if (region) {
32668                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
32669                 if (!i.background) {
32670                     abn[region] = nb[region] ;
32671                 }
32672             }
32673             
32674         });
32675         this.endUpdate();
32676
32677         // make the last non-background panel active..
32678         //if (nb) { Roo.log(abn); }
32679         if (nb) {
32680             
32681             for(var r in abn) {
32682                 region = this.getRegion(r);
32683                 if (region) {
32684                     // tried using nb[r], but it does not work..
32685                      
32686                     region.showPanel(abn[r]);
32687                    
32688                 }
32689             }
32690         }
32691         return ret;
32692         
32693     },
32694     
32695     
32696 // private
32697     factory : function(cfg)
32698     {
32699         
32700         var validRegions = Roo.bootstrap.layout.Border.regions;
32701
32702         var target = cfg.region;
32703         cfg.mgr = this;
32704         
32705         var r = Roo.bootstrap.layout;
32706         Roo.log(target);
32707         switch(target){
32708             case "north":
32709                 return new r.North(cfg);
32710             case "south":
32711                 return new r.South(cfg);
32712             case "east":
32713                 return new r.East(cfg);
32714             case "west":
32715                 return new r.West(cfg);
32716             case "center":
32717                 return new r.Center(cfg);
32718         }
32719         throw 'Layout region "'+target+'" not supported.';
32720     }
32721     
32722     
32723 });
32724  /*
32725  * Based on:
32726  * Ext JS Library 1.1.1
32727  * Copyright(c) 2006-2007, Ext JS, LLC.
32728  *
32729  * Originally Released Under LGPL - original licence link has changed is not relivant.
32730  *
32731  * Fork - LGPL
32732  * <script type="text/javascript">
32733  */
32734  
32735 /**
32736  * @class Roo.bootstrap.layout.Basic
32737  * @extends Roo.util.Observable
32738  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
32739  * and does not have a titlebar, tabs or any other features. All it does is size and position 
32740  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
32741  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
32742  * @cfg {string}   region  the region that it inhabits..
32743  * @cfg {bool}   skipConfig skip config?
32744  * 
32745
32746  */
32747 Roo.bootstrap.layout.Basic = function(config){
32748     
32749     this.mgr = config.mgr;
32750     
32751     this.position = config.region;
32752     
32753     var skipConfig = config.skipConfig;
32754     
32755     this.events = {
32756         /**
32757          * @scope Roo.BasicLayoutRegion
32758          */
32759         
32760         /**
32761          * @event beforeremove
32762          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
32763          * @param {Roo.LayoutRegion} this
32764          * @param {Roo.ContentPanel} panel The panel
32765          * @param {Object} e The cancel event object
32766          */
32767         "beforeremove" : true,
32768         /**
32769          * @event invalidated
32770          * Fires when the layout for this region is changed.
32771          * @param {Roo.LayoutRegion} this
32772          */
32773         "invalidated" : true,
32774         /**
32775          * @event visibilitychange
32776          * Fires when this region is shown or hidden 
32777          * @param {Roo.LayoutRegion} this
32778          * @param {Boolean} visibility true or false
32779          */
32780         "visibilitychange" : true,
32781         /**
32782          * @event paneladded
32783          * Fires when a panel is added. 
32784          * @param {Roo.LayoutRegion} this
32785          * @param {Roo.ContentPanel} panel The panel
32786          */
32787         "paneladded" : true,
32788         /**
32789          * @event panelremoved
32790          * Fires when a panel is removed. 
32791          * @param {Roo.LayoutRegion} this
32792          * @param {Roo.ContentPanel} panel The panel
32793          */
32794         "panelremoved" : true,
32795         /**
32796          * @event beforecollapse
32797          * Fires when this region before collapse.
32798          * @param {Roo.LayoutRegion} this
32799          */
32800         "beforecollapse" : true,
32801         /**
32802          * @event collapsed
32803          * Fires when this region is collapsed.
32804          * @param {Roo.LayoutRegion} this
32805          */
32806         "collapsed" : true,
32807         /**
32808          * @event expanded
32809          * Fires when this region is expanded.
32810          * @param {Roo.LayoutRegion} this
32811          */
32812         "expanded" : true,
32813         /**
32814          * @event slideshow
32815          * Fires when this region is slid into view.
32816          * @param {Roo.LayoutRegion} this
32817          */
32818         "slideshow" : true,
32819         /**
32820          * @event slidehide
32821          * Fires when this region slides out of view. 
32822          * @param {Roo.LayoutRegion} this
32823          */
32824         "slidehide" : true,
32825         /**
32826          * @event panelactivated
32827          * Fires when a panel is activated. 
32828          * @param {Roo.LayoutRegion} this
32829          * @param {Roo.ContentPanel} panel The activated panel
32830          */
32831         "panelactivated" : true,
32832         /**
32833          * @event resized
32834          * Fires when the user resizes this region. 
32835          * @param {Roo.LayoutRegion} this
32836          * @param {Number} newSize The new size (width for east/west, height for north/south)
32837          */
32838         "resized" : true
32839     };
32840     /** A collection of panels in this region. @type Roo.util.MixedCollection */
32841     this.panels = new Roo.util.MixedCollection();
32842     this.panels.getKey = this.getPanelId.createDelegate(this);
32843     this.box = null;
32844     this.activePanel = null;
32845     // ensure listeners are added...
32846     
32847     if (config.listeners || config.events) {
32848         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
32849             listeners : config.listeners || {},
32850             events : config.events || {}
32851         });
32852     }
32853     
32854     if(skipConfig !== true){
32855         this.applyConfig(config);
32856     }
32857 };
32858
32859 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
32860 {
32861     getPanelId : function(p){
32862         return p.getId();
32863     },
32864     
32865     applyConfig : function(config){
32866         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32867         this.config = config;
32868         
32869     },
32870     
32871     /**
32872      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
32873      * the width, for horizontal (north, south) the height.
32874      * @param {Number} newSize The new width or height
32875      */
32876     resizeTo : function(newSize){
32877         var el = this.el ? this.el :
32878                  (this.activePanel ? this.activePanel.getEl() : null);
32879         if(el){
32880             switch(this.position){
32881                 case "east":
32882                 case "west":
32883                     el.setWidth(newSize);
32884                     this.fireEvent("resized", this, newSize);
32885                 break;
32886                 case "north":
32887                 case "south":
32888                     el.setHeight(newSize);
32889                     this.fireEvent("resized", this, newSize);
32890                 break;                
32891             }
32892         }
32893     },
32894     
32895     getBox : function(){
32896         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
32897     },
32898     
32899     getMargins : function(){
32900         return this.margins;
32901     },
32902     
32903     updateBox : function(box){
32904         this.box = box;
32905         var el = this.activePanel.getEl();
32906         el.dom.style.left = box.x + "px";
32907         el.dom.style.top = box.y + "px";
32908         this.activePanel.setSize(box.width, box.height);
32909     },
32910     
32911     /**
32912      * Returns the container element for this region.
32913      * @return {Roo.Element}
32914      */
32915     getEl : function(){
32916         return this.activePanel;
32917     },
32918     
32919     /**
32920      * Returns true if this region is currently visible.
32921      * @return {Boolean}
32922      */
32923     isVisible : function(){
32924         return this.activePanel ? true : false;
32925     },
32926     
32927     setActivePanel : function(panel){
32928         panel = this.getPanel(panel);
32929         if(this.activePanel && this.activePanel != panel){
32930             this.activePanel.setActiveState(false);
32931             this.activePanel.getEl().setLeftTop(-10000,-10000);
32932         }
32933         this.activePanel = panel;
32934         panel.setActiveState(true);
32935         if(this.box){
32936             panel.setSize(this.box.width, this.box.height);
32937         }
32938         this.fireEvent("panelactivated", this, panel);
32939         this.fireEvent("invalidated");
32940     },
32941     
32942     /**
32943      * Show the specified panel.
32944      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
32945      * @return {Roo.ContentPanel} The shown panel or null
32946      */
32947     showPanel : function(panel){
32948         panel = this.getPanel(panel);
32949         if(panel){
32950             this.setActivePanel(panel);
32951         }
32952         return panel;
32953     },
32954     
32955     /**
32956      * Get the active panel for this region.
32957      * @return {Roo.ContentPanel} The active panel or null
32958      */
32959     getActivePanel : function(){
32960         return this.activePanel;
32961     },
32962     
32963     /**
32964      * Add the passed ContentPanel(s)
32965      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32966      * @return {Roo.ContentPanel} The panel added (if only one was added)
32967      */
32968     add : function(panel){
32969         if(arguments.length > 1){
32970             for(var i = 0, len = arguments.length; i < len; i++) {
32971                 this.add(arguments[i]);
32972             }
32973             return null;
32974         }
32975         if(this.hasPanel(panel)){
32976             this.showPanel(panel);
32977             return panel;
32978         }
32979         var el = panel.getEl();
32980         if(el.dom.parentNode != this.mgr.el.dom){
32981             this.mgr.el.dom.appendChild(el.dom);
32982         }
32983         if(panel.setRegion){
32984             panel.setRegion(this);
32985         }
32986         this.panels.add(panel);
32987         el.setStyle("position", "absolute");
32988         if(!panel.background){
32989             this.setActivePanel(panel);
32990             if(this.config.initialSize && this.panels.getCount()==1){
32991                 this.resizeTo(this.config.initialSize);
32992             }
32993         }
32994         this.fireEvent("paneladded", this, panel);
32995         return panel;
32996     },
32997     
32998     /**
32999      * Returns true if the panel is in this region.
33000      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33001      * @return {Boolean}
33002      */
33003     hasPanel : function(panel){
33004         if(typeof panel == "object"){ // must be panel obj
33005             panel = panel.getId();
33006         }
33007         return this.getPanel(panel) ? true : false;
33008     },
33009     
33010     /**
33011      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33012      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33013      * @param {Boolean} preservePanel Overrides the config preservePanel option
33014      * @return {Roo.ContentPanel} The panel that was removed
33015      */
33016     remove : function(panel, preservePanel){
33017         panel = this.getPanel(panel);
33018         if(!panel){
33019             return null;
33020         }
33021         var e = {};
33022         this.fireEvent("beforeremove", this, panel, e);
33023         if(e.cancel === true){
33024             return null;
33025         }
33026         var panelId = panel.getId();
33027         this.panels.removeKey(panelId);
33028         return panel;
33029     },
33030     
33031     /**
33032      * Returns the panel specified or null if it's not in this region.
33033      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33034      * @return {Roo.ContentPanel}
33035      */
33036     getPanel : function(id){
33037         if(typeof id == "object"){ // must be panel obj
33038             return id;
33039         }
33040         return this.panels.get(id);
33041     },
33042     
33043     /**
33044      * Returns this regions position (north/south/east/west/center).
33045      * @return {String} 
33046      */
33047     getPosition: function(){
33048         return this.position;    
33049     }
33050 });/*
33051  * Based on:
33052  * Ext JS Library 1.1.1
33053  * Copyright(c) 2006-2007, Ext JS, LLC.
33054  *
33055  * Originally Released Under LGPL - original licence link has changed is not relivant.
33056  *
33057  * Fork - LGPL
33058  * <script type="text/javascript">
33059  */
33060  
33061 /**
33062  * @class Roo.bootstrap.layout.Region
33063  * @extends Roo.bootstrap.layout.Basic
33064  * This class represents a region in a layout manager.
33065  
33066  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33067  * @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})
33068  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
33069  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
33070  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
33071  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
33072  * @cfg {String}    title           The title for the region (overrides panel titles)
33073  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
33074  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33075  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
33076  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33077  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
33078  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33079  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
33080  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
33081  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
33082  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
33083
33084  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
33085  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
33086  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
33087  * @cfg {Number}    width           For East/West panels
33088  * @cfg {Number}    height          For North/South panels
33089  * @cfg {Boolean}   split           To show the splitter
33090  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
33091  * 
33092  * @cfg {string}   cls             Extra CSS classes to add to region
33093  * 
33094  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
33095  * @cfg {string}   region  the region that it inhabits..
33096  *
33097
33098  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
33099  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
33100
33101  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
33102  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
33103  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
33104  */
33105 Roo.bootstrap.layout.Region = function(config)
33106 {
33107     this.applyConfig(config);
33108
33109     var mgr = config.mgr;
33110     var pos = config.region;
33111     config.skipConfig = true;
33112     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
33113     
33114     if (mgr.el) {
33115         this.onRender(mgr.el);   
33116     }
33117      
33118     this.visible = true;
33119     this.collapsed = false;
33120     this.unrendered_panels = [];
33121 };
33122
33123 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
33124
33125     position: '', // set by wrapper (eg. north/south etc..)
33126     unrendered_panels : null,  // unrendered panels.
33127     createBody : function(){
33128         /** This region's body element 
33129         * @type Roo.Element */
33130         this.bodyEl = this.el.createChild({
33131                 tag: "div",
33132                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
33133         });
33134     },
33135
33136     onRender: function(ctr, pos)
33137     {
33138         var dh = Roo.DomHelper;
33139         /** This region's container element 
33140         * @type Roo.Element */
33141         this.el = dh.append(ctr.dom, {
33142                 tag: "div",
33143                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
33144             }, true);
33145         /** This region's title element 
33146         * @type Roo.Element */
33147     
33148         this.titleEl = dh.append(this.el.dom,
33149             {
33150                     tag: "div",
33151                     unselectable: "on",
33152                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
33153                     children:[
33154                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
33155                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
33156                     ]}, true);
33157         
33158         this.titleEl.enableDisplayMode();
33159         /** This region's title text element 
33160         * @type HTMLElement */
33161         this.titleTextEl = this.titleEl.dom.firstChild;
33162         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33163         /*
33164         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
33165         this.closeBtn.enableDisplayMode();
33166         this.closeBtn.on("click", this.closeClicked, this);
33167         this.closeBtn.hide();
33168     */
33169         this.createBody(this.config);
33170         if(this.config.hideWhenEmpty){
33171             this.hide();
33172             this.on("paneladded", this.validateVisibility, this);
33173             this.on("panelremoved", this.validateVisibility, this);
33174         }
33175         if(this.autoScroll){
33176             this.bodyEl.setStyle("overflow", "auto");
33177         }else{
33178             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
33179         }
33180         //if(c.titlebar !== false){
33181             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
33182                 this.titleEl.hide();
33183             }else{
33184                 this.titleEl.show();
33185                 if(this.config.title){
33186                     this.titleTextEl.innerHTML = this.config.title;
33187                 }
33188             }
33189         //}
33190         if(this.config.collapsed){
33191             this.collapse(true);
33192         }
33193         if(this.config.hidden){
33194             this.hide();
33195         }
33196         
33197         if (this.unrendered_panels && this.unrendered_panels.length) {
33198             for (var i =0;i< this.unrendered_panels.length; i++) {
33199                 this.add(this.unrendered_panels[i]);
33200             }
33201             this.unrendered_panels = null;
33202             
33203         }
33204         
33205     },
33206     
33207     applyConfig : function(c)
33208     {
33209         /*
33210          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
33211             var dh = Roo.DomHelper;
33212             if(c.titlebar !== false){
33213                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
33214                 this.collapseBtn.on("click", this.collapse, this);
33215                 this.collapseBtn.enableDisplayMode();
33216                 /*
33217                 if(c.showPin === true || this.showPin){
33218                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
33219                     this.stickBtn.enableDisplayMode();
33220                     this.stickBtn.on("click", this.expand, this);
33221                     this.stickBtn.hide();
33222                 }
33223                 
33224             }
33225             */
33226             /** This region's collapsed element
33227             * @type Roo.Element */
33228             /*
33229              *
33230             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33231                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33232             ]}, true);
33233             
33234             if(c.floatable !== false){
33235                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33236                this.collapsedEl.on("click", this.collapseClick, this);
33237             }
33238
33239             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33240                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33241                    id: "message", unselectable: "on", style:{"float":"left"}});
33242                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33243              }
33244             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33245             this.expandBtn.on("click", this.expand, this);
33246             
33247         }
33248         
33249         if(this.collapseBtn){
33250             this.collapseBtn.setVisible(c.collapsible == true);
33251         }
33252         
33253         this.cmargins = c.cmargins || this.cmargins ||
33254                          (this.position == "west" || this.position == "east" ?
33255                              {top: 0, left: 2, right:2, bottom: 0} :
33256                              {top: 2, left: 0, right:0, bottom: 2});
33257         */
33258         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33259         
33260         
33261         this.bottomTabs = c.tabPosition != "top";
33262         
33263         this.autoScroll = c.autoScroll || false;
33264         
33265         
33266        
33267         
33268         this.duration = c.duration || .30;
33269         this.slideDuration = c.slideDuration || .45;
33270         this.config = c;
33271        
33272     },
33273     /**
33274      * Returns true if this region is currently visible.
33275      * @return {Boolean}
33276      */
33277     isVisible : function(){
33278         return this.visible;
33279     },
33280
33281     /**
33282      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33283      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
33284      */
33285     //setCollapsedTitle : function(title){
33286     //    title = title || "&#160;";
33287      //   if(this.collapsedTitleTextEl){
33288       //      this.collapsedTitleTextEl.innerHTML = title;
33289        // }
33290     //},
33291
33292     getBox : function(){
33293         var b;
33294       //  if(!this.collapsed){
33295             b = this.el.getBox(false, true);
33296        // }else{
33297           //  b = this.collapsedEl.getBox(false, true);
33298         //}
33299         return b;
33300     },
33301
33302     getMargins : function(){
33303         return this.margins;
33304         //return this.collapsed ? this.cmargins : this.margins;
33305     },
33306 /*
33307     highlight : function(){
33308         this.el.addClass("x-layout-panel-dragover");
33309     },
33310
33311     unhighlight : function(){
33312         this.el.removeClass("x-layout-panel-dragover");
33313     },
33314 */
33315     updateBox : function(box)
33316     {
33317         if (!this.bodyEl) {
33318             return; // not rendered yet..
33319         }
33320         
33321         this.box = box;
33322         if(!this.collapsed){
33323             this.el.dom.style.left = box.x + "px";
33324             this.el.dom.style.top = box.y + "px";
33325             this.updateBody(box.width, box.height);
33326         }else{
33327             this.collapsedEl.dom.style.left = box.x + "px";
33328             this.collapsedEl.dom.style.top = box.y + "px";
33329             this.collapsedEl.setSize(box.width, box.height);
33330         }
33331         if(this.tabs){
33332             this.tabs.autoSizeTabs();
33333         }
33334     },
33335
33336     updateBody : function(w, h)
33337     {
33338         if(w !== null){
33339             this.el.setWidth(w);
33340             w -= this.el.getBorderWidth("rl");
33341             if(this.config.adjustments){
33342                 w += this.config.adjustments[0];
33343             }
33344         }
33345         if(h !== null && h > 0){
33346             this.el.setHeight(h);
33347             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
33348             h -= this.el.getBorderWidth("tb");
33349             if(this.config.adjustments){
33350                 h += this.config.adjustments[1];
33351             }
33352             this.bodyEl.setHeight(h);
33353             if(this.tabs){
33354                 h = this.tabs.syncHeight(h);
33355             }
33356         }
33357         if(this.panelSize){
33358             w = w !== null ? w : this.panelSize.width;
33359             h = h !== null ? h : this.panelSize.height;
33360         }
33361         if(this.activePanel){
33362             var el = this.activePanel.getEl();
33363             w = w !== null ? w : el.getWidth();
33364             h = h !== null ? h : el.getHeight();
33365             this.panelSize = {width: w, height: h};
33366             this.activePanel.setSize(w, h);
33367         }
33368         if(Roo.isIE && this.tabs){
33369             this.tabs.el.repaint();
33370         }
33371     },
33372
33373     /**
33374      * Returns the container element for this region.
33375      * @return {Roo.Element}
33376      */
33377     getEl : function(){
33378         return this.el;
33379     },
33380
33381     /**
33382      * Hides this region.
33383      */
33384     hide : function(){
33385         //if(!this.collapsed){
33386             this.el.dom.style.left = "-2000px";
33387             this.el.hide();
33388         //}else{
33389          //   this.collapsedEl.dom.style.left = "-2000px";
33390          //   this.collapsedEl.hide();
33391        // }
33392         this.visible = false;
33393         this.fireEvent("visibilitychange", this, false);
33394     },
33395
33396     /**
33397      * Shows this region if it was previously hidden.
33398      */
33399     show : function(){
33400         //if(!this.collapsed){
33401             this.el.show();
33402         //}else{
33403         //    this.collapsedEl.show();
33404        // }
33405         this.visible = true;
33406         this.fireEvent("visibilitychange", this, true);
33407     },
33408 /*
33409     closeClicked : function(){
33410         if(this.activePanel){
33411             this.remove(this.activePanel);
33412         }
33413     },
33414
33415     collapseClick : function(e){
33416         if(this.isSlid){
33417            e.stopPropagation();
33418            this.slideIn();
33419         }else{
33420            e.stopPropagation();
33421            this.slideOut();
33422         }
33423     },
33424 */
33425     /**
33426      * Collapses this region.
33427      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
33428      */
33429     /*
33430     collapse : function(skipAnim, skipCheck = false){
33431         if(this.collapsed) {
33432             return;
33433         }
33434         
33435         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
33436             
33437             this.collapsed = true;
33438             if(this.split){
33439                 this.split.el.hide();
33440             }
33441             if(this.config.animate && skipAnim !== true){
33442                 this.fireEvent("invalidated", this);
33443                 this.animateCollapse();
33444             }else{
33445                 this.el.setLocation(-20000,-20000);
33446                 this.el.hide();
33447                 this.collapsedEl.show();
33448                 this.fireEvent("collapsed", this);
33449                 this.fireEvent("invalidated", this);
33450             }
33451         }
33452         
33453     },
33454 */
33455     animateCollapse : function(){
33456         // overridden
33457     },
33458
33459     /**
33460      * Expands this region if it was previously collapsed.
33461      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
33462      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
33463      */
33464     /*
33465     expand : function(e, skipAnim){
33466         if(e) {
33467             e.stopPropagation();
33468         }
33469         if(!this.collapsed || this.el.hasActiveFx()) {
33470             return;
33471         }
33472         if(this.isSlid){
33473             this.afterSlideIn();
33474             skipAnim = true;
33475         }
33476         this.collapsed = false;
33477         if(this.config.animate && skipAnim !== true){
33478             this.animateExpand();
33479         }else{
33480             this.el.show();
33481             if(this.split){
33482                 this.split.el.show();
33483             }
33484             this.collapsedEl.setLocation(-2000,-2000);
33485             this.collapsedEl.hide();
33486             this.fireEvent("invalidated", this);
33487             this.fireEvent("expanded", this);
33488         }
33489     },
33490 */
33491     animateExpand : function(){
33492         // overridden
33493     },
33494
33495     initTabs : function()
33496     {
33497         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
33498         
33499         var ts = new Roo.bootstrap.panel.Tabs({
33500                 el: this.bodyEl.dom,
33501                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
33502                 disableTooltips: this.config.disableTabTips,
33503                 toolbar : this.config.toolbar
33504             });
33505         
33506         if(this.config.hideTabs){
33507             ts.stripWrap.setDisplayed(false);
33508         }
33509         this.tabs = ts;
33510         ts.resizeTabs = this.config.resizeTabs === true;
33511         ts.minTabWidth = this.config.minTabWidth || 40;
33512         ts.maxTabWidth = this.config.maxTabWidth || 250;
33513         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
33514         ts.monitorResize = false;
33515         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
33516         ts.bodyEl.addClass('roo-layout-tabs-body');
33517         this.panels.each(this.initPanelAsTab, this);
33518     },
33519
33520     initPanelAsTab : function(panel){
33521         var ti = this.tabs.addTab(
33522             panel.getEl().id,
33523             panel.getTitle(),
33524             null,
33525             this.config.closeOnTab && panel.isClosable(),
33526             panel.tpl
33527         );
33528         if(panel.tabTip !== undefined){
33529             ti.setTooltip(panel.tabTip);
33530         }
33531         ti.on("activate", function(){
33532               this.setActivePanel(panel);
33533         }, this);
33534         
33535         if(this.config.closeOnTab){
33536             ti.on("beforeclose", function(t, e){
33537                 e.cancel = true;
33538                 this.remove(panel);
33539             }, this);
33540         }
33541         
33542         panel.tabItem = ti;
33543         
33544         return ti;
33545     },
33546
33547     updatePanelTitle : function(panel, title)
33548     {
33549         if(this.activePanel == panel){
33550             this.updateTitle(title);
33551         }
33552         if(this.tabs){
33553             var ti = this.tabs.getTab(panel.getEl().id);
33554             ti.setText(title);
33555             if(panel.tabTip !== undefined){
33556                 ti.setTooltip(panel.tabTip);
33557             }
33558         }
33559     },
33560
33561     updateTitle : function(title){
33562         if(this.titleTextEl && !this.config.title){
33563             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
33564         }
33565     },
33566
33567     setActivePanel : function(panel)
33568     {
33569         panel = this.getPanel(panel);
33570         if(this.activePanel && this.activePanel != panel){
33571             this.activePanel.setActiveState(false);
33572         }
33573         this.activePanel = panel;
33574         panel.setActiveState(true);
33575         if(this.panelSize){
33576             panel.setSize(this.panelSize.width, this.panelSize.height);
33577         }
33578         if(this.closeBtn){
33579             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
33580         }
33581         this.updateTitle(panel.getTitle());
33582         if(this.tabs){
33583             this.fireEvent("invalidated", this);
33584         }
33585         this.fireEvent("panelactivated", this, panel);
33586     },
33587
33588     /**
33589      * Shows the specified panel.
33590      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
33591      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
33592      */
33593     showPanel : function(panel)
33594     {
33595         panel = this.getPanel(panel);
33596         if(panel){
33597             if(this.tabs){
33598                 var tab = this.tabs.getTab(panel.getEl().id);
33599                 if(tab.isHidden()){
33600                     this.tabs.unhideTab(tab.id);
33601                 }
33602                 tab.activate();
33603             }else{
33604                 this.setActivePanel(panel);
33605             }
33606         }
33607         return panel;
33608     },
33609
33610     /**
33611      * Get the active panel for this region.
33612      * @return {Roo.ContentPanel} The active panel or null
33613      */
33614     getActivePanel : function(){
33615         return this.activePanel;
33616     },
33617
33618     validateVisibility : function(){
33619         if(this.panels.getCount() < 1){
33620             this.updateTitle("&#160;");
33621             this.closeBtn.hide();
33622             this.hide();
33623         }else{
33624             if(!this.isVisible()){
33625                 this.show();
33626             }
33627         }
33628     },
33629
33630     /**
33631      * Adds the passed ContentPanel(s) to this region.
33632      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33633      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
33634      */
33635     add : function(panel)
33636     {
33637         if(arguments.length > 1){
33638             for(var i = 0, len = arguments.length; i < len; i++) {
33639                 this.add(arguments[i]);
33640             }
33641             return null;
33642         }
33643         
33644         // if we have not been rendered yet, then we can not really do much of this..
33645         if (!this.bodyEl) {
33646             this.unrendered_panels.push(panel);
33647             return panel;
33648         }
33649         
33650         
33651         
33652         
33653         if(this.hasPanel(panel)){
33654             this.showPanel(panel);
33655             return panel;
33656         }
33657         panel.setRegion(this);
33658         this.panels.add(panel);
33659        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
33660             // sinle panel - no tab...?? would it not be better to render it with the tabs,
33661             // and hide them... ???
33662             this.bodyEl.dom.appendChild(panel.getEl().dom);
33663             if(panel.background !== true){
33664                 this.setActivePanel(panel);
33665             }
33666             this.fireEvent("paneladded", this, panel);
33667             return panel;
33668         }
33669         */
33670         if(!this.tabs){
33671             this.initTabs();
33672         }else{
33673             this.initPanelAsTab(panel);
33674         }
33675         
33676         
33677         if(panel.background !== true){
33678             this.tabs.activate(panel.getEl().id);
33679         }
33680         this.fireEvent("paneladded", this, panel);
33681         return panel;
33682     },
33683
33684     /**
33685      * Hides the tab for the specified panel.
33686      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33687      */
33688     hidePanel : function(panel){
33689         if(this.tabs && (panel = this.getPanel(panel))){
33690             this.tabs.hideTab(panel.getEl().id);
33691         }
33692     },
33693
33694     /**
33695      * Unhides the tab for a previously hidden panel.
33696      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33697      */
33698     unhidePanel : function(panel){
33699         if(this.tabs && (panel = this.getPanel(panel))){
33700             this.tabs.unhideTab(panel.getEl().id);
33701         }
33702     },
33703
33704     clearPanels : function(){
33705         while(this.panels.getCount() > 0){
33706              this.remove(this.panels.first());
33707         }
33708     },
33709
33710     /**
33711      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33712      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33713      * @param {Boolean} preservePanel Overrides the config preservePanel option
33714      * @return {Roo.ContentPanel} The panel that was removed
33715      */
33716     remove : function(panel, preservePanel)
33717     {
33718         panel = this.getPanel(panel);
33719         if(!panel){
33720             return null;
33721         }
33722         var e = {};
33723         this.fireEvent("beforeremove", this, panel, e);
33724         if(e.cancel === true){
33725             return null;
33726         }
33727         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
33728         var panelId = panel.getId();
33729         this.panels.removeKey(panelId);
33730         if(preservePanel){
33731             document.body.appendChild(panel.getEl().dom);
33732         }
33733         if(this.tabs){
33734             this.tabs.removeTab(panel.getEl().id);
33735         }else if (!preservePanel){
33736             this.bodyEl.dom.removeChild(panel.getEl().dom);
33737         }
33738         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
33739             var p = this.panels.first();
33740             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
33741             tempEl.appendChild(p.getEl().dom);
33742             this.bodyEl.update("");
33743             this.bodyEl.dom.appendChild(p.getEl().dom);
33744             tempEl = null;
33745             this.updateTitle(p.getTitle());
33746             this.tabs = null;
33747             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33748             this.setActivePanel(p);
33749         }
33750         panel.setRegion(null);
33751         if(this.activePanel == panel){
33752             this.activePanel = null;
33753         }
33754         if(this.config.autoDestroy !== false && preservePanel !== true){
33755             try{panel.destroy();}catch(e){}
33756         }
33757         this.fireEvent("panelremoved", this, panel);
33758         return panel;
33759     },
33760
33761     /**
33762      * Returns the TabPanel component used by this region
33763      * @return {Roo.TabPanel}
33764      */
33765     getTabs : function(){
33766         return this.tabs;
33767     },
33768
33769     createTool : function(parentEl, className){
33770         var btn = Roo.DomHelper.append(parentEl, {
33771             tag: "div",
33772             cls: "x-layout-tools-button",
33773             children: [ {
33774                 tag: "div",
33775                 cls: "roo-layout-tools-button-inner " + className,
33776                 html: "&#160;"
33777             }]
33778         }, true);
33779         btn.addClassOnOver("roo-layout-tools-button-over");
33780         return btn;
33781     }
33782 });/*
33783  * Based on:
33784  * Ext JS Library 1.1.1
33785  * Copyright(c) 2006-2007, Ext JS, LLC.
33786  *
33787  * Originally Released Under LGPL - original licence link has changed is not relivant.
33788  *
33789  * Fork - LGPL
33790  * <script type="text/javascript">
33791  */
33792  
33793
33794
33795 /**
33796  * @class Roo.SplitLayoutRegion
33797  * @extends Roo.LayoutRegion
33798  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
33799  */
33800 Roo.bootstrap.layout.Split = function(config){
33801     this.cursor = config.cursor;
33802     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
33803 };
33804
33805 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
33806 {
33807     splitTip : "Drag to resize.",
33808     collapsibleSplitTip : "Drag to resize. Double click to hide.",
33809     useSplitTips : false,
33810
33811     applyConfig : function(config){
33812         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
33813     },
33814     
33815     onRender : function(ctr,pos) {
33816         
33817         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
33818         if(!this.config.split){
33819             return;
33820         }
33821         if(!this.split){
33822             
33823             var splitEl = Roo.DomHelper.append(ctr.dom,  {
33824                             tag: "div",
33825                             id: this.el.id + "-split",
33826                             cls: "roo-layout-split roo-layout-split-"+this.position,
33827                             html: "&#160;"
33828             });
33829             /** The SplitBar for this region 
33830             * @type Roo.SplitBar */
33831             // does not exist yet...
33832             Roo.log([this.position, this.orientation]);
33833             
33834             this.split = new Roo.bootstrap.SplitBar({
33835                 dragElement : splitEl,
33836                 resizingElement: this.el,
33837                 orientation : this.orientation
33838             });
33839             
33840             this.split.on("moved", this.onSplitMove, this);
33841             this.split.useShim = this.config.useShim === true;
33842             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
33843             if(this.useSplitTips){
33844                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
33845             }
33846             //if(config.collapsible){
33847             //    this.split.el.on("dblclick", this.collapse,  this);
33848             //}
33849         }
33850         if(typeof this.config.minSize != "undefined"){
33851             this.split.minSize = this.config.minSize;
33852         }
33853         if(typeof this.config.maxSize != "undefined"){
33854             this.split.maxSize = this.config.maxSize;
33855         }
33856         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
33857             this.hideSplitter();
33858         }
33859         
33860     },
33861
33862     getHMaxSize : function(){
33863          var cmax = this.config.maxSize || 10000;
33864          var center = this.mgr.getRegion("center");
33865          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
33866     },
33867
33868     getVMaxSize : function(){
33869          var cmax = this.config.maxSize || 10000;
33870          var center = this.mgr.getRegion("center");
33871          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
33872     },
33873
33874     onSplitMove : function(split, newSize){
33875         this.fireEvent("resized", this, newSize);
33876     },
33877     
33878     /** 
33879      * Returns the {@link Roo.SplitBar} for this region.
33880      * @return {Roo.SplitBar}
33881      */
33882     getSplitBar : function(){
33883         return this.split;
33884     },
33885     
33886     hide : function(){
33887         this.hideSplitter();
33888         Roo.bootstrap.layout.Split.superclass.hide.call(this);
33889     },
33890
33891     hideSplitter : function(){
33892         if(this.split){
33893             this.split.el.setLocation(-2000,-2000);
33894             this.split.el.hide();
33895         }
33896     },
33897
33898     show : function(){
33899         if(this.split){
33900             this.split.el.show();
33901         }
33902         Roo.bootstrap.layout.Split.superclass.show.call(this);
33903     },
33904     
33905     beforeSlide: function(){
33906         if(Roo.isGecko){// firefox overflow auto bug workaround
33907             this.bodyEl.clip();
33908             if(this.tabs) {
33909                 this.tabs.bodyEl.clip();
33910             }
33911             if(this.activePanel){
33912                 this.activePanel.getEl().clip();
33913                 
33914                 if(this.activePanel.beforeSlide){
33915                     this.activePanel.beforeSlide();
33916                 }
33917             }
33918         }
33919     },
33920     
33921     afterSlide : function(){
33922         if(Roo.isGecko){// firefox overflow auto bug workaround
33923             this.bodyEl.unclip();
33924             if(this.tabs) {
33925                 this.tabs.bodyEl.unclip();
33926             }
33927             if(this.activePanel){
33928                 this.activePanel.getEl().unclip();
33929                 if(this.activePanel.afterSlide){
33930                     this.activePanel.afterSlide();
33931                 }
33932             }
33933         }
33934     },
33935
33936     initAutoHide : function(){
33937         if(this.autoHide !== false){
33938             if(!this.autoHideHd){
33939                 var st = new Roo.util.DelayedTask(this.slideIn, this);
33940                 this.autoHideHd = {
33941                     "mouseout": function(e){
33942                         if(!e.within(this.el, true)){
33943                             st.delay(500);
33944                         }
33945                     },
33946                     "mouseover" : function(e){
33947                         st.cancel();
33948                     },
33949                     scope : this
33950                 };
33951             }
33952             this.el.on(this.autoHideHd);
33953         }
33954     },
33955
33956     clearAutoHide : function(){
33957         if(this.autoHide !== false){
33958             this.el.un("mouseout", this.autoHideHd.mouseout);
33959             this.el.un("mouseover", this.autoHideHd.mouseover);
33960         }
33961     },
33962
33963     clearMonitor : function(){
33964         Roo.get(document).un("click", this.slideInIf, this);
33965     },
33966
33967     // these names are backwards but not changed for compat
33968     slideOut : function(){
33969         if(this.isSlid || this.el.hasActiveFx()){
33970             return;
33971         }
33972         this.isSlid = true;
33973         if(this.collapseBtn){
33974             this.collapseBtn.hide();
33975         }
33976         this.closeBtnState = this.closeBtn.getStyle('display');
33977         this.closeBtn.hide();
33978         if(this.stickBtn){
33979             this.stickBtn.show();
33980         }
33981         this.el.show();
33982         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
33983         this.beforeSlide();
33984         this.el.setStyle("z-index", 10001);
33985         this.el.slideIn(this.getSlideAnchor(), {
33986             callback: function(){
33987                 this.afterSlide();
33988                 this.initAutoHide();
33989                 Roo.get(document).on("click", this.slideInIf, this);
33990                 this.fireEvent("slideshow", this);
33991             },
33992             scope: this,
33993             block: true
33994         });
33995     },
33996
33997     afterSlideIn : function(){
33998         this.clearAutoHide();
33999         this.isSlid = false;
34000         this.clearMonitor();
34001         this.el.setStyle("z-index", "");
34002         if(this.collapseBtn){
34003             this.collapseBtn.show();
34004         }
34005         this.closeBtn.setStyle('display', this.closeBtnState);
34006         if(this.stickBtn){
34007             this.stickBtn.hide();
34008         }
34009         this.fireEvent("slidehide", this);
34010     },
34011
34012     slideIn : function(cb){
34013         if(!this.isSlid || this.el.hasActiveFx()){
34014             Roo.callback(cb);
34015             return;
34016         }
34017         this.isSlid = false;
34018         this.beforeSlide();
34019         this.el.slideOut(this.getSlideAnchor(), {
34020             callback: function(){
34021                 this.el.setLeftTop(-10000, -10000);
34022                 this.afterSlide();
34023                 this.afterSlideIn();
34024                 Roo.callback(cb);
34025             },
34026             scope: this,
34027             block: true
34028         });
34029     },
34030     
34031     slideInIf : function(e){
34032         if(!e.within(this.el)){
34033             this.slideIn();
34034         }
34035     },
34036
34037     animateCollapse : function(){
34038         this.beforeSlide();
34039         this.el.setStyle("z-index", 20000);
34040         var anchor = this.getSlideAnchor();
34041         this.el.slideOut(anchor, {
34042             callback : function(){
34043                 this.el.setStyle("z-index", "");
34044                 this.collapsedEl.slideIn(anchor, {duration:.3});
34045                 this.afterSlide();
34046                 this.el.setLocation(-10000,-10000);
34047                 this.el.hide();
34048                 this.fireEvent("collapsed", this);
34049             },
34050             scope: this,
34051             block: true
34052         });
34053     },
34054
34055     animateExpand : function(){
34056         this.beforeSlide();
34057         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34058         this.el.setStyle("z-index", 20000);
34059         this.collapsedEl.hide({
34060             duration:.1
34061         });
34062         this.el.slideIn(this.getSlideAnchor(), {
34063             callback : function(){
34064                 this.el.setStyle("z-index", "");
34065                 this.afterSlide();
34066                 if(this.split){
34067                     this.split.el.show();
34068                 }
34069                 this.fireEvent("invalidated", this);
34070                 this.fireEvent("expanded", this);
34071             },
34072             scope: this,
34073             block: true
34074         });
34075     },
34076
34077     anchors : {
34078         "west" : "left",
34079         "east" : "right",
34080         "north" : "top",
34081         "south" : "bottom"
34082     },
34083
34084     sanchors : {
34085         "west" : "l",
34086         "east" : "r",
34087         "north" : "t",
34088         "south" : "b"
34089     },
34090
34091     canchors : {
34092         "west" : "tl-tr",
34093         "east" : "tr-tl",
34094         "north" : "tl-bl",
34095         "south" : "bl-tl"
34096     },
34097
34098     getAnchor : function(){
34099         return this.anchors[this.position];
34100     },
34101
34102     getCollapseAnchor : function(){
34103         return this.canchors[this.position];
34104     },
34105
34106     getSlideAnchor : function(){
34107         return this.sanchors[this.position];
34108     },
34109
34110     getAlignAdj : function(){
34111         var cm = this.cmargins;
34112         switch(this.position){
34113             case "west":
34114                 return [0, 0];
34115             break;
34116             case "east":
34117                 return [0, 0];
34118             break;
34119             case "north":
34120                 return [0, 0];
34121             break;
34122             case "south":
34123                 return [0, 0];
34124             break;
34125         }
34126     },
34127
34128     getExpandAdj : function(){
34129         var c = this.collapsedEl, cm = this.cmargins;
34130         switch(this.position){
34131             case "west":
34132                 return [-(cm.right+c.getWidth()+cm.left), 0];
34133             break;
34134             case "east":
34135                 return [cm.right+c.getWidth()+cm.left, 0];
34136             break;
34137             case "north":
34138                 return [0, -(cm.top+cm.bottom+c.getHeight())];
34139             break;
34140             case "south":
34141                 return [0, cm.top+cm.bottom+c.getHeight()];
34142             break;
34143         }
34144     }
34145 });/*
34146  * Based on:
34147  * Ext JS Library 1.1.1
34148  * Copyright(c) 2006-2007, Ext JS, LLC.
34149  *
34150  * Originally Released Under LGPL - original licence link has changed is not relivant.
34151  *
34152  * Fork - LGPL
34153  * <script type="text/javascript">
34154  */
34155 /*
34156  * These classes are private internal classes
34157  */
34158 Roo.bootstrap.layout.Center = function(config){
34159     config.region = "center";
34160     Roo.bootstrap.layout.Region.call(this, config);
34161     this.visible = true;
34162     this.minWidth = config.minWidth || 20;
34163     this.minHeight = config.minHeight || 20;
34164 };
34165
34166 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
34167     hide : function(){
34168         // center panel can't be hidden
34169     },
34170     
34171     show : function(){
34172         // center panel can't be hidden
34173     },
34174     
34175     getMinWidth: function(){
34176         return this.minWidth;
34177     },
34178     
34179     getMinHeight: function(){
34180         return this.minHeight;
34181     }
34182 });
34183
34184
34185
34186
34187  
34188
34189
34190
34191
34192
34193 Roo.bootstrap.layout.North = function(config)
34194 {
34195     config.region = 'north';
34196     config.cursor = 'n-resize';
34197     
34198     Roo.bootstrap.layout.Split.call(this, config);
34199     
34200     
34201     if(this.split){
34202         this.split.placement = Roo.bootstrap.SplitBar.TOP;
34203         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
34204         this.split.el.addClass("roo-layout-split-v");
34205     }
34206     var size = config.initialSize || config.height;
34207     if(typeof size != "undefined"){
34208         this.el.setHeight(size);
34209     }
34210 };
34211 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
34212 {
34213     orientation: Roo.bootstrap.SplitBar.VERTICAL,
34214     
34215     
34216     
34217     getBox : function(){
34218         if(this.collapsed){
34219             return this.collapsedEl.getBox();
34220         }
34221         var box = this.el.getBox();
34222         if(this.split){
34223             box.height += this.split.el.getHeight();
34224         }
34225         return box;
34226     },
34227     
34228     updateBox : function(box){
34229         if(this.split && !this.collapsed){
34230             box.height -= this.split.el.getHeight();
34231             this.split.el.setLeft(box.x);
34232             this.split.el.setTop(box.y+box.height);
34233             this.split.el.setWidth(box.width);
34234         }
34235         if(this.collapsed){
34236             this.updateBody(box.width, null);
34237         }
34238         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34239     }
34240 });
34241
34242
34243
34244
34245
34246 Roo.bootstrap.layout.South = function(config){
34247     config.region = 'south';
34248     config.cursor = 's-resize';
34249     Roo.bootstrap.layout.Split.call(this, config);
34250     if(this.split){
34251         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
34252         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
34253         this.split.el.addClass("roo-layout-split-v");
34254     }
34255     var size = config.initialSize || config.height;
34256     if(typeof size != "undefined"){
34257         this.el.setHeight(size);
34258     }
34259 };
34260
34261 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
34262     orientation: Roo.bootstrap.SplitBar.VERTICAL,
34263     getBox : function(){
34264         if(this.collapsed){
34265             return this.collapsedEl.getBox();
34266         }
34267         var box = this.el.getBox();
34268         if(this.split){
34269             var sh = this.split.el.getHeight();
34270             box.height += sh;
34271             box.y -= sh;
34272         }
34273         return box;
34274     },
34275     
34276     updateBox : function(box){
34277         if(this.split && !this.collapsed){
34278             var sh = this.split.el.getHeight();
34279             box.height -= sh;
34280             box.y += sh;
34281             this.split.el.setLeft(box.x);
34282             this.split.el.setTop(box.y-sh);
34283             this.split.el.setWidth(box.width);
34284         }
34285         if(this.collapsed){
34286             this.updateBody(box.width, null);
34287         }
34288         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34289     }
34290 });
34291
34292 Roo.bootstrap.layout.East = function(config){
34293     config.region = "east";
34294     config.cursor = "e-resize";
34295     Roo.bootstrap.layout.Split.call(this, config);
34296     if(this.split){
34297         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
34298         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
34299         this.split.el.addClass("roo-layout-split-h");
34300     }
34301     var size = config.initialSize || config.width;
34302     if(typeof size != "undefined"){
34303         this.el.setWidth(size);
34304     }
34305 };
34306 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
34307     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
34308     getBox : function(){
34309         if(this.collapsed){
34310             return this.collapsedEl.getBox();
34311         }
34312         var box = this.el.getBox();
34313         if(this.split){
34314             var sw = this.split.el.getWidth();
34315             box.width += sw;
34316             box.x -= sw;
34317         }
34318         return box;
34319     },
34320
34321     updateBox : function(box){
34322         if(this.split && !this.collapsed){
34323             var sw = this.split.el.getWidth();
34324             box.width -= sw;
34325             this.split.el.setLeft(box.x);
34326             this.split.el.setTop(box.y);
34327             this.split.el.setHeight(box.height);
34328             box.x += sw;
34329         }
34330         if(this.collapsed){
34331             this.updateBody(null, box.height);
34332         }
34333         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34334     }
34335 });
34336
34337 Roo.bootstrap.layout.West = function(config){
34338     config.region = "west";
34339     config.cursor = "w-resize";
34340     
34341     Roo.bootstrap.layout.Split.call(this, config);
34342     if(this.split){
34343         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
34344         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
34345         this.split.el.addClass("roo-layout-split-h");
34346     }
34347     
34348 };
34349 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
34350     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
34351     
34352     onRender: function(ctr, pos)
34353     {
34354         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
34355         var size = this.config.initialSize || this.config.width;
34356         if(typeof size != "undefined"){
34357             this.el.setWidth(size);
34358         }
34359     },
34360     
34361     getBox : function(){
34362         if(this.collapsed){
34363             return this.collapsedEl.getBox();
34364         }
34365         var box = this.el.getBox();
34366         if(this.split){
34367             box.width += this.split.el.getWidth();
34368         }
34369         return box;
34370     },
34371     
34372     updateBox : function(box){
34373         if(this.split && !this.collapsed){
34374             var sw = this.split.el.getWidth();
34375             box.width -= sw;
34376             this.split.el.setLeft(box.x+box.width);
34377             this.split.el.setTop(box.y);
34378             this.split.el.setHeight(box.height);
34379         }
34380         if(this.collapsed){
34381             this.updateBody(null, box.height);
34382         }
34383         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34384     }
34385 });
34386 Roo.namespace("Roo.bootstrap.panel");/*
34387  * Based on:
34388  * Ext JS Library 1.1.1
34389  * Copyright(c) 2006-2007, Ext JS, LLC.
34390  *
34391  * Originally Released Under LGPL - original licence link has changed is not relivant.
34392  *
34393  * Fork - LGPL
34394  * <script type="text/javascript">
34395  */
34396 /**
34397  * @class Roo.ContentPanel
34398  * @extends Roo.util.Observable
34399  * A basic ContentPanel element.
34400  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
34401  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
34402  * @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
34403  * @cfg {Boolean}   closable      True if the panel can be closed/removed
34404  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
34405  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
34406  * @cfg {Toolbar}   toolbar       A toolbar for this panel
34407  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
34408  * @cfg {String} title          The title for this panel
34409  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
34410  * @cfg {String} url            Calls {@link #setUrl} with this value
34411  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
34412  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
34413  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
34414  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
34415  * @cfg {Boolean} badges render the badges
34416
34417  * @constructor
34418  * Create a new ContentPanel.
34419  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
34420  * @param {String/Object} config A string to set only the title or a config object
34421  * @param {String} content (optional) Set the HTML content for this panel
34422  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
34423  */
34424 Roo.bootstrap.panel.Content = function( config){
34425     
34426     this.tpl = config.tpl || false;
34427     
34428     var el = config.el;
34429     var content = config.content;
34430
34431     if(config.autoCreate){ // xtype is available if this is called from factory
34432         el = Roo.id();
34433     }
34434     this.el = Roo.get(el);
34435     if(!this.el && config && config.autoCreate){
34436         if(typeof config.autoCreate == "object"){
34437             if(!config.autoCreate.id){
34438                 config.autoCreate.id = config.id||el;
34439             }
34440             this.el = Roo.DomHelper.append(document.body,
34441                         config.autoCreate, true);
34442         }else{
34443             var elcfg =  {   tag: "div",
34444                             cls: "roo-layout-inactive-content",
34445                             id: config.id||el
34446                             };
34447             if (config.html) {
34448                 elcfg.html = config.html;
34449                 
34450             }
34451                         
34452             this.el = Roo.DomHelper.append(document.body, elcfg , true);
34453         }
34454     } 
34455     this.closable = false;
34456     this.loaded = false;
34457     this.active = false;
34458    
34459       
34460     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
34461         
34462         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
34463         
34464         this.wrapEl = this.el; //this.el.wrap();
34465         var ti = [];
34466         if (config.toolbar.items) {
34467             ti = config.toolbar.items ;
34468             delete config.toolbar.items ;
34469         }
34470         
34471         var nitems = [];
34472         this.toolbar.render(this.wrapEl, 'before');
34473         for(var i =0;i < ti.length;i++) {
34474           //  Roo.log(['add child', items[i]]);
34475             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34476         }
34477         this.toolbar.items = nitems;
34478         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
34479         delete config.toolbar;
34480         
34481     }
34482     /*
34483     // xtype created footer. - not sure if will work as we normally have to render first..
34484     if (this.footer && !this.footer.el && this.footer.xtype) {
34485         if (!this.wrapEl) {
34486             this.wrapEl = this.el.wrap();
34487         }
34488     
34489         this.footer.container = this.wrapEl.createChild();
34490          
34491         this.footer = Roo.factory(this.footer, Roo);
34492         
34493     }
34494     */
34495     
34496      if(typeof config == "string"){
34497         this.title = config;
34498     }else{
34499         Roo.apply(this, config);
34500     }
34501     
34502     if(this.resizeEl){
34503         this.resizeEl = Roo.get(this.resizeEl, true);
34504     }else{
34505         this.resizeEl = this.el;
34506     }
34507     // handle view.xtype
34508     
34509  
34510     
34511     
34512     this.addEvents({
34513         /**
34514          * @event activate
34515          * Fires when this panel is activated. 
34516          * @param {Roo.ContentPanel} this
34517          */
34518         "activate" : true,
34519         /**
34520          * @event deactivate
34521          * Fires when this panel is activated. 
34522          * @param {Roo.ContentPanel} this
34523          */
34524         "deactivate" : true,
34525
34526         /**
34527          * @event resize
34528          * Fires when this panel is resized if fitToFrame is true.
34529          * @param {Roo.ContentPanel} this
34530          * @param {Number} width The width after any component adjustments
34531          * @param {Number} height The height after any component adjustments
34532          */
34533         "resize" : true,
34534         
34535          /**
34536          * @event render
34537          * Fires when this tab is created
34538          * @param {Roo.ContentPanel} this
34539          */
34540         "render" : true
34541         
34542         
34543         
34544     });
34545     
34546
34547     
34548     
34549     if(this.autoScroll){
34550         this.resizeEl.setStyle("overflow", "auto");
34551     } else {
34552         // fix randome scrolling
34553         //this.el.on('scroll', function() {
34554         //    Roo.log('fix random scolling');
34555         //    this.scrollTo('top',0); 
34556         //});
34557     }
34558     content = content || this.content;
34559     if(content){
34560         this.setContent(content);
34561     }
34562     if(config && config.url){
34563         this.setUrl(this.url, this.params, this.loadOnce);
34564     }
34565     
34566     
34567     
34568     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
34569     
34570     if (this.view && typeof(this.view.xtype) != 'undefined') {
34571         this.view.el = this.el.appendChild(document.createElement("div"));
34572         this.view = Roo.factory(this.view); 
34573         this.view.render  &&  this.view.render(false, '');  
34574     }
34575     
34576     
34577     this.fireEvent('render', this);
34578 };
34579
34580 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
34581     
34582     tabTip : '',
34583     
34584     setRegion : function(region){
34585         this.region = region;
34586         this.setActiveClass(region && !this.background);
34587     },
34588     
34589     
34590     setActiveClass: function(state)
34591     {
34592         if(state){
34593            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
34594            this.el.setStyle('position','relative');
34595         }else{
34596            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
34597            this.el.setStyle('position', 'absolute');
34598         } 
34599     },
34600     
34601     /**
34602      * Returns the toolbar for this Panel if one was configured. 
34603      * @return {Roo.Toolbar} 
34604      */
34605     getToolbar : function(){
34606         return this.toolbar;
34607     },
34608     
34609     setActiveState : function(active)
34610     {
34611         this.active = active;
34612         this.setActiveClass(active);
34613         if(!active){
34614             this.fireEvent("deactivate", this);
34615         }else{
34616             this.fireEvent("activate", this);
34617         }
34618     },
34619     /**
34620      * Updates this panel's element
34621      * @param {String} content The new content
34622      * @param {Boolean} loadScripts (optional) true to look for and process scripts
34623     */
34624     setContent : function(content, loadScripts){
34625         this.el.update(content, loadScripts);
34626     },
34627
34628     ignoreResize : function(w, h){
34629         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
34630             return true;
34631         }else{
34632             this.lastSize = {width: w, height: h};
34633             return false;
34634         }
34635     },
34636     /**
34637      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
34638      * @return {Roo.UpdateManager} The UpdateManager
34639      */
34640     getUpdateManager : function(){
34641         return this.el.getUpdateManager();
34642     },
34643      /**
34644      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
34645      * @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:
34646 <pre><code>
34647 panel.load({
34648     url: "your-url.php",
34649     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
34650     callback: yourFunction,
34651     scope: yourObject, //(optional scope)
34652     discardUrl: false,
34653     nocache: false,
34654     text: "Loading...",
34655     timeout: 30,
34656     scripts: false
34657 });
34658 </code></pre>
34659      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
34660      * 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.
34661      * @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}
34662      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
34663      * @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.
34664      * @return {Roo.ContentPanel} this
34665      */
34666     load : function(){
34667         var um = this.el.getUpdateManager();
34668         um.update.apply(um, arguments);
34669         return this;
34670     },
34671
34672
34673     /**
34674      * 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.
34675      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
34676      * @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)
34677      * @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)
34678      * @return {Roo.UpdateManager} The UpdateManager
34679      */
34680     setUrl : function(url, params, loadOnce){
34681         if(this.refreshDelegate){
34682             this.removeListener("activate", this.refreshDelegate);
34683         }
34684         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34685         this.on("activate", this.refreshDelegate);
34686         return this.el.getUpdateManager();
34687     },
34688     
34689     _handleRefresh : function(url, params, loadOnce){
34690         if(!loadOnce || !this.loaded){
34691             var updater = this.el.getUpdateManager();
34692             updater.update(url, params, this._setLoaded.createDelegate(this));
34693         }
34694     },
34695     
34696     _setLoaded : function(){
34697         this.loaded = true;
34698     }, 
34699     
34700     /**
34701      * Returns this panel's id
34702      * @return {String} 
34703      */
34704     getId : function(){
34705         return this.el.id;
34706     },
34707     
34708     /** 
34709      * Returns this panel's element - used by regiosn to add.
34710      * @return {Roo.Element} 
34711      */
34712     getEl : function(){
34713         return this.wrapEl || this.el;
34714     },
34715     
34716    
34717     
34718     adjustForComponents : function(width, height)
34719     {
34720         //Roo.log('adjustForComponents ');
34721         if(this.resizeEl != this.el){
34722             width -= this.el.getFrameWidth('lr');
34723             height -= this.el.getFrameWidth('tb');
34724         }
34725         if(this.toolbar){
34726             var te = this.toolbar.getEl();
34727             height -= te.getHeight();
34728             te.setWidth(width);
34729         }
34730         if(this.footer){
34731             var te = this.footer.getEl();
34732             Roo.log("footer:" + te.getHeight());
34733             
34734             height -= te.getHeight();
34735             te.setWidth(width);
34736         }
34737         
34738         
34739         if(this.adjustments){
34740             width += this.adjustments[0];
34741             height += this.adjustments[1];
34742         }
34743         return {"width": width, "height": height};
34744     },
34745     
34746     setSize : function(width, height){
34747         if(this.fitToFrame && !this.ignoreResize(width, height)){
34748             if(this.fitContainer && this.resizeEl != this.el){
34749                 this.el.setSize(width, height);
34750             }
34751             var size = this.adjustForComponents(width, height);
34752             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
34753             this.fireEvent('resize', this, size.width, size.height);
34754         }
34755     },
34756     
34757     /**
34758      * Returns this panel's title
34759      * @return {String} 
34760      */
34761     getTitle : function(){
34762         return this.title;
34763     },
34764     
34765     /**
34766      * Set this panel's title
34767      * @param {String} title
34768      */
34769     setTitle : function(title){
34770         this.title = title;
34771         if(this.region){
34772             this.region.updatePanelTitle(this, title);
34773         }
34774     },
34775     
34776     /**
34777      * Returns true is this panel was configured to be closable
34778      * @return {Boolean} 
34779      */
34780     isClosable : function(){
34781         return this.closable;
34782     },
34783     
34784     beforeSlide : function(){
34785         this.el.clip();
34786         this.resizeEl.clip();
34787     },
34788     
34789     afterSlide : function(){
34790         this.el.unclip();
34791         this.resizeEl.unclip();
34792     },
34793     
34794     /**
34795      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
34796      *   Will fail silently if the {@link #setUrl} method has not been called.
34797      *   This does not activate the panel, just updates its content.
34798      */
34799     refresh : function(){
34800         if(this.refreshDelegate){
34801            this.loaded = false;
34802            this.refreshDelegate();
34803         }
34804     },
34805     
34806     /**
34807      * Destroys this panel
34808      */
34809     destroy : function(){
34810         this.el.removeAllListeners();
34811         var tempEl = document.createElement("span");
34812         tempEl.appendChild(this.el.dom);
34813         tempEl.innerHTML = "";
34814         this.el.remove();
34815         this.el = null;
34816     },
34817     
34818     /**
34819      * form - if the content panel contains a form - this is a reference to it.
34820      * @type {Roo.form.Form}
34821      */
34822     form : false,
34823     /**
34824      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
34825      *    This contains a reference to it.
34826      * @type {Roo.View}
34827      */
34828     view : false,
34829     
34830       /**
34831      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
34832      * <pre><code>
34833
34834 layout.addxtype({
34835        xtype : 'Form',
34836        items: [ .... ]
34837    }
34838 );
34839
34840 </code></pre>
34841      * @param {Object} cfg Xtype definition of item to add.
34842      */
34843     
34844     
34845     getChildContainer: function () {
34846         return this.getEl();
34847     }
34848     
34849     
34850     /*
34851         var  ret = new Roo.factory(cfg);
34852         return ret;
34853         
34854         
34855         // add form..
34856         if (cfg.xtype.match(/^Form$/)) {
34857             
34858             var el;
34859             //if (this.footer) {
34860             //    el = this.footer.container.insertSibling(false, 'before');
34861             //} else {
34862                 el = this.el.createChild();
34863             //}
34864
34865             this.form = new  Roo.form.Form(cfg);
34866             
34867             
34868             if ( this.form.allItems.length) {
34869                 this.form.render(el.dom);
34870             }
34871             return this.form;
34872         }
34873         // should only have one of theses..
34874         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
34875             // views.. should not be just added - used named prop 'view''
34876             
34877             cfg.el = this.el.appendChild(document.createElement("div"));
34878             // factory?
34879             
34880             var ret = new Roo.factory(cfg);
34881              
34882              ret.render && ret.render(false, ''); // render blank..
34883             this.view = ret;
34884             return ret;
34885         }
34886         return false;
34887     }
34888     \*/
34889 });
34890  
34891 /**
34892  * @class Roo.bootstrap.panel.Grid
34893  * @extends Roo.bootstrap.panel.Content
34894  * @constructor
34895  * Create a new GridPanel.
34896  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
34897  * @param {Object} config A the config object
34898   
34899  */
34900
34901
34902
34903 Roo.bootstrap.panel.Grid = function(config)
34904 {
34905     
34906       
34907     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
34908         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
34909
34910     config.el = this.wrapper;
34911     //this.el = this.wrapper;
34912     
34913       if (config.container) {
34914         // ctor'ed from a Border/panel.grid
34915         
34916         
34917         this.wrapper.setStyle("overflow", "hidden");
34918         this.wrapper.addClass('roo-grid-container');
34919
34920     }
34921     
34922     
34923     if(config.toolbar){
34924         var tool_el = this.wrapper.createChild();    
34925         this.toolbar = Roo.factory(config.toolbar);
34926         var ti = [];
34927         if (config.toolbar.items) {
34928             ti = config.toolbar.items ;
34929             delete config.toolbar.items ;
34930         }
34931         
34932         var nitems = [];
34933         this.toolbar.render(tool_el);
34934         for(var i =0;i < ti.length;i++) {
34935           //  Roo.log(['add child', items[i]]);
34936             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34937         }
34938         this.toolbar.items = nitems;
34939         
34940         delete config.toolbar;
34941     }
34942     
34943     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
34944     config.grid.scrollBody = true;;
34945     config.grid.monitorWindowResize = false; // turn off autosizing
34946     config.grid.autoHeight = false;
34947     config.grid.autoWidth = false;
34948     
34949     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
34950     
34951     if (config.background) {
34952         // render grid on panel activation (if panel background)
34953         this.on('activate', function(gp) {
34954             if (!gp.grid.rendered) {
34955                 gp.grid.render(this.wrapper);
34956                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
34957             }
34958         });
34959             
34960     } else {
34961         this.grid.render(this.wrapper);
34962         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
34963
34964     }
34965     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
34966     // ??? needed ??? config.el = this.wrapper;
34967     
34968     
34969     
34970   
34971     // xtype created footer. - not sure if will work as we normally have to render first..
34972     if (this.footer && !this.footer.el && this.footer.xtype) {
34973         
34974         var ctr = this.grid.getView().getFooterPanel(true);
34975         this.footer.dataSource = this.grid.dataSource;
34976         this.footer = Roo.factory(this.footer, Roo);
34977         this.footer.render(ctr);
34978         
34979     }
34980     
34981     
34982     
34983     
34984      
34985 };
34986
34987 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
34988     getId : function(){
34989         return this.grid.id;
34990     },
34991     
34992     /**
34993      * Returns the grid for this panel
34994      * @return {Roo.bootstrap.Table} 
34995      */
34996     getGrid : function(){
34997         return this.grid;    
34998     },
34999     
35000     setSize : function(width, height){
35001         if(!this.ignoreResize(width, height)){
35002             var grid = this.grid;
35003             var size = this.adjustForComponents(width, height);
35004             var gridel = grid.getGridEl();
35005             gridel.setSize(size.width, size.height);
35006             /*
35007             var thd = grid.getGridEl().select('thead',true).first();
35008             var tbd = grid.getGridEl().select('tbody', true).first();
35009             if (tbd) {
35010                 tbd.setSize(width, height - thd.getHeight());
35011             }
35012             */
35013             grid.autoSize();
35014         }
35015     },
35016      
35017     
35018     
35019     beforeSlide : function(){
35020         this.grid.getView().scroller.clip();
35021     },
35022     
35023     afterSlide : function(){
35024         this.grid.getView().scroller.unclip();
35025     },
35026     
35027     destroy : function(){
35028         this.grid.destroy();
35029         delete this.grid;
35030         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
35031     }
35032 });
35033
35034 /**
35035  * @class Roo.bootstrap.panel.Nest
35036  * @extends Roo.bootstrap.panel.Content
35037  * @constructor
35038  * Create a new Panel, that can contain a layout.Border.
35039  * 
35040  * 
35041  * @param {Roo.BorderLayout} layout The layout for this panel
35042  * @param {String/Object} config A string to set only the title or a config object
35043  */
35044 Roo.bootstrap.panel.Nest = function(config)
35045 {
35046     // construct with only one argument..
35047     /* FIXME - implement nicer consturctors
35048     if (layout.layout) {
35049         config = layout;
35050         layout = config.layout;
35051         delete config.layout;
35052     }
35053     if (layout.xtype && !layout.getEl) {
35054         // then layout needs constructing..
35055         layout = Roo.factory(layout, Roo);
35056     }
35057     */
35058     
35059     config.el =  config.layout.getEl();
35060     
35061     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
35062     
35063     config.layout.monitorWindowResize = false; // turn off autosizing
35064     this.layout = config.layout;
35065     this.layout.getEl().addClass("roo-layout-nested-layout");
35066     
35067     
35068     
35069     
35070 };
35071
35072 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
35073
35074     setSize : function(width, height){
35075         if(!this.ignoreResize(width, height)){
35076             var size = this.adjustForComponents(width, height);
35077             var el = this.layout.getEl();
35078             if (size.height < 1) {
35079                 el.setWidth(size.width);   
35080             } else {
35081                 el.setSize(size.width, size.height);
35082             }
35083             var touch = el.dom.offsetWidth;
35084             this.layout.layout();
35085             // ie requires a double layout on the first pass
35086             if(Roo.isIE && !this.initialized){
35087                 this.initialized = true;
35088                 this.layout.layout();
35089             }
35090         }
35091     },
35092     
35093     // activate all subpanels if not currently active..
35094     
35095     setActiveState : function(active){
35096         this.active = active;
35097         this.setActiveClass(active);
35098         
35099         if(!active){
35100             this.fireEvent("deactivate", this);
35101             return;
35102         }
35103         
35104         this.fireEvent("activate", this);
35105         // not sure if this should happen before or after..
35106         if (!this.layout) {
35107             return; // should not happen..
35108         }
35109         var reg = false;
35110         for (var r in this.layout.regions) {
35111             reg = this.layout.getRegion(r);
35112             if (reg.getActivePanel()) {
35113                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
35114                 reg.setActivePanel(reg.getActivePanel());
35115                 continue;
35116             }
35117             if (!reg.panels.length) {
35118                 continue;
35119             }
35120             reg.showPanel(reg.getPanel(0));
35121         }
35122         
35123         
35124         
35125         
35126     },
35127     
35128     /**
35129      * Returns the nested BorderLayout for this panel
35130      * @return {Roo.BorderLayout} 
35131      */
35132     getLayout : function(){
35133         return this.layout;
35134     },
35135     
35136      /**
35137      * Adds a xtype elements to the layout of the nested panel
35138      * <pre><code>
35139
35140 panel.addxtype({
35141        xtype : 'ContentPanel',
35142        region: 'west',
35143        items: [ .... ]
35144    }
35145 );
35146
35147 panel.addxtype({
35148         xtype : 'NestedLayoutPanel',
35149         region: 'west',
35150         layout: {
35151            center: { },
35152            west: { }   
35153         },
35154         items : [ ... list of content panels or nested layout panels.. ]
35155    }
35156 );
35157 </code></pre>
35158      * @param {Object} cfg Xtype definition of item to add.
35159      */
35160     addxtype : function(cfg) {
35161         return this.layout.addxtype(cfg);
35162     
35163     }
35164 });        /*
35165  * Based on:
35166  * Ext JS Library 1.1.1
35167  * Copyright(c) 2006-2007, Ext JS, LLC.
35168  *
35169  * Originally Released Under LGPL - original licence link has changed is not relivant.
35170  *
35171  * Fork - LGPL
35172  * <script type="text/javascript">
35173  */
35174 /**
35175  * @class Roo.TabPanel
35176  * @extends Roo.util.Observable
35177  * A lightweight tab container.
35178  * <br><br>
35179  * Usage:
35180  * <pre><code>
35181 // basic tabs 1, built from existing content
35182 var tabs = new Roo.TabPanel("tabs1");
35183 tabs.addTab("script", "View Script");
35184 tabs.addTab("markup", "View Markup");
35185 tabs.activate("script");
35186
35187 // more advanced tabs, built from javascript
35188 var jtabs = new Roo.TabPanel("jtabs");
35189 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
35190
35191 // set up the UpdateManager
35192 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
35193 var updater = tab2.getUpdateManager();
35194 updater.setDefaultUrl("ajax1.htm");
35195 tab2.on('activate', updater.refresh, updater, true);
35196
35197 // Use setUrl for Ajax loading
35198 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
35199 tab3.setUrl("ajax2.htm", null, true);
35200
35201 // Disabled tab
35202 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
35203 tab4.disable();
35204
35205 jtabs.activate("jtabs-1");
35206  * </code></pre>
35207  * @constructor
35208  * Create a new TabPanel.
35209  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
35210  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
35211  */
35212 Roo.bootstrap.panel.Tabs = function(config){
35213     /**
35214     * The container element for this TabPanel.
35215     * @type Roo.Element
35216     */
35217     this.el = Roo.get(config.el);
35218     delete config.el;
35219     if(config){
35220         if(typeof config == "boolean"){
35221             this.tabPosition = config ? "bottom" : "top";
35222         }else{
35223             Roo.apply(this, config);
35224         }
35225     }
35226     
35227     if(this.tabPosition == "bottom"){
35228         this.bodyEl = Roo.get(this.createBody(this.el.dom));
35229         this.el.addClass("roo-tabs-bottom");
35230     }
35231     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
35232     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
35233     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
35234     if(Roo.isIE){
35235         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
35236     }
35237     if(this.tabPosition != "bottom"){
35238         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
35239          * @type Roo.Element
35240          */
35241         this.bodyEl = Roo.get(this.createBody(this.el.dom));
35242         this.el.addClass("roo-tabs-top");
35243     }
35244     this.items = [];
35245
35246     this.bodyEl.setStyle("position", "relative");
35247
35248     this.active = null;
35249     this.activateDelegate = this.activate.createDelegate(this);
35250
35251     this.addEvents({
35252         /**
35253          * @event tabchange
35254          * Fires when the active tab changes
35255          * @param {Roo.TabPanel} this
35256          * @param {Roo.TabPanelItem} activePanel The new active tab
35257          */
35258         "tabchange": true,
35259         /**
35260          * @event beforetabchange
35261          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
35262          * @param {Roo.TabPanel} this
35263          * @param {Object} e Set cancel to true on this object to cancel the tab change
35264          * @param {Roo.TabPanelItem} tab The tab being changed to
35265          */
35266         "beforetabchange" : true
35267     });
35268
35269     Roo.EventManager.onWindowResize(this.onResize, this);
35270     this.cpad = this.el.getPadding("lr");
35271     this.hiddenCount = 0;
35272
35273
35274     // toolbar on the tabbar support...
35275     if (this.toolbar) {
35276         alert("no toolbar support yet");
35277         this.toolbar  = false;
35278         /*
35279         var tcfg = this.toolbar;
35280         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
35281         this.toolbar = new Roo.Toolbar(tcfg);
35282         if (Roo.isSafari) {
35283             var tbl = tcfg.container.child('table', true);
35284             tbl.setAttribute('width', '100%');
35285         }
35286         */
35287         
35288     }
35289    
35290
35291
35292     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
35293 };
35294
35295 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
35296     /*
35297      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
35298      */
35299     tabPosition : "top",
35300     /*
35301      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
35302      */
35303     currentTabWidth : 0,
35304     /*
35305      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
35306      */
35307     minTabWidth : 40,
35308     /*
35309      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
35310      */
35311     maxTabWidth : 250,
35312     /*
35313      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
35314      */
35315     preferredTabWidth : 175,
35316     /*
35317      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
35318      */
35319     resizeTabs : false,
35320     /*
35321      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
35322      */
35323     monitorResize : true,
35324     /*
35325      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
35326      */
35327     toolbar : false,
35328
35329     /**
35330      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
35331      * @param {String} id The id of the div to use <b>or create</b>
35332      * @param {String} text The text for the tab
35333      * @param {String} content (optional) Content to put in the TabPanelItem body
35334      * @param {Boolean} closable (optional) True to create a close icon on the tab
35335      * @return {Roo.TabPanelItem} The created TabPanelItem
35336      */
35337     addTab : function(id, text, content, closable, tpl)
35338     {
35339         var item = new Roo.bootstrap.panel.TabItem({
35340             panel: this,
35341             id : id,
35342             text : text,
35343             closable : closable,
35344             tpl : tpl
35345         });
35346         this.addTabItem(item);
35347         if(content){
35348             item.setContent(content);
35349         }
35350         return item;
35351     },
35352
35353     /**
35354      * Returns the {@link Roo.TabPanelItem} with the specified id/index
35355      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
35356      * @return {Roo.TabPanelItem}
35357      */
35358     getTab : function(id){
35359         return this.items[id];
35360     },
35361
35362     /**
35363      * Hides the {@link Roo.TabPanelItem} with the specified id/index
35364      * @param {String/Number} id The id or index of the TabPanelItem to hide.
35365      */
35366     hideTab : function(id){
35367         var t = this.items[id];
35368         if(!t.isHidden()){
35369            t.setHidden(true);
35370            this.hiddenCount++;
35371            this.autoSizeTabs();
35372         }
35373     },
35374
35375     /**
35376      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
35377      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
35378      */
35379     unhideTab : function(id){
35380         var t = this.items[id];
35381         if(t.isHidden()){
35382            t.setHidden(false);
35383            this.hiddenCount--;
35384            this.autoSizeTabs();
35385         }
35386     },
35387
35388     /**
35389      * Adds an existing {@link Roo.TabPanelItem}.
35390      * @param {Roo.TabPanelItem} item The TabPanelItem to add
35391      */
35392     addTabItem : function(item){
35393         this.items[item.id] = item;
35394         this.items.push(item);
35395       //  if(this.resizeTabs){
35396     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
35397   //         this.autoSizeTabs();
35398 //        }else{
35399 //            item.autoSize();
35400        // }
35401     },
35402
35403     /**
35404      * Removes a {@link Roo.TabPanelItem}.
35405      * @param {String/Number} id The id or index of the TabPanelItem to remove.
35406      */
35407     removeTab : function(id){
35408         var items = this.items;
35409         var tab = items[id];
35410         if(!tab) { return; }
35411         var index = items.indexOf(tab);
35412         if(this.active == tab && items.length > 1){
35413             var newTab = this.getNextAvailable(index);
35414             if(newTab) {
35415                 newTab.activate();
35416             }
35417         }
35418         this.stripEl.dom.removeChild(tab.pnode.dom);
35419         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
35420             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
35421         }
35422         items.splice(index, 1);
35423         delete this.items[tab.id];
35424         tab.fireEvent("close", tab);
35425         tab.purgeListeners();
35426         this.autoSizeTabs();
35427     },
35428
35429     getNextAvailable : function(start){
35430         var items = this.items;
35431         var index = start;
35432         // look for a next tab that will slide over to
35433         // replace the one being removed
35434         while(index < items.length){
35435             var item = items[++index];
35436             if(item && !item.isHidden()){
35437                 return item;
35438             }
35439         }
35440         // if one isn't found select the previous tab (on the left)
35441         index = start;
35442         while(index >= 0){
35443             var item = items[--index];
35444             if(item && !item.isHidden()){
35445                 return item;
35446             }
35447         }
35448         return null;
35449     },
35450
35451     /**
35452      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
35453      * @param {String/Number} id The id or index of the TabPanelItem to disable.
35454      */
35455     disableTab : function(id){
35456         var tab = this.items[id];
35457         if(tab && this.active != tab){
35458             tab.disable();
35459         }
35460     },
35461
35462     /**
35463      * Enables a {@link Roo.TabPanelItem} that is disabled.
35464      * @param {String/Number} id The id or index of the TabPanelItem to enable.
35465      */
35466     enableTab : function(id){
35467         var tab = this.items[id];
35468         tab.enable();
35469     },
35470
35471     /**
35472      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
35473      * @param {String/Number} id The id or index of the TabPanelItem to activate.
35474      * @return {Roo.TabPanelItem} The TabPanelItem.
35475      */
35476     activate : function(id){
35477         var tab = this.items[id];
35478         if(!tab){
35479             return null;
35480         }
35481         if(tab == this.active || tab.disabled){
35482             return tab;
35483         }
35484         var e = {};
35485         this.fireEvent("beforetabchange", this, e, tab);
35486         if(e.cancel !== true && !tab.disabled){
35487             if(this.active){
35488                 this.active.hide();
35489             }
35490             this.active = this.items[id];
35491             this.active.show();
35492             this.fireEvent("tabchange", this, this.active);
35493         }
35494         return tab;
35495     },
35496
35497     /**
35498      * Gets the active {@link Roo.TabPanelItem}.
35499      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
35500      */
35501     getActiveTab : function(){
35502         return this.active;
35503     },
35504
35505     /**
35506      * Updates the tab body element to fit the height of the container element
35507      * for overflow scrolling
35508      * @param {Number} targetHeight (optional) Override the starting height from the elements height
35509      */
35510     syncHeight : function(targetHeight){
35511         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35512         var bm = this.bodyEl.getMargins();
35513         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
35514         this.bodyEl.setHeight(newHeight);
35515         return newHeight;
35516     },
35517
35518     onResize : function(){
35519         if(this.monitorResize){
35520             this.autoSizeTabs();
35521         }
35522     },
35523
35524     /**
35525      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
35526      */
35527     beginUpdate : function(){
35528         this.updating = true;
35529     },
35530
35531     /**
35532      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
35533      */
35534     endUpdate : function(){
35535         this.updating = false;
35536         this.autoSizeTabs();
35537     },
35538
35539     /**
35540      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
35541      */
35542     autoSizeTabs : function(){
35543         var count = this.items.length;
35544         var vcount = count - this.hiddenCount;
35545         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
35546             return;
35547         }
35548         var w = Math.max(this.el.getWidth() - this.cpad, 10);
35549         var availWidth = Math.floor(w / vcount);
35550         var b = this.stripBody;
35551         if(b.getWidth() > w){
35552             var tabs = this.items;
35553             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
35554             if(availWidth < this.minTabWidth){
35555                 /*if(!this.sleft){    // incomplete scrolling code
35556                     this.createScrollButtons();
35557                 }
35558                 this.showScroll();
35559                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
35560             }
35561         }else{
35562             if(this.currentTabWidth < this.preferredTabWidth){
35563                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
35564             }
35565         }
35566     },
35567
35568     /**
35569      * Returns the number of tabs in this TabPanel.
35570      * @return {Number}
35571      */
35572      getCount : function(){
35573          return this.items.length;
35574      },
35575
35576     /**
35577      * Resizes all the tabs to the passed width
35578      * @param {Number} The new width
35579      */
35580     setTabWidth : function(width){
35581         this.currentTabWidth = width;
35582         for(var i = 0, len = this.items.length; i < len; i++) {
35583                 if(!this.items[i].isHidden()) {
35584                 this.items[i].setWidth(width);
35585             }
35586         }
35587     },
35588
35589     /**
35590      * Destroys this TabPanel
35591      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
35592      */
35593     destroy : function(removeEl){
35594         Roo.EventManager.removeResizeListener(this.onResize, this);
35595         for(var i = 0, len = this.items.length; i < len; i++){
35596             this.items[i].purgeListeners();
35597         }
35598         if(removeEl === true){
35599             this.el.update("");
35600             this.el.remove();
35601         }
35602     },
35603     
35604     createStrip : function(container)
35605     {
35606         var strip = document.createElement("nav");
35607         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
35608         container.appendChild(strip);
35609         return strip;
35610     },
35611     
35612     createStripList : function(strip)
35613     {
35614         // div wrapper for retard IE
35615         // returns the "tr" element.
35616         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
35617         //'<div class="x-tabs-strip-wrap">'+
35618           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
35619           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
35620         return strip.firstChild; //.firstChild.firstChild.firstChild;
35621     },
35622     createBody : function(container)
35623     {
35624         var body = document.createElement("div");
35625         Roo.id(body, "tab-body");
35626         //Roo.fly(body).addClass("x-tabs-body");
35627         Roo.fly(body).addClass("tab-content");
35628         container.appendChild(body);
35629         return body;
35630     },
35631     createItemBody :function(bodyEl, id){
35632         var body = Roo.getDom(id);
35633         if(!body){
35634             body = document.createElement("div");
35635             body.id = id;
35636         }
35637         //Roo.fly(body).addClass("x-tabs-item-body");
35638         Roo.fly(body).addClass("tab-pane");
35639          bodyEl.insertBefore(body, bodyEl.firstChild);
35640         return body;
35641     },
35642     /** @private */
35643     createStripElements :  function(stripEl, text, closable, tpl)
35644     {
35645         var td = document.createElement("li"); // was td..
35646         
35647         
35648         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
35649         
35650         
35651         stripEl.appendChild(td);
35652         /*if(closable){
35653             td.className = "x-tabs-closable";
35654             if(!this.closeTpl){
35655                 this.closeTpl = new Roo.Template(
35656                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
35657                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
35658                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
35659                 );
35660             }
35661             var el = this.closeTpl.overwrite(td, {"text": text});
35662             var close = el.getElementsByTagName("div")[0];
35663             var inner = el.getElementsByTagName("em")[0];
35664             return {"el": el, "close": close, "inner": inner};
35665         } else {
35666         */
35667         // not sure what this is..
35668 //            if(!this.tabTpl){
35669                 //this.tabTpl = new Roo.Template(
35670                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
35671                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
35672                 //);
35673 //                this.tabTpl = new Roo.Template(
35674 //                   '<a href="#">' +
35675 //                   '<span unselectable="on"' +
35676 //                            (this.disableTooltips ? '' : ' title="{text}"') +
35677 //                            ' >{text}</span></a>'
35678 //                );
35679 //                
35680 //            }
35681
35682
35683             var template = tpl || this.tabTpl || false;
35684             
35685             if(!template){
35686                 
35687                 template = new Roo.Template(
35688                    '<a href="#">' +
35689                    '<span unselectable="on"' +
35690                             (this.disableTooltips ? '' : ' title="{text}"') +
35691                             ' >{text}</span></a>'
35692                 );
35693             }
35694             
35695             switch (typeof(template)) {
35696                 case 'object' :
35697                     break;
35698                 case 'string' :
35699                     template = new Roo.Template(template);
35700                     break;
35701                 default :
35702                     break;
35703             }
35704             
35705             var el = template.overwrite(td, {"text": text});
35706             
35707             var inner = el.getElementsByTagName("span")[0];
35708             
35709             return {"el": el, "inner": inner};
35710             
35711     }
35712         
35713     
35714 });
35715
35716 /**
35717  * @class Roo.TabPanelItem
35718  * @extends Roo.util.Observable
35719  * Represents an individual item (tab plus body) in a TabPanel.
35720  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
35721  * @param {String} id The id of this TabPanelItem
35722  * @param {String} text The text for the tab of this TabPanelItem
35723  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
35724  */
35725 Roo.bootstrap.panel.TabItem = function(config){
35726     /**
35727      * The {@link Roo.TabPanel} this TabPanelItem belongs to
35728      * @type Roo.TabPanel
35729      */
35730     this.tabPanel = config.panel;
35731     /**
35732      * The id for this TabPanelItem
35733      * @type String
35734      */
35735     this.id = config.id;
35736     /** @private */
35737     this.disabled = false;
35738     /** @private */
35739     this.text = config.text;
35740     /** @private */
35741     this.loaded = false;
35742     this.closable = config.closable;
35743
35744     /**
35745      * The body element for this TabPanelItem.
35746      * @type Roo.Element
35747      */
35748     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
35749     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
35750     this.bodyEl.setStyle("display", "block");
35751     this.bodyEl.setStyle("zoom", "1");
35752     //this.hideAction();
35753
35754     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
35755     /** @private */
35756     this.el = Roo.get(els.el);
35757     this.inner = Roo.get(els.inner, true);
35758     this.textEl = Roo.get(this.el.dom.firstChild, true);
35759     this.pnode = Roo.get(els.el.parentNode, true);
35760     this.el.on("mousedown", this.onTabMouseDown, this);
35761     this.el.on("click", this.onTabClick, this);
35762     /** @private */
35763     if(config.closable){
35764         var c = Roo.get(els.close, true);
35765         c.dom.title = this.closeText;
35766         c.addClassOnOver("close-over");
35767         c.on("click", this.closeClick, this);
35768      }
35769
35770     this.addEvents({
35771          /**
35772          * @event activate
35773          * Fires when this tab becomes the active tab.
35774          * @param {Roo.TabPanel} tabPanel The parent TabPanel
35775          * @param {Roo.TabPanelItem} this
35776          */
35777         "activate": true,
35778         /**
35779          * @event beforeclose
35780          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
35781          * @param {Roo.TabPanelItem} this
35782          * @param {Object} e Set cancel to true on this object to cancel the close.
35783          */
35784         "beforeclose": true,
35785         /**
35786          * @event close
35787          * Fires when this tab is closed.
35788          * @param {Roo.TabPanelItem} this
35789          */
35790          "close": true,
35791         /**
35792          * @event deactivate
35793          * Fires when this tab is no longer the active tab.
35794          * @param {Roo.TabPanel} tabPanel The parent TabPanel
35795          * @param {Roo.TabPanelItem} this
35796          */
35797          "deactivate" : true
35798     });
35799     this.hidden = false;
35800
35801     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
35802 };
35803
35804 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
35805            {
35806     purgeListeners : function(){
35807        Roo.util.Observable.prototype.purgeListeners.call(this);
35808        this.el.removeAllListeners();
35809     },
35810     /**
35811      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
35812      */
35813     show : function(){
35814         this.pnode.addClass("active");
35815         this.showAction();
35816         if(Roo.isOpera){
35817             this.tabPanel.stripWrap.repaint();
35818         }
35819         this.fireEvent("activate", this.tabPanel, this);
35820     },
35821
35822     /**
35823      * Returns true if this tab is the active tab.
35824      * @return {Boolean}
35825      */
35826     isActive : function(){
35827         return this.tabPanel.getActiveTab() == this;
35828     },
35829
35830     /**
35831      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
35832      */
35833     hide : function(){
35834         this.pnode.removeClass("active");
35835         this.hideAction();
35836         this.fireEvent("deactivate", this.tabPanel, this);
35837     },
35838
35839     hideAction : function(){
35840         this.bodyEl.hide();
35841         this.bodyEl.setStyle("position", "absolute");
35842         this.bodyEl.setLeft("-20000px");
35843         this.bodyEl.setTop("-20000px");
35844     },
35845
35846     showAction : function(){
35847         this.bodyEl.setStyle("position", "relative");
35848         this.bodyEl.setTop("");
35849         this.bodyEl.setLeft("");
35850         this.bodyEl.show();
35851     },
35852
35853     /**
35854      * Set the tooltip for the tab.
35855      * @param {String} tooltip The tab's tooltip
35856      */
35857     setTooltip : function(text){
35858         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
35859             this.textEl.dom.qtip = text;
35860             this.textEl.dom.removeAttribute('title');
35861         }else{
35862             this.textEl.dom.title = text;
35863         }
35864     },
35865
35866     onTabClick : function(e){
35867         e.preventDefault();
35868         this.tabPanel.activate(this.id);
35869     },
35870
35871     onTabMouseDown : function(e){
35872         e.preventDefault();
35873         this.tabPanel.activate(this.id);
35874     },
35875 /*
35876     getWidth : function(){
35877         return this.inner.getWidth();
35878     },
35879
35880     setWidth : function(width){
35881         var iwidth = width - this.pnode.getPadding("lr");
35882         this.inner.setWidth(iwidth);
35883         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
35884         this.pnode.setWidth(width);
35885     },
35886 */
35887     /**
35888      * Show or hide the tab
35889      * @param {Boolean} hidden True to hide or false to show.
35890      */
35891     setHidden : function(hidden){
35892         this.hidden = hidden;
35893         this.pnode.setStyle("display", hidden ? "none" : "");
35894     },
35895
35896     /**
35897      * Returns true if this tab is "hidden"
35898      * @return {Boolean}
35899      */
35900     isHidden : function(){
35901         return this.hidden;
35902     },
35903
35904     /**
35905      * Returns the text for this tab
35906      * @return {String}
35907      */
35908     getText : function(){
35909         return this.text;
35910     },
35911     /*
35912     autoSize : function(){
35913         //this.el.beginMeasure();
35914         this.textEl.setWidth(1);
35915         /*
35916          *  #2804 [new] Tabs in Roojs
35917          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
35918          */
35919         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
35920         //this.el.endMeasure();
35921     //},
35922
35923     /**
35924      * Sets the text for the tab (Note: this also sets the tooltip text)
35925      * @param {String} text The tab's text and tooltip
35926      */
35927     setText : function(text){
35928         this.text = text;
35929         this.textEl.update(text);
35930         this.setTooltip(text);
35931         //if(!this.tabPanel.resizeTabs){
35932         //    this.autoSize();
35933         //}
35934     },
35935     /**
35936      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
35937      */
35938     activate : function(){
35939         this.tabPanel.activate(this.id);
35940     },
35941
35942     /**
35943      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
35944      */
35945     disable : function(){
35946         if(this.tabPanel.active != this){
35947             this.disabled = true;
35948             this.pnode.addClass("disabled");
35949         }
35950     },
35951
35952     /**
35953      * Enables this TabPanelItem if it was previously disabled.
35954      */
35955     enable : function(){
35956         this.disabled = false;
35957         this.pnode.removeClass("disabled");
35958     },
35959
35960     /**
35961      * Sets the content for this TabPanelItem.
35962      * @param {String} content The content
35963      * @param {Boolean} loadScripts true to look for and load scripts
35964      */
35965     setContent : function(content, loadScripts){
35966         this.bodyEl.update(content, loadScripts);
35967     },
35968
35969     /**
35970      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
35971      * @return {Roo.UpdateManager} The UpdateManager
35972      */
35973     getUpdateManager : function(){
35974         return this.bodyEl.getUpdateManager();
35975     },
35976
35977     /**
35978      * Set a URL to be used to load the content for this TabPanelItem.
35979      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
35980      * @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)
35981      * @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)
35982      * @return {Roo.UpdateManager} The UpdateManager
35983      */
35984     setUrl : function(url, params, loadOnce){
35985         if(this.refreshDelegate){
35986             this.un('activate', this.refreshDelegate);
35987         }
35988         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35989         this.on("activate", this.refreshDelegate);
35990         return this.bodyEl.getUpdateManager();
35991     },
35992
35993     /** @private */
35994     _handleRefresh : function(url, params, loadOnce){
35995         if(!loadOnce || !this.loaded){
35996             var updater = this.bodyEl.getUpdateManager();
35997             updater.update(url, params, this._setLoaded.createDelegate(this));
35998         }
35999     },
36000
36001     /**
36002      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
36003      *   Will fail silently if the setUrl method has not been called.
36004      *   This does not activate the panel, just updates its content.
36005      */
36006     refresh : function(){
36007         if(this.refreshDelegate){
36008            this.loaded = false;
36009            this.refreshDelegate();
36010         }
36011     },
36012
36013     /** @private */
36014     _setLoaded : function(){
36015         this.loaded = true;
36016     },
36017
36018     /** @private */
36019     closeClick : function(e){
36020         var o = {};
36021         e.stopEvent();
36022         this.fireEvent("beforeclose", this, o);
36023         if(o.cancel !== true){
36024             this.tabPanel.removeTab(this.id);
36025         }
36026     },
36027     /**
36028      * The text displayed in the tooltip for the close icon.
36029      * @type String
36030      */
36031     closeText : "Close this tab"
36032 });