Roo/grid/ColumnModel.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         
105         cfg.id = this.id || Roo.id();
106         
107         // fill in the extra attributes 
108         if (this.xattr && typeof(this.xattr) =='object') {
109             for (var i in this.xattr) {
110                 cfg[i] = this.xattr[i];
111             }
112         }
113         
114         if(this.dataId){
115             cfg.dataId = this.dataId;
116         }
117         
118         if (this.cls) {
119             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
120         }
121         
122         if (this.style) { // fixme needs to support more complex style data.
123             cfg.style = this.style;
124         }
125         
126         if(this.name){
127             cfg.name = this.name;
128         }
129         
130         this.el = ct.createChild(cfg, position);
131         
132         if (this.tooltip) {
133             this.tooltipEl().attr('tooltip', this.tooltip);
134         }
135         
136         if(this.tabIndex !== undefined){
137             this.el.dom.setAttribute('tabIndex', this.tabIndex);
138         }
139         
140         this.initEvents();
141         
142     },
143     /**
144      * Fetch the element to add children to
145      * @return {Roo.Element} defaults to this.el
146      */
147     getChildContainer : function()
148     {
149         return this.el;
150     },
151     /**
152      * Fetch the element to display the tooltip on.
153      * @return {Roo.Element} defaults to this.el
154      */
155     tooltipEl : function()
156     {
157         return this.el;
158     },
159         
160     addxtype  : function(tree,cntr)
161     {
162         var cn = this;
163         
164         cn = Roo.factory(tree);
165         //Roo.log(['addxtype', cn]);
166            
167         cn.parentType = this.xtype; //??
168         cn.parentId = this.id;
169         
170         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171         if (typeof(cn.container_method) == 'string') {
172             cntr = cn.container_method;
173         }
174         
175         
176         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
177         
178         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
179         
180         var build_from_html =  Roo.XComponent.build_from_html;
181           
182         var is_body  = (tree.xtype == 'Body') ;
183           
184         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
185           
186         var self_cntr_el = Roo.get(this[cntr](false));
187         
188         // do not try and build conditional elements 
189         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
190             return false;
191         }
192         
193         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195                 return this.addxtypeChild(tree,cntr, is_body);
196             }
197             
198             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
199                 
200             if(echild){
201                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
202             }
203             
204             Roo.log('skipping render');
205             return cn;
206             
207         }
208         
209         var ret = false;
210         if (!build_from_html) {
211             return false;
212         }
213         
214         // this i think handles overlaying multiple children of the same type
215         // with the sam eelement.. - which might be buggy..
216         while (true) {
217             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
218             
219             if (!echild) {
220                 break;
221             }
222             
223             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
224                 break;
225             }
226             
227             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
228         }
229        
230         return ret;
231     },
232     
233     
234     addxtypeChild : function (tree, cntr, is_body)
235     {
236         Roo.debug && Roo.log('addxtypeChild:' + cntr);
237         var cn = this;
238         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
239         
240         
241         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
242                     (typeof(tree['flexy:foreach']) != 'undefined');
243           
244         
245         
246          skip_children = false;
247         // render the element if it's not BODY.
248         if (!is_body) {
249            
250             cn = Roo.factory(tree);
251            
252             cn.parentType = this.xtype; //??
253             cn.parentId = this.id;
254             
255             var build_from_html =  Roo.XComponent.build_from_html;
256             
257             
258             // does the container contain child eleemnts with 'xtype' attributes.
259             // that match this xtype..
260             // note - when we render we create these as well..
261             // so we should check to see if body has xtype set.
262             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
263                
264                 var self_cntr_el = Roo.get(this[cntr](false));
265                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
266                 if (echild) { 
267                     //Roo.log(Roo.XComponent.build_from_html);
268                     //Roo.log("got echild:");
269                     //Roo.log(echild);
270                 }
271                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
272                 // and are not displayed -this causes this to use up the wrong element when matching.
273                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
274                 
275                 
276                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
277                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
278                   
279                   
280                   
281                     cn.el = echild;
282                   //  Roo.log("GOT");
283                     //echild.dom.removeAttribute('xtype');
284                 } else {
285                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
286                     Roo.debug && Roo.log(self_cntr_el);
287                     Roo.debug && Roo.log(echild);
288                     Roo.debug && Roo.log(cn);
289                 }
290             }
291            
292             
293            
294             // if object has flexy:if - then it may or may not be rendered.
295             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
296                 // skip a flexy if element.
297                 Roo.debug && Roo.log('skipping render');
298                 Roo.debug && Roo.log(tree);
299                 if (!cn.el) {
300                     Roo.debug && Roo.log('skipping all children');
301                     skip_children = true;
302                 }
303                 
304              } else {
305                  
306                 // actually if flexy:foreach is found, we really want to create 
307                 // multiple copies here...
308                 //Roo.log('render');
309                 //Roo.log(this[cntr]());
310                 // some elements do not have render methods.. like the layouts...
311                 cn.render && cn.render(this[cntr](true));
312              }
313             // then add the element..
314         }
315         
316         
317         // handle the kids..
318         
319         var nitems = [];
320         /*
321         if (typeof (tree.menu) != 'undefined') {
322             tree.menu.parentType = cn.xtype;
323             tree.menu.triggerEl = cn.el;
324             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
325             
326         }
327         */
328         if (!tree.items || !tree.items.length) {
329             cn.items = nitems;
330             //Roo.log(["no children", this]);
331             
332             return cn;
333         }
334          
335         var items = tree.items;
336         delete tree.items;
337         
338         //Roo.log(items.length);
339             // add the items..
340         if (!skip_children) {    
341             for(var i =0;i < items.length;i++) {
342               //  Roo.log(['add child', items[i]]);
343                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
344             }
345         }
346         
347         cn.items = nitems;
348         
349         //Roo.log("fire childrenrendered");
350         
351         cn.fireEvent('childrenrendered', this);
352         
353         return cn;
354     },
355     /**
356      * Show a component - removes 'hidden' class
357      */
358     show : function()
359     {
360         if (this.el) {
361             this.el.removeClass('hidden');
362         }
363     },
364     /**
365      * Hide a component - adds 'hidden' class
366      */
367     hide: function()
368     {
369         if (this.el && !this.el.hasClass('hidden')) {
370             this.el.addClass('hidden');
371         }
372         
373     }
374 });
375
376  /*
377  * - LGPL
378  *
379  * Body
380  *
381  */
382
383 /**
384  * @class Roo.bootstrap.Body
385  * @extends Roo.bootstrap.Component
386  * Bootstrap Body class
387  *
388  * @constructor
389  * Create a new body
390  * @param {Object} config The config object
391  */
392
393 Roo.bootstrap.Body = function(config){
394
395     config = config || {};
396
397     Roo.bootstrap.Body.superclass.constructor.call(this, config);
398     this.el = Roo.get(config.el ? config.el : document.body );
399     if (this.cls && this.cls.length) {
400         Roo.get(document.body).addClass(this.cls);
401     }
402 };
403
404 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
405
406     is_body : true,// just to make sure it's constructed?
407
408         autoCreate : {
409         cls: 'container'
410     },
411     onRender : function(ct, position)
412     {
413        /* Roo.log("Roo.bootstrap.Body - onRender");
414         if (this.cls && this.cls.length) {
415             Roo.get(document.body).addClass(this.cls);
416         }
417         // style??? xttr???
418         */
419     }
420
421
422
423
424 });
425 /*
426  * - LGPL
427  *
428  * button group
429  * 
430  */
431
432
433 /**
434  * @class Roo.bootstrap.ButtonGroup
435  * @extends Roo.bootstrap.Component
436  * Bootstrap ButtonGroup class
437  * @cfg {String} size lg | sm | xs (default empty normal)
438  * @cfg {String} align vertical | justified  (default none)
439  * @cfg {String} direction up | down (default down)
440  * @cfg {Boolean} toolbar false | true
441  * @cfg {Boolean} btn true | false
442  * 
443  * 
444  * @constructor
445  * Create a new Input
446  * @param {Object} config The config object
447  */
448
449 Roo.bootstrap.ButtonGroup = function(config){
450     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
451 };
452
453 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
454     
455     size: '',
456     align: '',
457     direction: '',
458     toolbar: false,
459     btn: true,
460
461     getAutoCreate : function(){
462         var cfg = {
463             cls: 'btn-group',
464             html : null
465         };
466         
467         cfg.html = this.html || cfg.html;
468         
469         if (this.toolbar) {
470             cfg = {
471                 cls: 'btn-toolbar',
472                 html: null
473             };
474             
475             return cfg;
476         }
477         
478         if (['vertical','justified'].indexOf(this.align)!==-1) {
479             cfg.cls = 'btn-group-' + this.align;
480             
481             if (this.align == 'justified') {
482                 console.log(this.items);
483             }
484         }
485         
486         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
487             cfg.cls += ' btn-group-' + this.size;
488         }
489         
490         if (this.direction == 'up') {
491             cfg.cls += ' dropup' ;
492         }
493         
494         return cfg;
495     }
496    
497 });
498
499  /*
500  * - LGPL
501  *
502  * button
503  * 
504  */
505
506 /**
507  * @class Roo.bootstrap.Button
508  * @extends Roo.bootstrap.Component
509  * Bootstrap Button class
510  * @cfg {String} html The button content
511  * @cfg {String} weight (  primary | success | info | warning | danger | link ) default 
512  * @cfg {String} size ( lg | sm | xs)
513  * @cfg {String} tag ( a | input | submit)
514  * @cfg {String} href empty or href
515  * @cfg {Boolean} disabled default false;
516  * @cfg {Boolean} isClose default false;
517  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
518  * @cfg {String} badge text for badge
519  * @cfg {String} theme default 
520  * @cfg {Boolean} inverse 
521  * @cfg {Boolean} toggle 
522  * @cfg {String} ontext text for on toggle state
523  * @cfg {String} offtext text for off toggle state
524  * @cfg {Boolean} defaulton 
525  * @cfg {Boolean} preventDefault  default true
526  * @cfg {Boolean} removeClass remove the standard class..
527  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
528  * 
529  * @constructor
530  * Create a new button
531  * @param {Object} config The config object
532  */
533
534
535 Roo.bootstrap.Button = function(config){
536     Roo.bootstrap.Button.superclass.constructor.call(this, config);
537     this.addEvents({
538         // raw events
539         /**
540          * @event click
541          * When a butotn is pressed
542          * @param {Roo.bootstrap.Button} this
543          * @param {Roo.EventObject} e
544          */
545         "click" : true,
546          /**
547          * @event toggle
548          * After the button has been toggles
549          * @param {Roo.EventObject} e
550          * @param {boolean} pressed (also available as button.pressed)
551          */
552         "toggle" : true
553     });
554 };
555
556 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
557     html: false,
558     active: false,
559     weight: '',
560     size: '',
561     tag: 'button',
562     href: '',
563     disabled: false,
564     isClose: false,
565     glyphicon: '',
566     badge: '',
567     theme: 'default',
568     inverse: false,
569     
570     toggle: false,
571     ontext: 'ON',
572     offtext: 'OFF',
573     defaulton: true,
574     preventDefault: true,
575     removeClass: false,
576     name: false,
577     target: false,
578     
579     
580     pressed : null,
581      
582     
583     getAutoCreate : function(){
584         
585         var cfg = {
586             tag : 'button',
587             cls : 'roo-button',
588             html: ''
589         };
590         
591         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
592             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
593             this.tag = 'button';
594         } else {
595             cfg.tag = this.tag;
596         }
597         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
598         
599         if (this.toggle == true) {
600             cfg={
601                 tag: 'div',
602                 cls: 'slider-frame roo-button',
603                 cn: [
604                     {
605                         tag: 'span',
606                         'data-on-text':'ON',
607                         'data-off-text':'OFF',
608                         cls: 'slider-button',
609                         html: this.offtext
610                     }
611                 ]
612             };
613             
614             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
615                 cfg.cls += ' '+this.weight;
616             }
617             
618             return cfg;
619         }
620         
621         if (this.isClose) {
622             cfg.cls += ' close';
623             
624             cfg["aria-hidden"] = true;
625             
626             cfg.html = "&times;";
627             
628             return cfg;
629         }
630         
631          
632         if (this.theme==='default') {
633             cfg.cls = 'btn roo-button';
634             
635             //if (this.parentType != 'Navbar') {
636             this.weight = this.weight.length ?  this.weight : 'default';
637             //}
638             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
639                 
640                 cfg.cls += ' btn-' + this.weight;
641             }
642         } else if (this.theme==='glow') {
643             
644             cfg.tag = 'a';
645             cfg.cls = 'btn-glow roo-button';
646             
647             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
648                 
649                 cfg.cls += ' ' + this.weight;
650             }
651         }
652    
653         
654         if (this.inverse) {
655             this.cls += ' inverse';
656         }
657         
658         
659         if (this.active) {
660             cfg.cls += ' active';
661         }
662         
663         if (this.disabled) {
664             cfg.disabled = 'disabled';
665         }
666         
667         if (this.items) {
668             Roo.log('changing to ul' );
669             cfg.tag = 'ul';
670             this.glyphicon = 'caret';
671         }
672         
673         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
674          
675         //gsRoo.log(this.parentType);
676         if (this.parentType === 'Navbar' && !this.parent().bar) {
677             Roo.log('changing to li?');
678             
679             cfg.tag = 'li';
680             
681             cfg.cls = '';
682             cfg.cn =  [{
683                 tag : 'a',
684                 cls : 'roo-button',
685                 html : this.html,
686                 href : this.href || '#'
687             }];
688             if (this.menu) {
689                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
690                 cfg.cls += ' dropdown';
691             }   
692             
693             delete cfg.html;
694             
695         }
696         
697        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
698         
699         if (this.glyphicon) {
700             cfg.html = ' ' + cfg.html;
701             
702             cfg.cn = [
703                 {
704                     tag: 'span',
705                     cls: 'glyphicon glyphicon-' + this.glyphicon
706                 }
707             ];
708         }
709         
710         if (this.badge) {
711             cfg.html += ' ';
712             
713             cfg.tag = 'a';
714             
715 //            cfg.cls='btn roo-button';
716             
717             cfg.href=this.href;
718             
719             var value = cfg.html;
720             
721             if(this.glyphicon){
722                 value = {
723                             tag: 'span',
724                             cls: 'glyphicon glyphicon-' + this.glyphicon,
725                             html: this.html
726                         };
727                 
728             }
729             
730             cfg.cn = [
731                 value,
732                 {
733                     tag: 'span',
734                     cls: 'badge',
735                     html: this.badge
736                 }
737             ];
738             
739             cfg.html='';
740         }
741         
742         if (this.menu) {
743             cfg.cls += ' dropdown';
744             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
745         }
746         
747         if (cfg.tag !== 'a' && this.href !== '') {
748             throw "Tag must be a to set href.";
749         } else if (this.href.length > 0) {
750             cfg.href = this.href;
751         }
752         
753         if(this.removeClass){
754             cfg.cls = '';
755         }
756         
757         if(this.target){
758             cfg.target = this.target;
759         }
760         
761         return cfg;
762     },
763     initEvents: function() {
764        // Roo.log('init events?');
765 //        Roo.log(this.el.dom);
766         // add the menu...
767         
768         if (typeof (this.menu) != 'undefined') {
769             this.menu.parentType = this.xtype;
770             this.menu.triggerEl = this.el;
771             this.addxtype(Roo.apply({}, this.menu));
772         }
773
774
775        if (this.el.hasClass('roo-button')) {
776             this.el.on('click', this.onClick, this);
777        } else {
778             this.el.select('.roo-button').on('click', this.onClick, this);
779        }
780        
781        if(this.removeClass){
782            this.el.on('click', this.onClick, this);
783        }
784        
785        this.el.enableDisplayMode();
786         
787     },
788     onClick : function(e)
789     {
790         if (this.disabled) {
791             return;
792         }
793         
794         
795         Roo.log('button on click ');
796         if(this.preventDefault){
797             e.preventDefault();
798         }
799         if (this.pressed === true || this.pressed === false) {
800             this.pressed = !this.pressed;
801             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
802             this.fireEvent('toggle', this, e, this.pressed);
803         }
804         
805         
806         this.fireEvent('click', this, e);
807     },
808     
809     /**
810      * Enables this button
811      */
812     enable : function()
813     {
814         this.disabled = false;
815         this.el.removeClass('disabled');
816     },
817     
818     /**
819      * Disable this button
820      */
821     disable : function()
822     {
823         this.disabled = true;
824         this.el.addClass('disabled');
825     },
826      /**
827      * sets the active state on/off, 
828      * @param {Boolean} state (optional) Force a particular state
829      */
830     setActive : function(v) {
831         
832         this.el[v ? 'addClass' : 'removeClass']('active');
833     },
834      /**
835      * toggles the current active state 
836      */
837     toggleActive : function()
838     {
839        var active = this.el.hasClass('active');
840        this.setActive(!active);
841        
842         
843     },
844     setText : function(str)
845     {
846         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
847     },
848     getText : function()
849     {
850         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
851     },
852     hide: function() {
853        
854      
855         this.el.hide();   
856     },
857     show: function() {
858        
859         this.el.show();   
860     }
861     
862     
863 });
864
865  /*
866  * - LGPL
867  *
868  * column
869  * 
870  */
871
872 /**
873  * @class Roo.bootstrap.Column
874  * @extends Roo.bootstrap.Component
875  * Bootstrap Column class
876  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
877  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
878  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
879  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
880  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
881  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
882  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
883  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
884  *
885  * 
886  * @cfg {Boolean} hidden (true|false) hide the element
887  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
888  * @cfg {String} fa (ban|check|...) font awesome icon
889  * @cfg {Number} fasize (1|2|....) font awsome size
890
891  * @cfg {String} icon (info-sign|check|...) glyphicon name
892
893  * @cfg {String} html content of column.
894  * 
895  * @constructor
896  * Create a new Column
897  * @param {Object} config The config object
898  */
899
900 Roo.bootstrap.Column = function(config){
901     Roo.bootstrap.Column.superclass.constructor.call(this, config);
902 };
903
904 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
905     
906     xs: false,
907     sm: false,
908     md: false,
909     lg: false,
910     xsoff: false,
911     smoff: false,
912     mdoff: false,
913     lgoff: false,
914     html: '',
915     offset: 0,
916     alert: false,
917     fa: false,
918     icon : false,
919     hidden : false,
920     fasize : 1,
921     
922     getAutoCreate : function(){
923         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
924         
925         cfg = {
926             tag: 'div',
927             cls: 'column'
928         };
929         
930         var settings=this;
931         ['xs','sm','md','lg'].map(function(size){
932             //Roo.log( size + ':' + settings[size]);
933             
934             if (settings[size+'off'] !== false) {
935                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
936             }
937             
938             if (settings[size] === false) {
939                 return;
940             }
941             
942             if (!settings[size]) { // 0 = hidden
943                 cfg.cls += ' hidden-' + size;
944                 return;
945             }
946             cfg.cls += ' col-' + size + '-' + settings[size];
947             
948         });
949         
950         if (this.hidden) {
951             cfg.cls += ' hidden';
952         }
953         
954         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
955             cfg.cls +=' alert alert-' + this.alert;
956         }
957         
958         
959         if (this.html.length) {
960             cfg.html = this.html;
961         }
962         if (this.fa) {
963             var fasize = '';
964             if (this.fasize > 1) {
965                 fasize = ' fa-' + this.fasize + 'x';
966             }
967             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
968             
969             
970         }
971         if (this.icon) {
972             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
973         }
974         
975         return cfg;
976     }
977    
978 });
979
980  
981
982  /*
983  * - LGPL
984  *
985  * page container.
986  * 
987  */
988
989
990 /**
991  * @class Roo.bootstrap.Container
992  * @extends Roo.bootstrap.Component
993  * Bootstrap Container class
994  * @cfg {Boolean} jumbotron is it a jumbotron element
995  * @cfg {String} html content of element
996  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
997  * @cfg {String} panel (primary|success|info|warning|danger) render as panel  - type - primary/success.....
998  * @cfg {String} header content of header (for panel)
999  * @cfg {String} footer content of footer (for panel)
1000  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1001  * @cfg {String} tag (header|aside|section) type of HTML tag.
1002  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1003  * @cfg {String} fa font awesome icon
1004  * @cfg {String} icon (info-sign|check|...) glyphicon name
1005  * @cfg {Boolean} hidden (true|false) hide the element
1006  * @cfg {Boolean} expandable (true|false) default false
1007  * @cfg {Boolean} expanded (true|false) default true
1008  * @cfg {String} rheader contet on the right of header
1009  * @cfg {Boolean} clickable (true|false) default false
1010
1011  *     
1012  * @constructor
1013  * Create a new Container
1014  * @param {Object} config The config object
1015  */
1016
1017 Roo.bootstrap.Container = function(config){
1018     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1019     
1020     this.addEvents({
1021         // raw events
1022          /**
1023          * @event expand
1024          * After the panel has been expand
1025          * 
1026          * @param {Roo.bootstrap.Container} this
1027          */
1028         "expand" : true,
1029         /**
1030          * @event collapse
1031          * After the panel has been collapsed
1032          * 
1033          * @param {Roo.bootstrap.Container} this
1034          */
1035         "collapse" : true,
1036         /**
1037          * @event click
1038          * When a element is chick
1039          * @param {Roo.bootstrap.Container} this
1040          * @param {Roo.EventObject} e
1041          */
1042         "click" : true
1043     });
1044 };
1045
1046 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1047     
1048     jumbotron : false,
1049     well: '',
1050     panel : '',
1051     header: '',
1052     footer : '',
1053     sticky: '',
1054     tag : false,
1055     alert : false,
1056     fa: false,
1057     icon : false,
1058     expandable : false,
1059     rheader : '',
1060     expanded : true,
1061     clickable: false,
1062   
1063      
1064     getChildContainer : function() {
1065         
1066         if(!this.el){
1067             return false;
1068         }
1069         
1070         if (this.panel.length) {
1071             return this.el.select('.panel-body',true).first();
1072         }
1073         
1074         return this.el;
1075     },
1076     
1077     
1078     getAutoCreate : function(){
1079         
1080         var cfg = {
1081             tag : this.tag || 'div',
1082             html : '',
1083             cls : ''
1084         };
1085         if (this.jumbotron) {
1086             cfg.cls = 'jumbotron';
1087         }
1088         
1089         
1090         
1091         // - this is applied by the parent..
1092         //if (this.cls) {
1093         //    cfg.cls = this.cls + '';
1094         //}
1095         
1096         if (this.sticky.length) {
1097             
1098             var bd = Roo.get(document.body);
1099             if (!bd.hasClass('bootstrap-sticky')) {
1100                 bd.addClass('bootstrap-sticky');
1101                 Roo.select('html',true).setStyle('height', '100%');
1102             }
1103              
1104             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1105         }
1106         
1107         
1108         if (this.well.length) {
1109             switch (this.well) {
1110                 case 'lg':
1111                 case 'sm':
1112                     cfg.cls +=' well well-' +this.well;
1113                     break;
1114                 default:
1115                     cfg.cls +=' well';
1116                     break;
1117             }
1118         }
1119         
1120         if (this.hidden) {
1121             cfg.cls += ' hidden';
1122         }
1123         
1124         
1125         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1126             cfg.cls +=' alert alert-' + this.alert;
1127         }
1128         
1129         var body = cfg;
1130         
1131         if (this.panel.length) {
1132             cfg.cls += ' panel panel-' + this.panel;
1133             cfg.cn = [];
1134             if (this.header.length) {
1135                 
1136                 var h = [];
1137                 
1138                 if(this.expandable){
1139                     
1140                     cfg.cls = cfg.cls + ' expandable';
1141                     
1142                     h.push({
1143                         tag: 'i',
1144                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1145                     });
1146                     
1147                 }
1148                 
1149                 h.push(
1150                     {
1151                         tag: 'span',
1152                         cls : 'panel-title',
1153                         html : (this.expandable ? '&nbsp;' : '') + this.header
1154                     },
1155                     {
1156                         tag: 'span',
1157                         cls: 'panel-header-right',
1158                         html: this.rheader
1159                     }
1160                 );
1161                 
1162                 cfg.cn.push({
1163                     cls : 'panel-heading',
1164                     style : this.expandable ? 'cursor: pointer' : '',
1165                     cn : h
1166                 });
1167                 
1168             }
1169             
1170             body = false;
1171             cfg.cn.push({
1172                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1173                 html : this.html
1174             });
1175             
1176             
1177             if (this.footer.length) {
1178                 cfg.cn.push({
1179                     cls : 'panel-footer',
1180                     html : this.footer
1181                     
1182                 });
1183             }
1184             
1185         }
1186         
1187         if (body) {
1188             body.html = this.html || cfg.html;
1189             // prefix with the icons..
1190             if (this.fa) {
1191                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1192             }
1193             if (this.icon) {
1194                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1195             }
1196             
1197             
1198         }
1199         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1200             cfg.cls =  'container';
1201         }
1202         
1203         return cfg;
1204     },
1205     
1206     initEvents: function() 
1207     {
1208         if(this.expandable){
1209             var headerEl = this.headerEl();
1210         
1211             if(headerEl){
1212                 headerEl.on('click', this.onToggleClick, this);
1213             }
1214         }
1215         
1216         if(this.clickable){
1217             this.el.on('click', this.onClick, this);
1218         }
1219         
1220     },
1221     
1222     onToggleClick : function()
1223     {
1224         var headerEl = this.headerEl();
1225         
1226         if(!headerEl){
1227             return;
1228         }
1229         
1230         if(this.expanded){
1231             this.collapse();
1232             return;
1233         }
1234         
1235         this.expand();
1236     },
1237     
1238     expand : function()
1239     {
1240         if(this.fireEvent('expand', this)) {
1241             
1242             this.expanded = true;
1243             
1244             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1245             
1246             this.el.select('.panel-body',true).first().removeClass('hide');
1247             
1248             var toggleEl = this.toggleEl();
1249
1250             if(!toggleEl){
1251                 return;
1252             }
1253
1254             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1255         }
1256         
1257     },
1258     
1259     collapse : function()
1260     {
1261         if(this.fireEvent('collapse', this)) {
1262             
1263             this.expanded = false;
1264             
1265             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1266             this.el.select('.panel-body',true).first().addClass('hide');
1267         
1268             var toggleEl = this.toggleEl();
1269
1270             if(!toggleEl){
1271                 return;
1272             }
1273
1274             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1275         }
1276     },
1277     
1278     toggleEl : function()
1279     {
1280         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1281             return;
1282         }
1283         
1284         return this.el.select('.panel-heading .fa',true).first();
1285     },
1286     
1287     headerEl : function()
1288     {
1289         if(!this.el || !this.panel.length || !this.header.length){
1290             return;
1291         }
1292         
1293         return this.el.select('.panel-heading',true).first()
1294     },
1295     
1296     titleEl : function()
1297     {
1298         if(!this.el || !this.panel.length || !this.header.length){
1299             return;
1300         }
1301         
1302         return this.el.select('.panel-title',true).first();
1303     },
1304     
1305     setTitle : function(v)
1306     {
1307         var titleEl = this.titleEl();
1308         
1309         if(!titleEl){
1310             return;
1311         }
1312         
1313         titleEl.dom.innerHTML = v;
1314     },
1315     
1316     getTitle : function()
1317     {
1318         
1319         var titleEl = this.titleEl();
1320         
1321         if(!titleEl){
1322             return '';
1323         }
1324         
1325         return titleEl.dom.innerHTML;
1326     },
1327     
1328     setRightTitle : function(v)
1329     {
1330         var t = this.el.select('.panel-header-right',true).first();
1331         
1332         if(!t){
1333             return;
1334         }
1335         
1336         t.dom.innerHTML = v;
1337     },
1338     
1339     onClick : function(e)
1340     {
1341         e.preventDefault();
1342         
1343         this.fireEvent('click', this, e);
1344     }
1345    
1346 });
1347
1348  /*
1349  * - LGPL
1350  *
1351  * image
1352  * 
1353  */
1354
1355
1356 /**
1357  * @class Roo.bootstrap.Img
1358  * @extends Roo.bootstrap.Component
1359  * Bootstrap Img class
1360  * @cfg {Boolean} imgResponsive false | true
1361  * @cfg {String} border rounded | circle | thumbnail
1362  * @cfg {String} src image source
1363  * @cfg {String} alt image alternative text
1364  * @cfg {String} href a tag href
1365  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1366  * @cfg {String} xsUrl xs image source
1367  * @cfg {String} smUrl sm image source
1368  * @cfg {String} mdUrl md image source
1369  * @cfg {String} lgUrl lg image source
1370  * 
1371  * @constructor
1372  * Create a new Input
1373  * @param {Object} config The config object
1374  */
1375
1376 Roo.bootstrap.Img = function(config){
1377     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1378     
1379     this.addEvents({
1380         // img events
1381         /**
1382          * @event click
1383          * The img click event for the img.
1384          * @param {Roo.EventObject} e
1385          */
1386         "click" : true
1387     });
1388 };
1389
1390 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1391     
1392     imgResponsive: true,
1393     border: '',
1394     src: 'about:blank',
1395     href: false,
1396     target: false,
1397     xsUrl: '',
1398     smUrl: '',
1399     mdUrl: '',
1400     lgUrl: '',
1401
1402     getAutoCreate : function()
1403     {   
1404         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1405             return this.createSingleImg();
1406         }
1407         
1408         var cfg = {
1409             tag: 'div',
1410             cls: 'roo-image-responsive-group',
1411             cn: []
1412         };
1413         var _this = this;
1414         
1415         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1416             
1417             if(!_this[size + 'Url']){
1418                 return;
1419             }
1420             
1421             var img = {
1422                 tag: 'img',
1423                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1424                 html: _this.html || cfg.html,
1425                 src: _this[size + 'Url']
1426             };
1427             
1428             img.cls += ' roo-image-responsive-' + size;
1429             
1430             var s = ['xs', 'sm', 'md', 'lg'];
1431             
1432             s.splice(s.indexOf(size), 1);
1433             
1434             Roo.each(s, function(ss){
1435                 img.cls += ' hidden-' + ss;
1436             });
1437             
1438             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1439                 cfg.cls += ' img-' + _this.border;
1440             }
1441             
1442             if(_this.alt){
1443                 cfg.alt = _this.alt;
1444             }
1445             
1446             if(_this.href){
1447                 var a = {
1448                     tag: 'a',
1449                     href: _this.href,
1450                     cn: [
1451                         img
1452                     ]
1453                 };
1454
1455                 if(this.target){
1456                     a.target = _this.target;
1457                 }
1458             }
1459             
1460             cfg.cn.push((_this.href) ? a : img);
1461             
1462         });
1463         
1464         return cfg;
1465     },
1466     
1467     createSingleImg : function()
1468     {
1469         var cfg = {
1470             tag: 'img',
1471             cls: (this.imgResponsive) ? 'img-responsive' : '',
1472             html : null,
1473             src : 'about:blank'  // just incase src get's set to undefined?!?
1474         };
1475         
1476         cfg.html = this.html || cfg.html;
1477         
1478         cfg.src = this.src || cfg.src;
1479         
1480         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1481             cfg.cls += ' img-' + this.border;
1482         }
1483         
1484         if(this.alt){
1485             cfg.alt = this.alt;
1486         }
1487         
1488         if(this.href){
1489             var a = {
1490                 tag: 'a',
1491                 href: this.href,
1492                 cn: [
1493                     cfg
1494                 ]
1495             };
1496             
1497             if(this.target){
1498                 a.target = this.target;
1499             }
1500             
1501         }
1502         
1503         return (this.href) ? a : cfg;
1504     },
1505     
1506     initEvents: function() 
1507     {
1508         if(!this.href){
1509             this.el.on('click', this.onClick, this);
1510         }
1511         
1512     },
1513     
1514     onClick : function(e)
1515     {
1516         Roo.log('img onclick');
1517         this.fireEvent('click', this, e);
1518     },
1519     /**
1520      * Sets the url of the image - used to update it
1521      * @param {String} url the url of the image
1522      */
1523     
1524     setSrc : function(url)
1525     {
1526         this.src =  url;
1527         this.el.select('img', true).first().dom.src =  url;
1528     }
1529     
1530     
1531    
1532 });
1533
1534  /*
1535  * - LGPL
1536  *
1537  * image
1538  * 
1539  */
1540
1541
1542 /**
1543  * @class Roo.bootstrap.Link
1544  * @extends Roo.bootstrap.Component
1545  * Bootstrap Link Class
1546  * @cfg {String} alt image alternative text
1547  * @cfg {String} href a tag href
1548  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1549  * @cfg {String} html the content of the link.
1550  * @cfg {String} anchor name for the anchor link
1551  * @cfg {String} fa - favicon
1552
1553  * @cfg {Boolean} preventDefault (true | false) default false
1554
1555  * 
1556  * @constructor
1557  * Create a new Input
1558  * @param {Object} config The config object
1559  */
1560
1561 Roo.bootstrap.Link = function(config){
1562     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1563     
1564     this.addEvents({
1565         // img events
1566         /**
1567          * @event click
1568          * The img click event for the img.
1569          * @param {Roo.EventObject} e
1570          */
1571         "click" : true
1572     });
1573 };
1574
1575 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1576     
1577     href: false,
1578     target: false,
1579     preventDefault: false,
1580     anchor : false,
1581     alt : false,
1582     fa: false,
1583
1584
1585     getAutoCreate : function()
1586     {
1587         var html = this.html || '';
1588         
1589         if (this.fa !== false) {
1590             html = '<i class="fa fa-' + this.fa + '"></i>';
1591         }
1592         var cfg = {
1593             tag: 'a'
1594         };
1595         // anchor's do not require html/href...
1596         if (this.anchor === false) {
1597             cfg.html = html;
1598             cfg.href = this.href || '#';
1599         } else {
1600             cfg.name = this.anchor;
1601             if (this.html !== false || this.fa !== false) {
1602                 cfg.html = html;
1603             }
1604             if (this.href !== false) {
1605                 cfg.href = this.href;
1606             }
1607         }
1608         
1609         if(this.alt !== false){
1610             cfg.alt = this.alt;
1611         }
1612         
1613         
1614         if(this.target !== false) {
1615             cfg.target = this.target;
1616         }
1617         
1618         return cfg;
1619     },
1620     
1621     initEvents: function() {
1622         
1623         if(!this.href || this.preventDefault){
1624             this.el.on('click', this.onClick, this);
1625         }
1626     },
1627     
1628     onClick : function(e)
1629     {
1630         if(this.preventDefault){
1631             e.preventDefault();
1632         }
1633         //Roo.log('img onclick');
1634         this.fireEvent('click', this, e);
1635     }
1636    
1637 });
1638
1639  /*
1640  * - LGPL
1641  *
1642  * header
1643  * 
1644  */
1645
1646 /**
1647  * @class Roo.bootstrap.Header
1648  * @extends Roo.bootstrap.Component
1649  * Bootstrap Header class
1650  * @cfg {String} html content of header
1651  * @cfg {Number} level (1|2|3|4|5|6) default 1
1652  * 
1653  * @constructor
1654  * Create a new Header
1655  * @param {Object} config The config object
1656  */
1657
1658
1659 Roo.bootstrap.Header  = function(config){
1660     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1661 };
1662
1663 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1664     
1665     //href : false,
1666     html : false,
1667     level : 1,
1668     
1669     
1670     
1671     getAutoCreate : function(){
1672         
1673         
1674         
1675         var cfg = {
1676             tag: 'h' + (1 *this.level),
1677             html: this.html || ''
1678         } ;
1679         
1680         return cfg;
1681     }
1682    
1683 });
1684
1685  
1686
1687  /*
1688  * Based on:
1689  * Ext JS Library 1.1.1
1690  * Copyright(c) 2006-2007, Ext JS, LLC.
1691  *
1692  * Originally Released Under LGPL - original licence link has changed is not relivant.
1693  *
1694  * Fork - LGPL
1695  * <script type="text/javascript">
1696  */
1697  
1698 /**
1699  * @class Roo.bootstrap.MenuMgr
1700  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1701  * @singleton
1702  */
1703 Roo.bootstrap.MenuMgr = function(){
1704    var menus, active, groups = {}, attached = false, lastShow = new Date();
1705
1706    // private - called when first menu is created
1707    function init(){
1708        menus = {};
1709        active = new Roo.util.MixedCollection();
1710        Roo.get(document).addKeyListener(27, function(){
1711            if(active.length > 0){
1712                hideAll();
1713            }
1714        });
1715    }
1716
1717    // private
1718    function hideAll(){
1719        if(active && active.length > 0){
1720            var c = active.clone();
1721            c.each(function(m){
1722                m.hide();
1723            });
1724        }
1725    }
1726
1727    // private
1728    function onHide(m){
1729        active.remove(m);
1730        if(active.length < 1){
1731            Roo.get(document).un("mouseup", onMouseDown);
1732             
1733            attached = false;
1734        }
1735    }
1736
1737    // private
1738    function onShow(m){
1739        var last = active.last();
1740        lastShow = new Date();
1741        active.add(m);
1742        if(!attached){
1743           Roo.get(document).on("mouseup", onMouseDown);
1744            
1745            attached = true;
1746        }
1747        if(m.parentMenu){
1748           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1749           m.parentMenu.activeChild = m;
1750        }else if(last && last.isVisible()){
1751           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1752        }
1753    }
1754
1755    // private
1756    function onBeforeHide(m){
1757        if(m.activeChild){
1758            m.activeChild.hide();
1759        }
1760        if(m.autoHideTimer){
1761            clearTimeout(m.autoHideTimer);
1762            delete m.autoHideTimer;
1763        }
1764    }
1765
1766    // private
1767    function onBeforeShow(m){
1768        var pm = m.parentMenu;
1769        if(!pm && !m.allowOtherMenus){
1770            hideAll();
1771        }else if(pm && pm.activeChild && active != m){
1772            pm.activeChild.hide();
1773        }
1774    }
1775
1776    // private this should really trigger on mouseup..
1777    function onMouseDown(e){
1778         Roo.log("on Mouse Up");
1779         
1780         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1781             Roo.log("MenuManager hideAll");
1782             hideAll();
1783             e.stopEvent();
1784         }
1785         
1786         
1787    }
1788
1789    // private
1790    function onBeforeCheck(mi, state){
1791        if(state){
1792            var g = groups[mi.group];
1793            for(var i = 0, l = g.length; i < l; i++){
1794                if(g[i] != mi){
1795                    g[i].setChecked(false);
1796                }
1797            }
1798        }
1799    }
1800
1801    return {
1802
1803        /**
1804         * Hides all menus that are currently visible
1805         */
1806        hideAll : function(){
1807             hideAll();  
1808        },
1809
1810        // private
1811        register : function(menu){
1812            if(!menus){
1813                init();
1814            }
1815            menus[menu.id] = menu;
1816            menu.on("beforehide", onBeforeHide);
1817            menu.on("hide", onHide);
1818            menu.on("beforeshow", onBeforeShow);
1819            menu.on("show", onShow);
1820            var g = menu.group;
1821            if(g && menu.events["checkchange"]){
1822                if(!groups[g]){
1823                    groups[g] = [];
1824                }
1825                groups[g].push(menu);
1826                menu.on("checkchange", onCheck);
1827            }
1828        },
1829
1830         /**
1831          * Returns a {@link Roo.menu.Menu} object
1832          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1833          * be used to generate and return a new Menu instance.
1834          */
1835        get : function(menu){
1836            if(typeof menu == "string"){ // menu id
1837                return menus[menu];
1838            }else if(menu.events){  // menu instance
1839                return menu;
1840            }
1841            /*else if(typeof menu.length == 'number'){ // array of menu items?
1842                return new Roo.bootstrap.Menu({items:menu});
1843            }else{ // otherwise, must be a config
1844                return new Roo.bootstrap.Menu(menu);
1845            }
1846            */
1847            return false;
1848        },
1849
1850        // private
1851        unregister : function(menu){
1852            delete menus[menu.id];
1853            menu.un("beforehide", onBeforeHide);
1854            menu.un("hide", onHide);
1855            menu.un("beforeshow", onBeforeShow);
1856            menu.un("show", onShow);
1857            var g = menu.group;
1858            if(g && menu.events["checkchange"]){
1859                groups[g].remove(menu);
1860                menu.un("checkchange", onCheck);
1861            }
1862        },
1863
1864        // private
1865        registerCheckable : function(menuItem){
1866            var g = menuItem.group;
1867            if(g){
1868                if(!groups[g]){
1869                    groups[g] = [];
1870                }
1871                groups[g].push(menuItem);
1872                menuItem.on("beforecheckchange", onBeforeCheck);
1873            }
1874        },
1875
1876        // private
1877        unregisterCheckable : function(menuItem){
1878            var g = menuItem.group;
1879            if(g){
1880                groups[g].remove(menuItem);
1881                menuItem.un("beforecheckchange", onBeforeCheck);
1882            }
1883        }
1884    };
1885 }();/*
1886  * - LGPL
1887  *
1888  * menu
1889  * 
1890  */
1891
1892 /**
1893  * @class Roo.bootstrap.Menu
1894  * @extends Roo.bootstrap.Component
1895  * Bootstrap Menu class - container for MenuItems
1896  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1897  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1898  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1899  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1900  * 
1901  * @constructor
1902  * Create a new Menu
1903  * @param {Object} config The config object
1904  */
1905
1906
1907 Roo.bootstrap.Menu = function(config){
1908     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1909     if (this.registerMenu && this.type != 'treeview')  {
1910         Roo.bootstrap.MenuMgr.register(this);
1911     }
1912     this.addEvents({
1913         /**
1914          * @event beforeshow
1915          * Fires before this menu is displayed
1916          * @param {Roo.menu.Menu} this
1917          */
1918         beforeshow : true,
1919         /**
1920          * @event beforehide
1921          * Fires before this menu is hidden
1922          * @param {Roo.menu.Menu} this
1923          */
1924         beforehide : true,
1925         /**
1926          * @event show
1927          * Fires after this menu is displayed
1928          * @param {Roo.menu.Menu} this
1929          */
1930         show : true,
1931         /**
1932          * @event hide
1933          * Fires after this menu is hidden
1934          * @param {Roo.menu.Menu} this
1935          */
1936         hide : true,
1937         /**
1938          * @event click
1939          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1940          * @param {Roo.menu.Menu} this
1941          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1942          * @param {Roo.EventObject} e
1943          */
1944         click : true,
1945         /**
1946          * @event mouseover
1947          * Fires when the mouse is hovering over this menu
1948          * @param {Roo.menu.Menu} this
1949          * @param {Roo.EventObject} e
1950          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1951          */
1952         mouseover : true,
1953         /**
1954          * @event mouseout
1955          * Fires when the mouse exits this menu
1956          * @param {Roo.menu.Menu} this
1957          * @param {Roo.EventObject} e
1958          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1959          */
1960         mouseout : true,
1961         /**
1962          * @event itemclick
1963          * Fires when a menu item contained in this menu is clicked
1964          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1965          * @param {Roo.EventObject} e
1966          */
1967         itemclick: true
1968     });
1969     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1970 };
1971
1972 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1973     
1974    /// html : false,
1975     //align : '',
1976     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1977     type: false,
1978     /**
1979      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1980      */
1981     registerMenu : true,
1982     
1983     menuItems :false, // stores the menu items..
1984     
1985     hidden:true,
1986         
1987     parentMenu : false,
1988     
1989     stopEvent : true,
1990     
1991     isLink : false,
1992     
1993     getChildContainer : function() {
1994         return this.el;  
1995     },
1996     
1997     getAutoCreate : function(){
1998          
1999         //if (['right'].indexOf(this.align)!==-1) {
2000         //    cfg.cn[1].cls += ' pull-right'
2001         //}
2002         
2003         
2004         var cfg = {
2005             tag : 'ul',
2006             cls : 'dropdown-menu' ,
2007             style : 'z-index:1000'
2008             
2009         };
2010         
2011         if (this.type === 'submenu') {
2012             cfg.cls = 'submenu active';
2013         }
2014         if (this.type === 'treeview') {
2015             cfg.cls = 'treeview-menu';
2016         }
2017         
2018         return cfg;
2019     },
2020     initEvents : function() {
2021         
2022        // Roo.log("ADD event");
2023        // Roo.log(this.triggerEl.dom);
2024         
2025         this.triggerEl.on('click', this.onTriggerClick, this);
2026         
2027         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2028         
2029         this.triggerEl.addClass('dropdown-toggle');
2030         
2031         if (Roo.isTouch) {
2032             this.el.on('touchstart'  , this.onTouch, this);
2033         }
2034         this.el.on('click' , this.onClick, this);
2035
2036         this.el.on("mouseover", this.onMouseOver, this);
2037         this.el.on("mouseout", this.onMouseOut, this);
2038         
2039     },
2040     
2041     findTargetItem : function(e)
2042     {
2043         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2044         if(!t){
2045             return false;
2046         }
2047         //Roo.log(t);         Roo.log(t.id);
2048         if(t && t.id){
2049             //Roo.log(this.menuitems);
2050             return this.menuitems.get(t.id);
2051             
2052             //return this.items.get(t.menuItemId);
2053         }
2054         
2055         return false;
2056     },
2057     
2058     onTouch : function(e) 
2059     {
2060         Roo.log("menu.onTouch");
2061         //e.stopEvent(); this make the user popdown broken
2062         this.onClick(e);
2063     },
2064     
2065     onClick : function(e)
2066     {
2067         Roo.log("menu.onClick");
2068         
2069         var t = this.findTargetItem(e);
2070         if(!t || t.isContainer){
2071             return;
2072         }
2073         Roo.log(e);
2074         /*
2075         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2076             if(t == this.activeItem && t.shouldDeactivate(e)){
2077                 this.activeItem.deactivate();
2078                 delete this.activeItem;
2079                 return;
2080             }
2081             if(t.canActivate){
2082                 this.setActiveItem(t, true);
2083             }
2084             return;
2085             
2086             
2087         }
2088         */
2089        
2090         Roo.log('pass click event');
2091         
2092         t.onClick(e);
2093         
2094         this.fireEvent("click", this, t, e);
2095         
2096         var _this = this;
2097         
2098         (function() { _this.hide(); }).defer(100);
2099     },
2100     
2101     onMouseOver : function(e){
2102         var t  = this.findTargetItem(e);
2103         //Roo.log(t);
2104         //if(t){
2105         //    if(t.canActivate && !t.disabled){
2106         //        this.setActiveItem(t, true);
2107         //    }
2108         //}
2109         
2110         this.fireEvent("mouseover", this, e, t);
2111     },
2112     isVisible : function(){
2113         return !this.hidden;
2114     },
2115      onMouseOut : function(e){
2116         var t  = this.findTargetItem(e);
2117         
2118         //if(t ){
2119         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2120         //        this.activeItem.deactivate();
2121         //        delete this.activeItem;
2122         //    }
2123         //}
2124         this.fireEvent("mouseout", this, e, t);
2125     },
2126     
2127     
2128     /**
2129      * Displays this menu relative to another element
2130      * @param {String/HTMLElement/Roo.Element} element The element to align to
2131      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2132      * the element (defaults to this.defaultAlign)
2133      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2134      */
2135     show : function(el, pos, parentMenu){
2136         this.parentMenu = parentMenu;
2137         if(!this.el){
2138             this.render();
2139         }
2140         this.fireEvent("beforeshow", this);
2141         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2142     },
2143      /**
2144      * Displays this menu at a specific xy position
2145      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2146      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2147      */
2148     showAt : function(xy, parentMenu, /* private: */_e){
2149         this.parentMenu = parentMenu;
2150         if(!this.el){
2151             this.render();
2152         }
2153         if(_e !== false){
2154             this.fireEvent("beforeshow", this);
2155             //xy = this.el.adjustForConstraints(xy);
2156         }
2157         
2158         //this.el.show();
2159         this.hideMenuItems();
2160         this.hidden = false;
2161         this.triggerEl.addClass('open');
2162         
2163         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2164             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2165         }
2166         
2167         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2168             this.el.setXY(xy);
2169         }
2170         
2171         this.focus();
2172         this.fireEvent("show", this);
2173     },
2174     
2175     focus : function(){
2176         return;
2177         if(!this.hidden){
2178             this.doFocus.defer(50, this);
2179         }
2180     },
2181
2182     doFocus : function(){
2183         if(!this.hidden){
2184             this.focusEl.focus();
2185         }
2186     },
2187
2188     /**
2189      * Hides this menu and optionally all parent menus
2190      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2191      */
2192     hide : function(deep)
2193     {
2194         
2195         this.hideMenuItems();
2196         if(this.el && this.isVisible()){
2197             this.fireEvent("beforehide", this);
2198             if(this.activeItem){
2199                 this.activeItem.deactivate();
2200                 this.activeItem = null;
2201             }
2202             this.triggerEl.removeClass('open');;
2203             this.hidden = true;
2204             this.fireEvent("hide", this);
2205         }
2206         if(deep === true && this.parentMenu){
2207             this.parentMenu.hide(true);
2208         }
2209     },
2210     
2211     onTriggerClick : function(e)
2212     {
2213         Roo.log('trigger click');
2214         
2215         var target = e.getTarget();
2216         
2217         Roo.log(target.nodeName.toLowerCase());
2218         
2219         if(target.nodeName.toLowerCase() === 'i'){
2220             e.preventDefault();
2221         }
2222         
2223     },
2224     
2225     onTriggerPress  : function(e)
2226     {
2227         Roo.log('trigger press');
2228         //Roo.log(e.getTarget());
2229        // Roo.log(this.triggerEl.dom);
2230        
2231         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2232         var pel = Roo.get(e.getTarget());
2233         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2234             Roo.log('is treeview or dropdown?');
2235             return;
2236         }
2237         
2238         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2239             return;
2240         }
2241         
2242         if (this.isVisible()) {
2243             Roo.log('hide');
2244             this.hide();
2245         } else {
2246             Roo.log('show');
2247             this.show(this.triggerEl, false, false);
2248         }
2249         
2250         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2251             e.stopEvent();
2252         }
2253         
2254     },
2255        
2256     
2257     hideMenuItems : function()
2258     {
2259         Roo.log("hide Menu Items");
2260         if (!this.el) { 
2261             return;
2262         }
2263         //$(backdrop).remove()
2264         this.el.select('.open',true).each(function(aa) {
2265             
2266             aa.removeClass('open');
2267           //var parent = getParent($(this))
2268           //var relatedTarget = { relatedTarget: this }
2269           
2270            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2271           //if (e.isDefaultPrevented()) return
2272            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2273         });
2274     },
2275     addxtypeChild : function (tree, cntr) {
2276         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2277           
2278         this.menuitems.add(comp);
2279         return comp;
2280
2281     },
2282     getEl : function()
2283     {
2284         Roo.log(this.el);
2285         return this.el;
2286     }
2287 });
2288
2289  
2290  /*
2291  * - LGPL
2292  *
2293  * menu item
2294  * 
2295  */
2296
2297
2298 /**
2299  * @class Roo.bootstrap.MenuItem
2300  * @extends Roo.bootstrap.Component
2301  * Bootstrap MenuItem class
2302  * @cfg {String} html the menu label
2303  * @cfg {String} href the link
2304  * @cfg {Boolean} preventDefault do not trigger A href on clicks.
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: true,
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         this.maskEl.hide();
2772         Roo.get(document.body).removeClass("x-body-masked");
2773         this.el.removeClass('in');
2774         this.el.select('.modal-dialog', true).first().setStyle('transform','');
2775         
2776         if(this.animate){ // why
2777             var _this = this;
2778             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2779         }else{
2780             this.el.setStyle('display', 'none');
2781         }
2782         
2783         this.fireEvent('hide', this);
2784     },
2785     
2786     addButton : function(str, cb)
2787     {
2788          
2789         
2790         var b = Roo.apply({}, { html : str } );
2791         b.xns = b.xns || Roo.bootstrap;
2792         b.xtype = b.xtype || 'Button';
2793         if (typeof(b.listeners) == 'undefined') {
2794             b.listeners = { click : cb.createDelegate(this)  };
2795         }
2796         
2797         var btn = Roo.factory(b);
2798            
2799         btn.render(this.el.select('.modal-footer div').first());
2800         
2801         return btn;   
2802        
2803     },
2804     
2805     setDefaultButton : function(btn)
2806     {
2807         //this.el.select('.modal-footer').()
2808     },
2809     diff : false,
2810     
2811     resizeTo: function(w,h)
2812     {
2813         // skip.. ?? why??
2814         
2815         this.dialogEl.setWidth(w);
2816         if (this.diff === false) {
2817             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2818         }
2819         
2820         this.bodyEl.setHeight(h-this.diff);
2821         
2822         
2823     },
2824     setContentSize  : function(w, h)
2825     {
2826         
2827     },
2828     onButtonClick: function(btn,e)
2829     {
2830         //Roo.log([a,b,c]);
2831         this.fireEvent('btnclick', btn.name, e);
2832     },
2833      /**
2834      * Set the title of the Dialog
2835      * @param {String} str new Title
2836      */
2837     setTitle: function(str) {
2838         this.titleEl.dom.innerHTML = str;    
2839     },
2840     /**
2841      * Set the body of the Dialog
2842      * @param {String} str new Title
2843      */
2844     setBody: function(str) {
2845         this.bodyEl.dom.innerHTML = str;    
2846     },
2847     /**
2848      * Set the body of the Dialog using the template
2849      * @param {Obj} data - apply this data to the template and replace the body contents.
2850      */
2851     applyBody: function(obj)
2852     {
2853         if (!this.tmpl) {
2854             Roo.log("Error - using apply Body without a template");
2855             //code
2856         }
2857         this.tmpl.overwrite(this.bodyEl, obj);
2858     }
2859     
2860 });
2861
2862
2863 Roo.apply(Roo.bootstrap.Modal,  {
2864     /**
2865          * Button config that displays a single OK button
2866          * @type Object
2867          */
2868         OK :  [{
2869             name : 'ok',
2870             weight : 'primary',
2871             html : 'OK'
2872         }], 
2873         /**
2874          * Button config that displays Yes and No buttons
2875          * @type Object
2876          */
2877         YESNO : [
2878             {
2879                 name  : 'no',
2880                 html : 'No'
2881             },
2882             {
2883                 name  :'yes',
2884                 weight : 'primary',
2885                 html : 'Yes'
2886             }
2887         ],
2888         
2889         /**
2890          * Button config that displays OK and Cancel buttons
2891          * @type Object
2892          */
2893         OKCANCEL : [
2894             {
2895                name : 'cancel',
2896                 html : 'Cancel'
2897             },
2898             {
2899                 name : 'ok',
2900                 weight : 'primary',
2901                 html : 'OK'
2902             }
2903         ],
2904         /**
2905          * Button config that displays Yes, No and Cancel buttons
2906          * @type Object
2907          */
2908         YESNOCANCEL : [
2909             {
2910                 name : 'yes',
2911                 weight : 'primary',
2912                 html : 'Yes'
2913             },
2914             {
2915                 name : 'no',
2916                 html : 'No'
2917             },
2918             {
2919                 name : 'cancel',
2920                 html : 'Cancel'
2921             }
2922         ]
2923 });
2924  
2925  /*
2926  * - LGPL
2927  *
2928  * messagebox - can be used as a replace
2929  * 
2930  */
2931 /**
2932  * @class Roo.MessageBox
2933  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2934  * Example usage:
2935  *<pre><code>
2936 // Basic alert:
2937 Roo.Msg.alert('Status', 'Changes saved successfully.');
2938
2939 // Prompt for user data:
2940 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2941     if (btn == 'ok'){
2942         // process text value...
2943     }
2944 });
2945
2946 // Show a dialog using config options:
2947 Roo.Msg.show({
2948    title:'Save Changes?',
2949    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2950    buttons: Roo.Msg.YESNOCANCEL,
2951    fn: processResult,
2952    animEl: 'elId'
2953 });
2954 </code></pre>
2955  * @singleton
2956  */
2957 Roo.bootstrap.MessageBox = function(){
2958     var dlg, opt, mask, waitTimer;
2959     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2960     var buttons, activeTextEl, bwidth;
2961
2962     
2963     // private
2964     var handleButton = function(button){
2965         dlg.hide();
2966         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2967     };
2968
2969     // private
2970     var handleHide = function(){
2971         if(opt && opt.cls){
2972             dlg.el.removeClass(opt.cls);
2973         }
2974         //if(waitTimer){
2975         //    Roo.TaskMgr.stop(waitTimer);
2976         //    waitTimer = null;
2977         //}
2978     };
2979
2980     // private
2981     var updateButtons = function(b){
2982         var width = 0;
2983         if(!b){
2984             buttons["ok"].hide();
2985             buttons["cancel"].hide();
2986             buttons["yes"].hide();
2987             buttons["no"].hide();
2988             //dlg.footer.dom.style.display = 'none';
2989             return width;
2990         }
2991         dlg.footerEl.dom.style.display = '';
2992         for(var k in buttons){
2993             if(typeof buttons[k] != "function"){
2994                 if(b[k]){
2995                     buttons[k].show();
2996                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2997                     width += buttons[k].el.getWidth()+15;
2998                 }else{
2999                     buttons[k].hide();
3000                 }
3001             }
3002         }
3003         return width;
3004     };
3005
3006     // private
3007     var handleEsc = function(d, k, e){
3008         if(opt && opt.closable !== false){
3009             dlg.hide();
3010         }
3011         if(e){
3012             e.stopEvent();
3013         }
3014     };
3015
3016     return {
3017         /**
3018          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3019          * @return {Roo.BasicDialog} The BasicDialog element
3020          */
3021         getDialog : function(){
3022            if(!dlg){
3023                 dlg = new Roo.bootstrap.Modal( {
3024                     //draggable: true,
3025                     //resizable:false,
3026                     //constraintoviewport:false,
3027                     //fixedcenter:true,
3028                     //collapsible : false,
3029                     //shim:true,
3030                     //modal: true,
3031                   //  width:400,
3032                   //  height:100,
3033                     //buttonAlign:"center",
3034                     closeClick : function(){
3035                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3036                             handleButton("no");
3037                         }else{
3038                             handleButton("cancel");
3039                         }
3040                     }
3041                 });
3042                 dlg.render();
3043                 dlg.on("hide", handleHide);
3044                 mask = dlg.mask;
3045                 //dlg.addKeyListener(27, handleEsc);
3046                 buttons = {};
3047                 this.buttons = buttons;
3048                 var bt = this.buttonText;
3049                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3050                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3051                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3052                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3053                 //Roo.log(buttons);
3054                 bodyEl = dlg.bodyEl.createChild({
3055
3056                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3057                         '<textarea class="roo-mb-textarea"></textarea>' +
3058                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3059                 });
3060                 msgEl = bodyEl.dom.firstChild;
3061                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3062                 textboxEl.enableDisplayMode();
3063                 textboxEl.addKeyListener([10,13], function(){
3064                     if(dlg.isVisible() && opt && opt.buttons){
3065                         if(opt.buttons.ok){
3066                             handleButton("ok");
3067                         }else if(opt.buttons.yes){
3068                             handleButton("yes");
3069                         }
3070                     }
3071                 });
3072                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3073                 textareaEl.enableDisplayMode();
3074                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3075                 progressEl.enableDisplayMode();
3076                 var pf = progressEl.dom.firstChild;
3077                 if (pf) {
3078                     pp = Roo.get(pf.firstChild);
3079                     pp.setHeight(pf.offsetHeight);
3080                 }
3081                 
3082             }
3083             return dlg;
3084         },
3085
3086         /**
3087          * Updates the message box body text
3088          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3089          * the XHTML-compliant non-breaking space character '&amp;#160;')
3090          * @return {Roo.MessageBox} This message box
3091          */
3092         updateText : function(text){
3093             if(!dlg.isVisible() && !opt.width){
3094                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
3095             }
3096             msgEl.innerHTML = text || '&#160;';
3097       
3098             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3099             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3100             var w = Math.max(
3101                     Math.min(opt.width || cw , this.maxWidth), 
3102                     Math.max(opt.minWidth || this.minWidth, bwidth)
3103             );
3104             if(opt.prompt){
3105                 activeTextEl.setWidth(w);
3106             }
3107             if(dlg.isVisible()){
3108                 dlg.fixedcenter = false;
3109             }
3110             // to big, make it scroll. = But as usual stupid IE does not support
3111             // !important..
3112             
3113             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3114                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3115                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3116             } else {
3117                 bodyEl.dom.style.height = '';
3118                 bodyEl.dom.style.overflowY = '';
3119             }
3120             if (cw > w) {
3121                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3122             } else {
3123                 bodyEl.dom.style.overflowX = '';
3124             }
3125             
3126             dlg.setContentSize(w, bodyEl.getHeight());
3127             if(dlg.isVisible()){
3128                 dlg.fixedcenter = true;
3129             }
3130             return this;
3131         },
3132
3133         /**
3134          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3135          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3136          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3137          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3138          * @return {Roo.MessageBox} This message box
3139          */
3140         updateProgress : function(value, text){
3141             if(text){
3142                 this.updateText(text);
3143             }
3144             if (pp) { // weird bug on my firefox - for some reason this is not defined
3145                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3146             }
3147             return this;
3148         },        
3149
3150         /**
3151          * Returns true if the message box is currently displayed
3152          * @return {Boolean} True if the message box is visible, else false
3153          */
3154         isVisible : function(){
3155             return dlg && dlg.isVisible();  
3156         },
3157
3158         /**
3159          * Hides the message box if it is displayed
3160          */
3161         hide : function(){
3162             if(this.isVisible()){
3163                 dlg.hide();
3164             }  
3165         },
3166
3167         /**
3168          * Displays a new message box, or reinitializes an existing message box, based on the config options
3169          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3170          * The following config object properties are supported:
3171          * <pre>
3172 Property    Type             Description
3173 ----------  ---------------  ------------------------------------------------------------------------------------
3174 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3175                                    closes (defaults to undefined)
3176 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3177                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3178 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3179                                    progress and wait dialogs will ignore this property and always hide the
3180                                    close button as they can only be closed programmatically.
3181 cls               String           A custom CSS class to apply to the message box element
3182 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3183                                    displayed (defaults to 75)
3184 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3185                                    function will be btn (the name of the button that was clicked, if applicable,
3186                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3187                                    Progress and wait dialogs will ignore this option since they do not respond to
3188                                    user actions and can only be closed programmatically, so any required function
3189                                    should be called by the same code after it closes the dialog.
3190 icon              String           A CSS class that provides a background image to be used as an icon for
3191                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3192 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3193 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3194 modal             Boolean          False to allow user interaction with the page while the message box is
3195                                    displayed (defaults to true)
3196 msg               String           A string that will replace the existing message box body text (defaults
3197                                    to the XHTML-compliant non-breaking space character '&#160;')
3198 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3199 progress          Boolean          True to display a progress bar (defaults to false)
3200 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3201 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3202 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3203 title             String           The title text
3204 value             String           The string value to set into the active textbox element if displayed
3205 wait              Boolean          True to display a progress bar (defaults to false)
3206 width             Number           The width of the dialog in pixels
3207 </pre>
3208          *
3209          * Example usage:
3210          * <pre><code>
3211 Roo.Msg.show({
3212    title: 'Address',
3213    msg: 'Please enter your address:',
3214    width: 300,
3215    buttons: Roo.MessageBox.OKCANCEL,
3216    multiline: true,
3217    fn: saveAddress,
3218    animEl: 'addAddressBtn'
3219 });
3220 </code></pre>
3221          * @param {Object} config Configuration options
3222          * @return {Roo.MessageBox} This message box
3223          */
3224         show : function(options)
3225         {
3226             
3227             // this causes nightmares if you show one dialog after another
3228             // especially on callbacks..
3229              
3230             if(this.isVisible()){
3231                 
3232                 this.hide();
3233                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3234                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3235                 Roo.log("New Dialog Message:" +  options.msg )
3236                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3237                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3238                 
3239             }
3240             var d = this.getDialog();
3241             opt = options;
3242             d.setTitle(opt.title || "&#160;");
3243             d.closeEl.setDisplayed(opt.closable !== false);
3244             activeTextEl = textboxEl;
3245             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3246             if(opt.prompt){
3247                 if(opt.multiline){
3248                     textboxEl.hide();
3249                     textareaEl.show();
3250                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3251                         opt.multiline : this.defaultTextHeight);
3252                     activeTextEl = textareaEl;
3253                 }else{
3254                     textboxEl.show();
3255                     textareaEl.hide();
3256                 }
3257             }else{
3258                 textboxEl.hide();
3259                 textareaEl.hide();
3260             }
3261             progressEl.setDisplayed(opt.progress === true);
3262             this.updateProgress(0);
3263             activeTextEl.dom.value = opt.value || "";
3264             if(opt.prompt){
3265                 dlg.setDefaultButton(activeTextEl);
3266             }else{
3267                 var bs = opt.buttons;
3268                 var db = null;
3269                 if(bs && bs.ok){
3270                     db = buttons["ok"];
3271                 }else if(bs && bs.yes){
3272                     db = buttons["yes"];
3273                 }
3274                 dlg.setDefaultButton(db);
3275             }
3276             bwidth = updateButtons(opt.buttons);
3277             this.updateText(opt.msg);
3278             if(opt.cls){
3279                 d.el.addClass(opt.cls);
3280             }
3281             d.proxyDrag = opt.proxyDrag === true;
3282             d.modal = opt.modal !== false;
3283             d.mask = opt.modal !== false ? mask : false;
3284             if(!d.isVisible()){
3285                 // force it to the end of the z-index stack so it gets a cursor in FF
3286                 document.body.appendChild(dlg.el.dom);
3287                 d.animateTarget = null;
3288                 d.show(options.animEl);
3289             }
3290             return this;
3291         },
3292
3293         /**
3294          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3295          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3296          * and closing the message box when the process is complete.
3297          * @param {String} title The title bar text
3298          * @param {String} msg The message box body text
3299          * @return {Roo.MessageBox} This message box
3300          */
3301         progress : function(title, msg){
3302             this.show({
3303                 title : title,
3304                 msg : msg,
3305                 buttons: false,
3306                 progress:true,
3307                 closable:false,
3308                 minWidth: this.minProgressWidth,
3309                 modal : true
3310             });
3311             return this;
3312         },
3313
3314         /**
3315          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3316          * If a callback function is passed it will be called after the user clicks the button, and the
3317          * id of the button that was clicked will be passed as the only parameter to the callback
3318          * (could also be the top-right close button).
3319          * @param {String} title The title bar text
3320          * @param {String} msg The message box body text
3321          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3322          * @param {Object} scope (optional) The scope of the callback function
3323          * @return {Roo.MessageBox} This message box
3324          */
3325         alert : function(title, msg, fn, scope){
3326             this.show({
3327                 title : title,
3328                 msg : msg,
3329                 buttons: this.OK,
3330                 fn: fn,
3331                 scope : scope,
3332                 modal : true
3333             });
3334             return this;
3335         },
3336
3337         /**
3338          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3339          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3340          * You are responsible for closing the message box when the process is complete.
3341          * @param {String} msg The message box body text
3342          * @param {String} title (optional) The title bar text
3343          * @return {Roo.MessageBox} This message box
3344          */
3345         wait : function(msg, title){
3346             this.show({
3347                 title : title,
3348                 msg : msg,
3349                 buttons: false,
3350                 closable:false,
3351                 progress:true,
3352                 modal:true,
3353                 width:300,
3354                 wait:true
3355             });
3356             waitTimer = Roo.TaskMgr.start({
3357                 run: function(i){
3358                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3359                 },
3360                 interval: 1000
3361             });
3362             return this;
3363         },
3364
3365         /**
3366          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3367          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3368          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3369          * @param {String} title The title bar text
3370          * @param {String} msg The message box body text
3371          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3372          * @param {Object} scope (optional) The scope of the callback function
3373          * @return {Roo.MessageBox} This message box
3374          */
3375         confirm : function(title, msg, fn, scope){
3376             this.show({
3377                 title : title,
3378                 msg : msg,
3379                 buttons: this.YESNO,
3380                 fn: fn,
3381                 scope : scope,
3382                 modal : true
3383             });
3384             return this;
3385         },
3386
3387         /**
3388          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3389          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3390          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3391          * (could also be the top-right close button) and the text that was entered will be passed as the two
3392          * parameters to the callback.
3393          * @param {String} title The title bar text
3394          * @param {String} msg The message box body text
3395          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3396          * @param {Object} scope (optional) The scope of the callback function
3397          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3398          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3399          * @return {Roo.MessageBox} This message box
3400          */
3401         prompt : function(title, msg, fn, scope, multiline){
3402             this.show({
3403                 title : title,
3404                 msg : msg,
3405                 buttons: this.OKCANCEL,
3406                 fn: fn,
3407                 minWidth:250,
3408                 scope : scope,
3409                 prompt:true,
3410                 multiline: multiline,
3411                 modal : true
3412             });
3413             return this;
3414         },
3415
3416         /**
3417          * Button config that displays a single OK button
3418          * @type Object
3419          */
3420         OK : {ok:true},
3421         /**
3422          * Button config that displays Yes and No buttons
3423          * @type Object
3424          */
3425         YESNO : {yes:true, no:true},
3426         /**
3427          * Button config that displays OK and Cancel buttons
3428          * @type Object
3429          */
3430         OKCANCEL : {ok:true, cancel:true},
3431         /**
3432          * Button config that displays Yes, No and Cancel buttons
3433          * @type Object
3434          */
3435         YESNOCANCEL : {yes:true, no:true, cancel:true},
3436
3437         /**
3438          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3439          * @type Number
3440          */
3441         defaultTextHeight : 75,
3442         /**
3443          * The maximum width in pixels of the message box (defaults to 600)
3444          * @type Number
3445          */
3446         maxWidth : 600,
3447         /**
3448          * The minimum width in pixels of the message box (defaults to 100)
3449          * @type Number
3450          */
3451         minWidth : 100,
3452         /**
3453          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3454          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3455          * @type Number
3456          */
3457         minProgressWidth : 250,
3458         /**
3459          * An object containing the default button text strings that can be overriden for localized language support.
3460          * Supported properties are: ok, cancel, yes and no.
3461          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3462          * @type Object
3463          */
3464         buttonText : {
3465             ok : "OK",
3466             cancel : "Cancel",
3467             yes : "Yes",
3468             no : "No"
3469         }
3470     };
3471 }();
3472
3473 /**
3474  * Shorthand for {@link Roo.MessageBox}
3475  */
3476 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3477 Roo.Msg = Roo.Msg || Roo.MessageBox;
3478 /*
3479  * - LGPL
3480  *
3481  * navbar
3482  * 
3483  */
3484
3485 /**
3486  * @class Roo.bootstrap.Navbar
3487  * @extends Roo.bootstrap.Component
3488  * Bootstrap Navbar class
3489
3490  * @constructor
3491  * Create a new Navbar
3492  * @param {Object} config The config object
3493  */
3494
3495
3496 Roo.bootstrap.Navbar = function(config){
3497     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3498     this.addEvents({
3499         // raw events
3500         /**
3501          * @event beforetoggle
3502          * Fire before toggle the menu
3503          * @param {Roo.EventObject} e
3504          */
3505         "beforetoggle" : true
3506     });
3507 };
3508
3509 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3510     
3511     
3512    
3513     // private
3514     navItems : false,
3515     loadMask : false,
3516     
3517     
3518     getAutoCreate : function(){
3519         
3520         
3521         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3522         
3523     },
3524     
3525     initEvents :function ()
3526     {
3527         //Roo.log(this.el.select('.navbar-toggle',true));
3528         this.el.select('.navbar-toggle',true).on('click', function() {
3529             if(this.fireEvent('beforetoggle', this) !== false){
3530                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3531             }
3532             
3533         }, this);
3534         
3535         var mark = {
3536             tag: "div",
3537             cls:"x-dlg-mask"
3538         };
3539         
3540         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3541         
3542         var size = this.el.getSize();
3543         this.maskEl.setSize(size.width, size.height);
3544         this.maskEl.enableDisplayMode("block");
3545         this.maskEl.hide();
3546         
3547         if(this.loadMask){
3548             this.maskEl.show();
3549         }
3550     },
3551     
3552     
3553     getChildContainer : function()
3554     {
3555         if (this.el.select('.collapse').getCount()) {
3556             return this.el.select('.collapse',true).first();
3557         }
3558         
3559         return this.el;
3560     },
3561     
3562     mask : function()
3563     {
3564         this.maskEl.show();
3565     },
3566     
3567     unmask : function()
3568     {
3569         this.maskEl.hide();
3570     } 
3571     
3572     
3573     
3574     
3575 });
3576
3577
3578
3579  
3580
3581  /*
3582  * - LGPL
3583  *
3584  * navbar
3585  * 
3586  */
3587
3588 /**
3589  * @class Roo.bootstrap.NavSimplebar
3590  * @extends Roo.bootstrap.Navbar
3591  * Bootstrap Sidebar class
3592  *
3593  * @cfg {Boolean} inverse is inverted color
3594  * 
3595  * @cfg {String} type (nav | pills | tabs)
3596  * @cfg {Boolean} arrangement stacked | justified
3597  * @cfg {String} align (left | right) alignment
3598  * 
3599  * @cfg {Boolean} main (true|false) main nav bar? default false
3600  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3601  * 
3602  * @cfg {String} tag (header|footer|nav|div) default is nav 
3603
3604  * 
3605  * 
3606  * 
3607  * @constructor
3608  * Create a new Sidebar
3609  * @param {Object} config The config object
3610  */
3611
3612
3613 Roo.bootstrap.NavSimplebar = function(config){
3614     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3615 };
3616
3617 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3618     
3619     inverse: false,
3620     
3621     type: false,
3622     arrangement: '',
3623     align : false,
3624     
3625     
3626     
3627     main : false,
3628     
3629     
3630     tag : false,
3631     
3632     
3633     getAutoCreate : function(){
3634         
3635         
3636         var cfg = {
3637             tag : this.tag || 'div',
3638             cls : 'navbar'
3639         };
3640           
3641         
3642         cfg.cn = [
3643             {
3644                 cls: 'nav',
3645                 tag : 'ul'
3646             }
3647         ];
3648         
3649          
3650         this.type = this.type || 'nav';
3651         if (['tabs','pills'].indexOf(this.type)!==-1) {
3652             cfg.cn[0].cls += ' nav-' + this.type
3653         
3654         
3655         } else {
3656             if (this.type!=='nav') {
3657                 Roo.log('nav type must be nav/tabs/pills')
3658             }
3659             cfg.cn[0].cls += ' navbar-nav'
3660         }
3661         
3662         
3663         
3664         
3665         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3666             cfg.cn[0].cls += ' nav-' + this.arrangement;
3667         }
3668         
3669         
3670         if (this.align === 'right') {
3671             cfg.cn[0].cls += ' navbar-right';
3672         }
3673         
3674         if (this.inverse) {
3675             cfg.cls += ' navbar-inverse';
3676             
3677         }
3678         
3679         
3680         return cfg;
3681     
3682         
3683     }
3684     
3685     
3686     
3687 });
3688
3689
3690
3691  
3692
3693  
3694        /*
3695  * - LGPL
3696  *
3697  * navbar
3698  * 
3699  */
3700
3701 /**
3702  * @class Roo.bootstrap.NavHeaderbar
3703  * @extends Roo.bootstrap.NavSimplebar
3704  * Bootstrap Sidebar class
3705  *
3706  * @cfg {String} brand what is brand
3707  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3708  * @cfg {String} brand_href href of the brand
3709  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3710  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3711  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3712  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3713  * 
3714  * @constructor
3715  * Create a new Sidebar
3716  * @param {Object} config The config object
3717  */
3718
3719
3720 Roo.bootstrap.NavHeaderbar = function(config){
3721     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3722       
3723 };
3724
3725 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3726     
3727     position: '',
3728     brand: '',
3729     brand_href: false,
3730     srButton : true,
3731     autohide : false,
3732     desktopCenter : false,
3733    
3734     
3735     getAutoCreate : function(){
3736         
3737         var   cfg = {
3738             tag: this.nav || 'nav',
3739             cls: 'navbar',
3740             role: 'navigation',
3741             cn: []
3742         };
3743         
3744         var cn = cfg.cn;
3745         if (this.desktopCenter) {
3746             cn.push({cls : 'container', cn : []});
3747             cn = cn[0].cn;
3748         }
3749         
3750         if(this.srButton){
3751             cn.push({
3752                 tag: 'div',
3753                 cls: 'navbar-header',
3754                 cn: [
3755                     {
3756                         tag: 'button',
3757                         type: 'button',
3758                         cls: 'navbar-toggle',
3759                         'data-toggle': 'collapse',
3760                         cn: [
3761                             {
3762                                 tag: 'span',
3763                                 cls: 'sr-only',
3764                                 html: 'Toggle navigation'
3765                             },
3766                             {
3767                                 tag: 'span',
3768                                 cls: 'icon-bar'
3769                             },
3770                             {
3771                                 tag: 'span',
3772                                 cls: 'icon-bar'
3773                             },
3774                             {
3775                                 tag: 'span',
3776                                 cls: 'icon-bar'
3777                             }
3778                         ]
3779                     }
3780                 ]
3781             });
3782         }
3783         
3784         cn.push({
3785             tag: 'div',
3786             cls: 'collapse navbar-collapse',
3787             cn : []
3788         });
3789         
3790         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3791         
3792         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3793             cfg.cls += ' navbar-' + this.position;
3794             
3795             // tag can override this..
3796             
3797             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3798         }
3799         
3800         if (this.brand !== '') {
3801             cn[0].cn.push({
3802                 tag: 'a',
3803                 href: this.brand_href ? this.brand_href : '#',
3804                 cls: 'navbar-brand',
3805                 cn: [
3806                 this.brand
3807                 ]
3808             });
3809         }
3810         
3811         if(this.main){
3812             cfg.cls += ' main-nav';
3813         }
3814         
3815         
3816         return cfg;
3817
3818         
3819     },
3820     getHeaderChildContainer : function()
3821     {
3822         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3823             return this.el.select('.navbar-header',true).first();
3824         }
3825         
3826         return this.getChildContainer();
3827     },
3828     
3829     
3830     initEvents : function()
3831     {
3832         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3833         
3834         if (this.autohide) {
3835             
3836             var prevScroll = 0;
3837             var ft = this.el;
3838             
3839             Roo.get(document).on('scroll',function(e) {
3840                 var ns = Roo.get(document).getScroll().top;
3841                 var os = prevScroll;
3842                 prevScroll = ns;
3843                 
3844                 if(ns > os){
3845                     ft.removeClass('slideDown');
3846                     ft.addClass('slideUp');
3847                     return;
3848                 }
3849                 ft.removeClass('slideUp');
3850                 ft.addClass('slideDown');
3851                  
3852               
3853           },this);
3854         }
3855     }    
3856     
3857 });
3858
3859
3860
3861  
3862
3863  /*
3864  * - LGPL
3865  *
3866  * navbar
3867  * 
3868  */
3869
3870 /**
3871  * @class Roo.bootstrap.NavSidebar
3872  * @extends Roo.bootstrap.Navbar
3873  * Bootstrap Sidebar class
3874  * 
3875  * @constructor
3876  * Create a new Sidebar
3877  * @param {Object} config The config object
3878  */
3879
3880
3881 Roo.bootstrap.NavSidebar = function(config){
3882     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3883 };
3884
3885 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3886     
3887     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3888     
3889     getAutoCreate : function(){
3890         
3891         
3892         return  {
3893             tag: 'div',
3894             cls: 'sidebar sidebar-nav'
3895         };
3896     
3897         
3898     }
3899     
3900     
3901     
3902 });
3903
3904
3905
3906  
3907
3908  /*
3909  * - LGPL
3910  *
3911  * nav group
3912  * 
3913  */
3914
3915 /**
3916  * @class Roo.bootstrap.NavGroup
3917  * @extends Roo.bootstrap.Component
3918  * Bootstrap NavGroup class
3919  * @cfg {String} align (left|right)
3920  * @cfg {Boolean} inverse
3921  * @cfg {String} type (nav|pills|tab) default nav
3922  * @cfg {String} navId - reference Id for navbar.
3923
3924  * 
3925  * @constructor
3926  * Create a new nav group
3927  * @param {Object} config The config object
3928  */
3929
3930 Roo.bootstrap.NavGroup = function(config){
3931     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3932     this.navItems = [];
3933    
3934     Roo.bootstrap.NavGroup.register(this);
3935      this.addEvents({
3936         /**
3937              * @event changed
3938              * Fires when the active item changes
3939              * @param {Roo.bootstrap.NavGroup} this
3940              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3941              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3942          */
3943         'changed': true
3944      });
3945     
3946 };
3947
3948 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3949     
3950     align: '',
3951     inverse: false,
3952     form: false,
3953     type: 'nav',
3954     navId : '',
3955     // private
3956     
3957     navItems : false, 
3958     
3959     getAutoCreate : function()
3960     {
3961         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3962         
3963         cfg = {
3964             tag : 'ul',
3965             cls: 'nav' 
3966         };
3967         
3968         if (['tabs','pills'].indexOf(this.type)!==-1) {
3969             cfg.cls += ' nav-' + this.type
3970         } else {
3971             if (this.type!=='nav') {
3972                 Roo.log('nav type must be nav/tabs/pills')
3973             }
3974             cfg.cls += ' navbar-nav'
3975         }
3976         
3977         if (this.parent().sidebar) {
3978             cfg = {
3979                 tag: 'ul',
3980                 cls: 'dashboard-menu sidebar-menu'
3981             };
3982             
3983             return cfg;
3984         }
3985         
3986         if (this.form === true) {
3987             cfg = {
3988                 tag: 'form',
3989                 cls: 'navbar-form'
3990             };
3991             
3992             if (this.align === 'right') {
3993                 cfg.cls += ' navbar-right';
3994             } else {
3995                 cfg.cls += ' navbar-left';
3996             }
3997         }
3998         
3999         if (this.align === 'right') {
4000             cfg.cls += ' navbar-right';
4001         }
4002         
4003         if (this.inverse) {
4004             cfg.cls += ' navbar-inverse';
4005             
4006         }
4007         
4008         
4009         return cfg;
4010     },
4011     /**
4012     * sets the active Navigation item
4013     * @param {Roo.bootstrap.NavItem} the new current navitem
4014     */
4015     setActiveItem : function(item)
4016     {
4017         var prev = false;
4018         Roo.each(this.navItems, function(v){
4019             if (v == item) {
4020                 return ;
4021             }
4022             if (v.isActive()) {
4023                 v.setActive(false, true);
4024                 prev = v;
4025                 
4026             }
4027             
4028         });
4029
4030         item.setActive(true, true);
4031         this.fireEvent('changed', this, item, prev);
4032         
4033         
4034     },
4035     /**
4036     * gets the active Navigation item
4037     * @return {Roo.bootstrap.NavItem} the current navitem
4038     */
4039     getActive : function()
4040     {
4041         
4042         var prev = false;
4043         Roo.each(this.navItems, function(v){
4044             
4045             if (v.isActive()) {
4046                 prev = v;
4047                 
4048             }
4049             
4050         });
4051         return prev;
4052     },
4053     
4054     indexOfNav : function()
4055     {
4056         
4057         var prev = false;
4058         Roo.each(this.navItems, function(v,i){
4059             
4060             if (v.isActive()) {
4061                 prev = i;
4062                 
4063             }
4064             
4065         });
4066         return prev;
4067     },
4068     /**
4069     * adds a Navigation item
4070     * @param {Roo.bootstrap.NavItem} the navitem to add
4071     */
4072     addItem : function(cfg)
4073     {
4074         var cn = new Roo.bootstrap.NavItem(cfg);
4075         this.register(cn);
4076         cn.parentId = this.id;
4077         cn.onRender(this.el, null);
4078         return cn;
4079     },
4080     /**
4081     * register a Navigation item
4082     * @param {Roo.bootstrap.NavItem} the navitem to add
4083     */
4084     register : function(item)
4085     {
4086         this.navItems.push( item);
4087         item.navId = this.navId;
4088     
4089     },
4090     
4091     /**
4092     * clear all the Navigation item
4093     */
4094    
4095     clearAll : function()
4096     {
4097         this.navItems = [];
4098         this.el.dom.innerHTML = '';
4099     },
4100     
4101     getNavItem: function(tabId)
4102     {
4103         var ret = false;
4104         Roo.each(this.navItems, function(e) {
4105             if (e.tabId == tabId) {
4106                ret =  e;
4107                return false;
4108             }
4109             return true;
4110             
4111         });
4112         return ret;
4113     },
4114     
4115     setActiveNext : function()
4116     {
4117         var i = this.indexOfNav(this.getActive());
4118         if (i > this.navItems.length) {
4119             return;
4120         }
4121         this.setActiveItem(this.navItems[i+1]);
4122     },
4123     setActivePrev : function()
4124     {
4125         var i = this.indexOfNav(this.getActive());
4126         if (i  < 1) {
4127             return;
4128         }
4129         this.setActiveItem(this.navItems[i-1]);
4130     },
4131     clearWasActive : function(except) {
4132         Roo.each(this.navItems, function(e) {
4133             if (e.tabId != except.tabId && e.was_active) {
4134                e.was_active = false;
4135                return false;
4136             }
4137             return true;
4138             
4139         });
4140     },
4141     getWasActive : function ()
4142     {
4143         var r = false;
4144         Roo.each(this.navItems, function(e) {
4145             if (e.was_active) {
4146                r = e;
4147                return false;
4148             }
4149             return true;
4150             
4151         });
4152         return r;
4153     }
4154     
4155     
4156 });
4157
4158  
4159 Roo.apply(Roo.bootstrap.NavGroup, {
4160     
4161     groups: {},
4162      /**
4163     * register a Navigation Group
4164     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4165     */
4166     register : function(navgrp)
4167     {
4168         this.groups[navgrp.navId] = navgrp;
4169         
4170     },
4171     /**
4172     * fetch a Navigation Group based on the navigation ID
4173     * @param {string} the navgroup to add
4174     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4175     */
4176     get: function(navId) {
4177         if (typeof(this.groups[navId]) == 'undefined') {
4178             return false;
4179             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4180         }
4181         return this.groups[navId] ;
4182     }
4183     
4184     
4185     
4186 });
4187
4188  /*
4189  * - LGPL
4190  *
4191  * row
4192  * 
4193  */
4194
4195 /**
4196  * @class Roo.bootstrap.NavItem
4197  * @extends Roo.bootstrap.Component
4198  * Bootstrap Navbar.NavItem class
4199  * @cfg {String} href  link to
4200  * @cfg {String} html content of button
4201  * @cfg {String} badge text inside badge
4202  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4203  * @cfg {String} glyphicon name of glyphicon
4204  * @cfg {String} icon name of font awesome icon
4205  * @cfg {Boolean} active Is item active
4206  * @cfg {Boolean} disabled Is item disabled
4207  
4208  * @cfg {Boolean} preventDefault (true | false) default false
4209  * @cfg {String} tabId the tab that this item activates.
4210  * @cfg {String} tagtype (a|span) render as a href or span?
4211  * @cfg {Boolean} animateRef (true|false) link to element default false  
4212   
4213  * @constructor
4214  * Create a new Navbar Item
4215  * @param {Object} config The config object
4216  */
4217 Roo.bootstrap.NavItem = function(config){
4218     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4219     this.addEvents({
4220         // raw events
4221         /**
4222          * @event click
4223          * The raw click event for the entire grid.
4224          * @param {Roo.EventObject} e
4225          */
4226         "click" : true,
4227          /**
4228             * @event changed
4229             * Fires when the active item active state changes
4230             * @param {Roo.bootstrap.NavItem} this
4231             * @param {boolean} state the new state
4232              
4233          */
4234         'changed': true,
4235         /**
4236             * @event scrollto
4237             * Fires when scroll to element
4238             * @param {Roo.bootstrap.NavItem} this
4239             * @param {Object} options
4240             * @param {Roo.EventObject} e
4241              
4242          */
4243         'scrollto': true
4244     });
4245    
4246 };
4247
4248 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4249     
4250     href: false,
4251     html: '',
4252     badge: '',
4253     icon: false,
4254     glyphicon: false,
4255     active: false,
4256     preventDefault : false,
4257     tabId : false,
4258     tagtype : 'a',
4259     disabled : false,
4260     animateRef : false,
4261     was_active : false,
4262     
4263     getAutoCreate : function(){
4264          
4265         var cfg = {
4266             tag: 'li',
4267             cls: 'nav-item'
4268             
4269         };
4270         
4271         if (this.active) {
4272             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4273         }
4274         if (this.disabled) {
4275             cfg.cls += ' disabled';
4276         }
4277         
4278         if (this.href || this.html || this.glyphicon || this.icon) {
4279             cfg.cn = [
4280                 {
4281                     tag: this.tagtype,
4282                     href : this.href || "#",
4283                     html: this.html || ''
4284                 }
4285             ];
4286             
4287             if (this.icon) {
4288                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4289             }
4290
4291             if(this.glyphicon) {
4292                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4293             }
4294             
4295             if (this.menu) {
4296                 
4297                 cfg.cn[0].html += " <span class='caret'></span>";
4298              
4299             }
4300             
4301             if (this.badge !== '') {
4302                  
4303                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4304             }
4305         }
4306         
4307         
4308         
4309         return cfg;
4310     },
4311     initEvents: function() 
4312     {
4313         if (typeof (this.menu) != 'undefined') {
4314             this.menu.parentType = this.xtype;
4315             this.menu.triggerEl = this.el;
4316             this.menu = this.addxtype(Roo.apply({}, this.menu));
4317         }
4318         
4319         this.el.select('a',true).on('click', this.onClick, this);
4320         
4321         if(this.tagtype == 'span'){
4322             this.el.select('span',true).on('click', this.onClick, this);
4323         }
4324        
4325         // at this point parent should be available..
4326         this.parent().register(this);
4327     },
4328     
4329     onClick : function(e)
4330     {
4331         if (e.getTarget('.dropdown-menu-item')) {
4332             // did you click on a menu itemm.... - then don't trigger onclick..
4333             return;
4334         }
4335         
4336         if(
4337                 this.preventDefault || 
4338                 this.href == '#' 
4339         ){
4340             Roo.log("NavItem - prevent Default?");
4341             e.preventDefault();
4342         }
4343         
4344         if (this.disabled) {
4345             return;
4346         }
4347         
4348         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4349         if (tg && tg.transition) {
4350             Roo.log("waiting for the transitionend");
4351             return;
4352         }
4353         
4354         
4355         
4356         //Roo.log("fire event clicked");
4357         if(this.fireEvent('click', this, e) === false){
4358             return;
4359         };
4360         
4361         if(this.tagtype == 'span'){
4362             return;
4363         }
4364         
4365         //Roo.log(this.href);
4366         var ael = this.el.select('a',true).first();
4367         //Roo.log(ael);
4368         
4369         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4370             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4371             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4372                 return; // ignore... - it's a 'hash' to another page.
4373             }
4374             Roo.log("NavItem - prevent Default?");
4375             e.preventDefault();
4376             this.scrollToElement(e);
4377         }
4378         
4379         
4380         var p =  this.parent();
4381    
4382         if (['tabs','pills'].indexOf(p.type)!==-1) {
4383             if (typeof(p.setActiveItem) !== 'undefined') {
4384                 p.setActiveItem(this);
4385             }
4386         }
4387         
4388         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4389         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4390             // remove the collapsed menu expand...
4391             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4392         }
4393     },
4394     
4395     isActive: function () {
4396         return this.active
4397     },
4398     setActive : function(state, fire, is_was_active)
4399     {
4400         if (this.active && !state && this.navId) {
4401             this.was_active = true;
4402             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4403             if (nv) {
4404                 nv.clearWasActive(this);
4405             }
4406             
4407         }
4408         this.active = state;
4409         
4410         if (!state ) {
4411             this.el.removeClass('active');
4412         } else if (!this.el.hasClass('active')) {
4413             this.el.addClass('active');
4414         }
4415         if (fire) {
4416             this.fireEvent('changed', this, state);
4417         }
4418         
4419         // show a panel if it's registered and related..
4420         
4421         if (!this.navId || !this.tabId || !state || is_was_active) {
4422             return;
4423         }
4424         
4425         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4426         if (!tg) {
4427             return;
4428         }
4429         var pan = tg.getPanelByName(this.tabId);
4430         if (!pan) {
4431             return;
4432         }
4433         // if we can not flip to new panel - go back to old nav highlight..
4434         if (false == tg.showPanel(pan)) {
4435             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4436             if (nv) {
4437                 var onav = nv.getWasActive();
4438                 if (onav) {
4439                     onav.setActive(true, false, true);
4440                 }
4441             }
4442             
4443         }
4444         
4445         
4446         
4447     },
4448      // this should not be here...
4449     setDisabled : function(state)
4450     {
4451         this.disabled = state;
4452         if (!state ) {
4453             this.el.removeClass('disabled');
4454         } else if (!this.el.hasClass('disabled')) {
4455             this.el.addClass('disabled');
4456         }
4457         
4458     },
4459     
4460     /**
4461      * Fetch the element to display the tooltip on.
4462      * @return {Roo.Element} defaults to this.el
4463      */
4464     tooltipEl : function()
4465     {
4466         return this.el.select('' + this.tagtype + '', true).first();
4467     },
4468     
4469     scrollToElement : function(e)
4470     {
4471         var c = document.body;
4472         
4473         /*
4474          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4475          */
4476         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4477             c = document.documentElement;
4478         }
4479         
4480         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4481         
4482         if(!target){
4483             return;
4484         }
4485
4486         var o = target.calcOffsetsTo(c);
4487         
4488         var options = {
4489             target : target,
4490             value : o[1]
4491         };
4492         
4493         this.fireEvent('scrollto', this, options, e);
4494         
4495         Roo.get(c).scrollTo('top', options.value, true);
4496         
4497         return;
4498     }
4499 });
4500  
4501
4502  /*
4503  * - LGPL
4504  *
4505  * sidebar item
4506  *
4507  *  li
4508  *    <span> icon </span>
4509  *    <span> text </span>
4510  *    <span>badge </span>
4511  */
4512
4513 /**
4514  * @class Roo.bootstrap.NavSidebarItem
4515  * @extends Roo.bootstrap.NavItem
4516  * Bootstrap Navbar.NavSidebarItem class
4517  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4518  * {bool} open is the menu open
4519  * @constructor
4520  * Create a new Navbar Button
4521  * @param {Object} config The config object
4522  */
4523 Roo.bootstrap.NavSidebarItem = function(config){
4524     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4525     this.addEvents({
4526         // raw events
4527         /**
4528          * @event click
4529          * The raw click event for the entire grid.
4530          * @param {Roo.EventObject} e
4531          */
4532         "click" : true,
4533          /**
4534             * @event changed
4535             * Fires when the active item active state changes
4536             * @param {Roo.bootstrap.NavSidebarItem} this
4537             * @param {boolean} state the new state
4538              
4539          */
4540         'changed': true
4541     });
4542    
4543 };
4544
4545 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4546     
4547     badgeWeight : 'default',
4548     
4549     open: false,
4550     
4551     getAutoCreate : function(){
4552         
4553         
4554         var a = {
4555                 tag: 'a',
4556                 href : this.href || '#',
4557                 cls: '',
4558                 html : '',
4559                 cn : []
4560         };
4561         var cfg = {
4562             tag: 'li',
4563             cls: '',
4564             cn: [ a ]
4565         };
4566         var span = {
4567             tag: 'span',
4568             html : this.html || ''
4569         };
4570         
4571         
4572         if (this.active) {
4573             cfg.cls += ' active';
4574         }
4575         
4576         if (this.disabled) {
4577             cfg.cls += ' disabled';
4578         }
4579         if (this.open) {
4580             cfg.cls += ' open x-open';
4581         }
4582         // left icon..
4583         if (this.glyphicon || this.icon) {
4584             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4585             a.cn.push({ tag : 'i', cls : c }) ;
4586         }
4587         // html..
4588         a.cn.push(span);
4589         // then badge..
4590         if (this.badge !== '') {
4591             
4592             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4593         }
4594         // fi
4595         if (this.menu) {
4596             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4597             a.cls += 'dropdown-toggle treeview' ;
4598         }
4599         
4600         return cfg;
4601          
4602            
4603     },
4604     
4605     initEvents : function()
4606     { 
4607         if (typeof (this.menu) != 'undefined') {
4608             this.menu.parentType = this.xtype;
4609             this.menu.triggerEl = this.el;
4610             this.menu = this.addxtype(Roo.apply({}, this.menu));
4611         }
4612         
4613         this.el.on('click', this.onClick, this);
4614        
4615     
4616         if(this.badge !== ''){
4617  
4618             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4619         }
4620         
4621     },
4622     
4623     onClick : function(e)
4624     {
4625         if(this.disabled){
4626             e.preventDefault();
4627             return;
4628         }
4629         
4630         if(this.preventDefault){
4631             e.preventDefault();
4632         }
4633         
4634         this.fireEvent('click', this);
4635     },
4636     
4637     disable : function()
4638     {
4639         this.setDisabled(true);
4640     },
4641     
4642     enable : function()
4643     {
4644         this.setDisabled(false);
4645     },
4646     
4647     setDisabled : function(state)
4648     {
4649         if(this.disabled == state){
4650             return;
4651         }
4652         
4653         this.disabled = state;
4654         
4655         if (state) {
4656             this.el.addClass('disabled');
4657             return;
4658         }
4659         
4660         this.el.removeClass('disabled');
4661         
4662         return;
4663     },
4664     
4665     setActive : function(state)
4666     {
4667         if(this.active == state){
4668             return;
4669         }
4670         
4671         this.active = state;
4672         
4673         if (state) {
4674             this.el.addClass('active');
4675             return;
4676         }
4677         
4678         this.el.removeClass('active');
4679         
4680         return;
4681     },
4682     
4683     isActive: function () 
4684     {
4685         return this.active;
4686     },
4687     
4688     setBadge : function(str)
4689     {
4690         if(!this.badgeEl){
4691             return;
4692         }
4693         
4694         this.badgeEl.dom.innerHTML = str;
4695     }
4696     
4697    
4698      
4699  
4700 });
4701  
4702
4703  /*
4704  * - LGPL
4705  *
4706  * row
4707  * 
4708  */
4709
4710 /**
4711  * @class Roo.bootstrap.Row
4712  * @extends Roo.bootstrap.Component
4713  * Bootstrap Row class (contains columns...)
4714  * 
4715  * @constructor
4716  * Create a new Row
4717  * @param {Object} config The config object
4718  */
4719
4720 Roo.bootstrap.Row = function(config){
4721     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4722 };
4723
4724 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4725     
4726     getAutoCreate : function(){
4727        return {
4728             cls: 'row clearfix'
4729        };
4730     }
4731     
4732     
4733 });
4734
4735  
4736
4737  /*
4738  * - LGPL
4739  *
4740  * element
4741  * 
4742  */
4743
4744 /**
4745  * @class Roo.bootstrap.Element
4746  * @extends Roo.bootstrap.Component
4747  * Bootstrap Element class
4748  * @cfg {String} html contents of the element
4749  * @cfg {String} tag tag of the element
4750  * @cfg {String} cls class of the element
4751  * @cfg {Boolean} preventDefault (true|false) default false
4752  * @cfg {Boolean} clickable (true|false) default false
4753  * 
4754  * @constructor
4755  * Create a new Element
4756  * @param {Object} config The config object
4757  */
4758
4759 Roo.bootstrap.Element = function(config){
4760     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4761     
4762     this.addEvents({
4763         // raw events
4764         /**
4765          * @event click
4766          * When a element is chick
4767          * @param {Roo.bootstrap.Element} this
4768          * @param {Roo.EventObject} e
4769          */
4770         "click" : true
4771     });
4772 };
4773
4774 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4775     
4776     tag: 'div',
4777     cls: '',
4778     html: '',
4779     preventDefault: false, 
4780     clickable: false,
4781     
4782     getAutoCreate : function(){
4783         
4784         var cfg = {
4785             tag: this.tag,
4786             cls: this.cls,
4787             html: this.html
4788         };
4789         
4790         return cfg;
4791     },
4792     
4793     initEvents: function() 
4794     {
4795         Roo.bootstrap.Element.superclass.initEvents.call(this);
4796         
4797         if(this.clickable){
4798             this.el.on('click', this.onClick, this);
4799         }
4800         
4801     },
4802     
4803     onClick : function(e)
4804     {
4805         if(this.preventDefault){
4806             e.preventDefault();
4807         }
4808         
4809         this.fireEvent('click', this, e);
4810     },
4811     
4812     getValue : function()
4813     {
4814         return this.el.dom.innerHTML;
4815     },
4816     
4817     setValue : function(value)
4818     {
4819         this.el.dom.innerHTML = value;
4820     }
4821    
4822 });
4823
4824  
4825
4826  /*
4827  * - LGPL
4828  *
4829  * pagination
4830  * 
4831  */
4832
4833 /**
4834  * @class Roo.bootstrap.Pagination
4835  * @extends Roo.bootstrap.Component
4836  * Bootstrap Pagination class
4837  * @cfg {String} size xs | sm | md | lg
4838  * @cfg {Boolean} inverse false | true
4839  * 
4840  * @constructor
4841  * Create a new Pagination
4842  * @param {Object} config The config object
4843  */
4844
4845 Roo.bootstrap.Pagination = function(config){
4846     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4847 };
4848
4849 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4850     
4851     cls: false,
4852     size: false,
4853     inverse: false,
4854     
4855     getAutoCreate : function(){
4856         var cfg = {
4857             tag: 'ul',
4858                 cls: 'pagination'
4859         };
4860         if (this.inverse) {
4861             cfg.cls += ' inverse';
4862         }
4863         if (this.html) {
4864             cfg.html=this.html;
4865         }
4866         if (this.cls) {
4867             cfg.cls += " " + this.cls;
4868         }
4869         return cfg;
4870     }
4871    
4872 });
4873
4874  
4875
4876  /*
4877  * - LGPL
4878  *
4879  * Pagination item
4880  * 
4881  */
4882
4883
4884 /**
4885  * @class Roo.bootstrap.PaginationItem
4886  * @extends Roo.bootstrap.Component
4887  * Bootstrap PaginationItem class
4888  * @cfg {String} html text
4889  * @cfg {String} href the link
4890  * @cfg {Boolean} preventDefault (true | false) default true
4891  * @cfg {Boolean} active (true | false) default false
4892  * @cfg {Boolean} disabled default false
4893  * 
4894  * 
4895  * @constructor
4896  * Create a new PaginationItem
4897  * @param {Object} config The config object
4898  */
4899
4900
4901 Roo.bootstrap.PaginationItem = function(config){
4902     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4903     this.addEvents({
4904         // raw events
4905         /**
4906          * @event click
4907          * The raw click event for the entire grid.
4908          * @param {Roo.EventObject} e
4909          */
4910         "click" : true
4911     });
4912 };
4913
4914 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4915     
4916     href : false,
4917     html : false,
4918     preventDefault: true,
4919     active : false,
4920     cls : false,
4921     disabled: false,
4922     
4923     getAutoCreate : function(){
4924         var cfg= {
4925             tag: 'li',
4926             cn: [
4927                 {
4928                     tag : 'a',
4929                     href : this.href ? this.href : '#',
4930                     html : this.html ? this.html : ''
4931                 }
4932             ]
4933         };
4934         
4935         if(this.cls){
4936             cfg.cls = this.cls;
4937         }
4938         
4939         if(this.disabled){
4940             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4941         }
4942         
4943         if(this.active){
4944             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4945         }
4946         
4947         return cfg;
4948     },
4949     
4950     initEvents: function() {
4951         
4952         this.el.on('click', this.onClick, this);
4953         
4954     },
4955     onClick : function(e)
4956     {
4957         Roo.log('PaginationItem on click ');
4958         if(this.preventDefault){
4959             e.preventDefault();
4960         }
4961         
4962         if(this.disabled){
4963             return;
4964         }
4965         
4966         this.fireEvent('click', this, e);
4967     }
4968    
4969 });
4970
4971  
4972
4973  /*
4974  * - LGPL
4975  *
4976  * slider
4977  * 
4978  */
4979
4980
4981 /**
4982  * @class Roo.bootstrap.Slider
4983  * @extends Roo.bootstrap.Component
4984  * Bootstrap Slider class
4985  *    
4986  * @constructor
4987  * Create a new Slider
4988  * @param {Object} config The config object
4989  */
4990
4991 Roo.bootstrap.Slider = function(config){
4992     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4993 };
4994
4995 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4996     
4997     getAutoCreate : function(){
4998         
4999         var cfg = {
5000             tag: 'div',
5001             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5002             cn: [
5003                 {
5004                     tag: 'a',
5005                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5006                 }
5007             ]
5008         };
5009         
5010         return cfg;
5011     }
5012    
5013 });
5014
5015  /*
5016  * Based on:
5017  * Ext JS Library 1.1.1
5018  * Copyright(c) 2006-2007, Ext JS, LLC.
5019  *
5020  * Originally Released Under LGPL - original licence link has changed is not relivant.
5021  *
5022  * Fork - LGPL
5023  * <script type="text/javascript">
5024  */
5025  
5026
5027 /**
5028  * @class Roo.grid.ColumnModel
5029  * @extends Roo.util.Observable
5030  * This is the default implementation of a ColumnModel used by the Grid. It defines
5031  * the columns in the grid.
5032  * <br>Usage:<br>
5033  <pre><code>
5034  var colModel = new Roo.grid.ColumnModel([
5035         {header: "Ticker", width: 60, sortable: true, locked: true},
5036         {header: "Company Name", width: 150, sortable: true},
5037         {header: "Market Cap.", width: 100, sortable: true},
5038         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5039         {header: "Employees", width: 100, sortable: true, resizable: false}
5040  ]);
5041  </code></pre>
5042  * <p>
5043  
5044  * The config options listed for this class are options which may appear in each
5045  * individual column definition.
5046  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5047  * @constructor
5048  * @param {Object} config An Array of column config objects. See this class's
5049  * config objects for details.
5050 */
5051 Roo.grid.ColumnModel = function(config){
5052         /**
5053      * The config passed into the constructor
5054      */
5055     this.config = config;
5056     this.lookup = {};
5057
5058     // if no id, create one
5059     // if the column does not have a dataIndex mapping,
5060     // map it to the order it is in the config
5061     for(var i = 0, len = config.length; i < len; i++){
5062         var c = config[i];
5063         if(typeof c.dataIndex == "undefined"){
5064             c.dataIndex = i;
5065         }
5066         if(typeof c.renderer == "string"){
5067             c.renderer = Roo.util.Format[c.renderer];
5068         }
5069         if(typeof c.id == "undefined"){
5070             c.id = Roo.id();
5071         }
5072         if(c.editor && c.editor.xtype){
5073             c.editor  = Roo.factory(c.editor, Roo.grid);
5074         }
5075         if(c.editor && c.editor.isFormField){
5076             c.editor = new Roo.grid.GridEditor(c.editor);
5077         }
5078         this.lookup[c.id] = c;
5079     }
5080
5081     /**
5082      * The width of columns which have no width specified (defaults to 100)
5083      * @type Number
5084      */
5085     this.defaultWidth = 100;
5086
5087     /**
5088      * Default sortable of columns which have no sortable specified (defaults to false)
5089      * @type Boolean
5090      */
5091     this.defaultSortable = false;
5092
5093     this.addEvents({
5094         /**
5095              * @event widthchange
5096              * Fires when the width of a column changes.
5097              * @param {ColumnModel} this
5098              * @param {Number} columnIndex The column index
5099              * @param {Number} newWidth The new width
5100              */
5101             "widthchange": true,
5102         /**
5103              * @event headerchange
5104              * Fires when the text of a header changes.
5105              * @param {ColumnModel} this
5106              * @param {Number} columnIndex The column index
5107              * @param {Number} newText The new header text
5108              */
5109             "headerchange": true,
5110         /**
5111              * @event hiddenchange
5112              * Fires when a column is hidden or "unhidden".
5113              * @param {ColumnModel} this
5114              * @param {Number} columnIndex The column index
5115              * @param {Boolean} hidden true if hidden, false otherwise
5116              */
5117             "hiddenchange": true,
5118             /**
5119          * @event columnmoved
5120          * Fires when a column is moved.
5121          * @param {ColumnModel} this
5122          * @param {Number} oldIndex
5123          * @param {Number} newIndex
5124          */
5125         "columnmoved" : true,
5126         /**
5127          * @event columlockchange
5128          * Fires when a column's locked state is changed
5129          * @param {ColumnModel} this
5130          * @param {Number} colIndex
5131          * @param {Boolean} locked true if locked
5132          */
5133         "columnlockchange" : true
5134     });
5135     Roo.grid.ColumnModel.superclass.constructor.call(this);
5136 };
5137 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5138     /**
5139      * @cfg {String} header The header text to display in the Grid view.
5140      */
5141     /**
5142      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5143      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5144      * specified, the column's index is used as an index into the Record's data Array.
5145      */
5146     /**
5147      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5148      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5149      */
5150     /**
5151      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5152      * Defaults to the value of the {@link #defaultSortable} property.
5153      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5154      */
5155     /**
5156      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5157      */
5158     /**
5159      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5160      */
5161     /**
5162      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5163      */
5164     /**
5165      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5166      */
5167     /**
5168      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5169      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5170      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5171      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5172      */
5173        /**
5174      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5175      */
5176     /**
5177      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5178      */
5179     /**
5180      * @cfg {String} cursor (Optional)
5181      */
5182     /**
5183      * @cfg {String} tooltip (Optional)
5184      */
5185     /**
5186      * @cfg {Number} xs (Optional)
5187      */
5188     /**
5189      * @cfg {Number} sm (Optional)
5190      */
5191     /**
5192      * @cfg {Number} md (Optional)
5193      */
5194     /**
5195      * @cfg {Number} lg (Optional)
5196      */
5197     /**
5198      * Returns the id of the column at the specified index.
5199      * @param {Number} index The column index
5200      * @return {String} the id
5201      */
5202     getColumnId : function(index){
5203         return this.config[index].id;
5204     },
5205
5206     /**
5207      * Returns the column for a specified id.
5208      * @param {String} id The column id
5209      * @return {Object} the column
5210      */
5211     getColumnById : function(id){
5212         return this.lookup[id];
5213     },
5214
5215     
5216     /**
5217      * Returns the column for a specified dataIndex.
5218      * @param {String} dataIndex The column dataIndex
5219      * @return {Object|Boolean} the column or false if not found
5220      */
5221     getColumnByDataIndex: function(dataIndex){
5222         var index = this.findColumnIndex(dataIndex);
5223         return index > -1 ? this.config[index] : false;
5224     },
5225     
5226     /**
5227      * Returns the index for a specified column id.
5228      * @param {String} id The column id
5229      * @return {Number} the index, or -1 if not found
5230      */
5231     getIndexById : function(id){
5232         for(var i = 0, len = this.config.length; i < len; i++){
5233             if(this.config[i].id == id){
5234                 return i;
5235             }
5236         }
5237         return -1;
5238     },
5239     
5240     /**
5241      * Returns the index for a specified column dataIndex.
5242      * @param {String} dataIndex The column dataIndex
5243      * @return {Number} the index, or -1 if not found
5244      */
5245     
5246     findColumnIndex : function(dataIndex){
5247         for(var i = 0, len = this.config.length; i < len; i++){
5248             if(this.config[i].dataIndex == dataIndex){
5249                 return i;
5250             }
5251         }
5252         return -1;
5253     },
5254     
5255     
5256     moveColumn : function(oldIndex, newIndex){
5257         var c = this.config[oldIndex];
5258         this.config.splice(oldIndex, 1);
5259         this.config.splice(newIndex, 0, c);
5260         this.dataMap = null;
5261         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5262     },
5263
5264     isLocked : function(colIndex){
5265         return this.config[colIndex].locked === true;
5266     },
5267
5268     setLocked : function(colIndex, value, suppressEvent){
5269         if(this.isLocked(colIndex) == value){
5270             return;
5271         }
5272         this.config[colIndex].locked = value;
5273         if(!suppressEvent){
5274             this.fireEvent("columnlockchange", this, colIndex, value);
5275         }
5276     },
5277
5278     getTotalLockedWidth : function(){
5279         var totalWidth = 0;
5280         for(var i = 0; i < this.config.length; i++){
5281             if(this.isLocked(i) && !this.isHidden(i)){
5282                 this.totalWidth += this.getColumnWidth(i);
5283             }
5284         }
5285         return totalWidth;
5286     },
5287
5288     getLockedCount : function(){
5289         for(var i = 0, len = this.config.length; i < len; i++){
5290             if(!this.isLocked(i)){
5291                 return i;
5292             }
5293         }
5294         
5295         return this.config.length;
5296     },
5297
5298     /**
5299      * Returns the number of columns.
5300      * @return {Number}
5301      */
5302     getColumnCount : function(visibleOnly){
5303         if(visibleOnly === true){
5304             var c = 0;
5305             for(var i = 0, len = this.config.length; i < len; i++){
5306                 if(!this.isHidden(i)){
5307                     c++;
5308                 }
5309             }
5310             return c;
5311         }
5312         return this.config.length;
5313     },
5314
5315     /**
5316      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5317      * @param {Function} fn
5318      * @param {Object} scope (optional)
5319      * @return {Array} result
5320      */
5321     getColumnsBy : function(fn, scope){
5322         var r = [];
5323         for(var i = 0, len = this.config.length; i < len; i++){
5324             var c = this.config[i];
5325             if(fn.call(scope||this, c, i) === true){
5326                 r[r.length] = c;
5327             }
5328         }
5329         return r;
5330     },
5331
5332     /**
5333      * Returns true if the specified column is sortable.
5334      * @param {Number} col The column index
5335      * @return {Boolean}
5336      */
5337     isSortable : function(col){
5338         if(typeof this.config[col].sortable == "undefined"){
5339             return this.defaultSortable;
5340         }
5341         return this.config[col].sortable;
5342     },
5343
5344     /**
5345      * Returns the rendering (formatting) function defined for the column.
5346      * @param {Number} col The column index.
5347      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5348      */
5349     getRenderer : function(col){
5350         if(!this.config[col].renderer){
5351             return Roo.grid.ColumnModel.defaultRenderer;
5352         }
5353         return this.config[col].renderer;
5354     },
5355
5356     /**
5357      * Sets the rendering (formatting) function for a column.
5358      * @param {Number} col The column index
5359      * @param {Function} fn The function to use to process the cell's raw data
5360      * to return HTML markup for the grid view. The render function is called with
5361      * the following parameters:<ul>
5362      * <li>Data value.</li>
5363      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5364      * <li>css A CSS style string to apply to the table cell.</li>
5365      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5366      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5367      * <li>Row index</li>
5368      * <li>Column index</li>
5369      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5370      */
5371     setRenderer : function(col, fn){
5372         this.config[col].renderer = fn;
5373     },
5374
5375     /**
5376      * Returns the width for the specified column.
5377      * @param {Number} col The column index
5378      * @return {Number}
5379      */
5380     getColumnWidth : function(col){
5381         return this.config[col].width * 1 || this.defaultWidth;
5382     },
5383
5384     /**
5385      * Sets the width for a column.
5386      * @param {Number} col The column index
5387      * @param {Number} width The new width
5388      */
5389     setColumnWidth : function(col, width, suppressEvent){
5390         this.config[col].width = width;
5391         this.totalWidth = null;
5392         if(!suppressEvent){
5393              this.fireEvent("widthchange", this, col, width);
5394         }
5395     },
5396
5397     /**
5398      * Returns the total width of all columns.
5399      * @param {Boolean} includeHidden True to include hidden column widths
5400      * @return {Number}
5401      */
5402     getTotalWidth : function(includeHidden){
5403         if(!this.totalWidth){
5404             this.totalWidth = 0;
5405             for(var i = 0, len = this.config.length; i < len; i++){
5406                 if(includeHidden || !this.isHidden(i)){
5407                     this.totalWidth += this.getColumnWidth(i);
5408                 }
5409             }
5410         }
5411         return this.totalWidth;
5412     },
5413
5414     /**
5415      * Returns the header for the specified column.
5416      * @param {Number} col The column index
5417      * @return {String}
5418      */
5419     getColumnHeader : function(col){
5420         return this.config[col].header;
5421     },
5422
5423     /**
5424      * Sets the header for a column.
5425      * @param {Number} col The column index
5426      * @param {String} header The new header
5427      */
5428     setColumnHeader : function(col, header){
5429         this.config[col].header = header;
5430         this.fireEvent("headerchange", this, col, header);
5431     },
5432
5433     /**
5434      * Returns the tooltip for the specified column.
5435      * @param {Number} col The column index
5436      * @return {String}
5437      */
5438     getColumnTooltip : function(col){
5439             return this.config[col].tooltip;
5440     },
5441     /**
5442      * Sets the tooltip for a column.
5443      * @param {Number} col The column index
5444      * @param {String} tooltip The new tooltip
5445      */
5446     setColumnTooltip : function(col, tooltip){
5447             this.config[col].tooltip = tooltip;
5448     },
5449
5450     /**
5451      * Returns the dataIndex for the specified column.
5452      * @param {Number} col The column index
5453      * @return {Number}
5454      */
5455     getDataIndex : function(col){
5456         return this.config[col].dataIndex;
5457     },
5458
5459     /**
5460      * Sets the dataIndex for a column.
5461      * @param {Number} col The column index
5462      * @param {Number} dataIndex The new dataIndex
5463      */
5464     setDataIndex : function(col, dataIndex){
5465         this.config[col].dataIndex = dataIndex;
5466     },
5467
5468     
5469     
5470     /**
5471      * Returns true if the cell is editable.
5472      * @param {Number} colIndex The column index
5473      * @param {Number} rowIndex The row index - this is nto actually used..?
5474      * @return {Boolean}
5475      */
5476     isCellEditable : function(colIndex, rowIndex){
5477         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5478     },
5479
5480     /**
5481      * Returns the editor defined for the cell/column.
5482      * return false or null to disable editing.
5483      * @param {Number} colIndex The column index
5484      * @param {Number} rowIndex The row index
5485      * @return {Object}
5486      */
5487     getCellEditor : function(colIndex, rowIndex){
5488         return this.config[colIndex].editor;
5489     },
5490
5491     /**
5492      * Sets if a column is editable.
5493      * @param {Number} col The column index
5494      * @param {Boolean} editable True if the column is editable
5495      */
5496     setEditable : function(col, editable){
5497         this.config[col].editable = editable;
5498     },
5499
5500
5501     /**
5502      * Returns true if the column is hidden.
5503      * @param {Number} colIndex The column index
5504      * @return {Boolean}
5505      */
5506     isHidden : function(colIndex){
5507         return this.config[colIndex].hidden;
5508     },
5509
5510
5511     /**
5512      * Returns true if the column width cannot be changed
5513      */
5514     isFixed : function(colIndex){
5515         return this.config[colIndex].fixed;
5516     },
5517
5518     /**
5519      * Returns true if the column can be resized
5520      * @return {Boolean}
5521      */
5522     isResizable : function(colIndex){
5523         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5524     },
5525     /**
5526      * Sets if a column is hidden.
5527      * @param {Number} colIndex The column index
5528      * @param {Boolean} hidden True if the column is hidden
5529      */
5530     setHidden : function(colIndex, hidden){
5531         this.config[colIndex].hidden = hidden;
5532         this.totalWidth = null;
5533         this.fireEvent("hiddenchange", this, colIndex, hidden);
5534     },
5535
5536     /**
5537      * Sets the editor for a column.
5538      * @param {Number} col The column index
5539      * @param {Object} editor The editor object
5540      */
5541     setEditor : function(col, editor){
5542         this.config[col].editor = editor;
5543     }
5544 });
5545
5546 Roo.grid.ColumnModel.defaultRenderer = function(value)
5547 {
5548     if(typeof value == "object") {
5549         return value;
5550     }
5551         if(typeof value == "string" && value.length < 1){
5552             return "&#160;";
5553         }
5554     
5555         return String.format("{0}", value);
5556 };
5557
5558 // Alias for backwards compatibility
5559 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5560 /*
5561  * Based on:
5562  * Ext JS Library 1.1.1
5563  * Copyright(c) 2006-2007, Ext JS, LLC.
5564  *
5565  * Originally Released Under LGPL - original licence link has changed is not relivant.
5566  *
5567  * Fork - LGPL
5568  * <script type="text/javascript">
5569  */
5570  
5571 /**
5572  * @class Roo.LoadMask
5573  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5574  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5575  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5576  * element's UpdateManager load indicator and will be destroyed after the initial load.
5577  * @constructor
5578  * Create a new LoadMask
5579  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5580  * @param {Object} config The config object
5581  */
5582 Roo.LoadMask = function(el, config){
5583     this.el = Roo.get(el);
5584     Roo.apply(this, config);
5585     if(this.store){
5586         this.store.on('beforeload', this.onBeforeLoad, this);
5587         this.store.on('load', this.onLoad, this);
5588         this.store.on('loadexception', this.onLoadException, this);
5589         this.removeMask = false;
5590     }else{
5591         var um = this.el.getUpdateManager();
5592         um.showLoadIndicator = false; // disable the default indicator
5593         um.on('beforeupdate', this.onBeforeLoad, this);
5594         um.on('update', this.onLoad, this);
5595         um.on('failure', this.onLoad, this);
5596         this.removeMask = true;
5597     }
5598 };
5599
5600 Roo.LoadMask.prototype = {
5601     /**
5602      * @cfg {Boolean} removeMask
5603      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5604      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5605      */
5606     /**
5607      * @cfg {String} msg
5608      * The text to display in a centered loading message box (defaults to 'Loading...')
5609      */
5610     msg : 'Loading...',
5611     /**
5612      * @cfg {String} msgCls
5613      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5614      */
5615     msgCls : 'x-mask-loading',
5616
5617     /**
5618      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5619      * @type Boolean
5620      */
5621     disabled: false,
5622
5623     /**
5624      * Disables the mask to prevent it from being displayed
5625      */
5626     disable : function(){
5627        this.disabled = true;
5628     },
5629
5630     /**
5631      * Enables the mask so that it can be displayed
5632      */
5633     enable : function(){
5634         this.disabled = false;
5635     },
5636     
5637     onLoadException : function()
5638     {
5639         Roo.log(arguments);
5640         
5641         if (typeof(arguments[3]) != 'undefined') {
5642             Roo.MessageBox.alert("Error loading",arguments[3]);
5643         } 
5644         /*
5645         try {
5646             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5647                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5648             }   
5649         } catch(e) {
5650             
5651         }
5652         */
5653     
5654         
5655         
5656         this.el.unmask(this.removeMask);
5657     },
5658     // private
5659     onLoad : function()
5660     {
5661         this.el.unmask(this.removeMask);
5662     },
5663
5664     // private
5665     onBeforeLoad : function(){
5666         if(!this.disabled){
5667             this.el.mask(this.msg, this.msgCls);
5668         }
5669     },
5670
5671     // private
5672     destroy : function(){
5673         if(this.store){
5674             this.store.un('beforeload', this.onBeforeLoad, this);
5675             this.store.un('load', this.onLoad, this);
5676             this.store.un('loadexception', this.onLoadException, this);
5677         }else{
5678             var um = this.el.getUpdateManager();
5679             um.un('beforeupdate', this.onBeforeLoad, this);
5680             um.un('update', this.onLoad, this);
5681             um.un('failure', this.onLoad, this);
5682         }
5683     }
5684 };/*
5685  * - LGPL
5686  *
5687  * table
5688  * 
5689  */
5690
5691 /**
5692  * @class Roo.bootstrap.Table
5693  * @extends Roo.bootstrap.Component
5694  * Bootstrap Table class
5695  * @cfg {String} cls table class
5696  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5697  * @cfg {String} bgcolor Specifies the background color for a table
5698  * @cfg {Number} border Specifies whether the table cells should have borders or not
5699  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5700  * @cfg {Number} cellspacing Specifies the space between cells
5701  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5702  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5703  * @cfg {String} sortable Specifies that the table should be sortable
5704  * @cfg {String} summary Specifies a summary of the content of a table
5705  * @cfg {Number} width Specifies the width of a table
5706  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5707  * 
5708  * @cfg {boolean} striped Should the rows be alternative striped
5709  * @cfg {boolean} bordered Add borders to the table
5710  * @cfg {boolean} hover Add hover highlighting
5711  * @cfg {boolean} condensed Format condensed
5712  * @cfg {boolean} responsive Format condensed
5713  * @cfg {Boolean} loadMask (true|false) default false
5714  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5715  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5716  * @cfg {Boolean} rowSelection (true|false) default false
5717  * @cfg {Boolean} cellSelection (true|false) default false
5718  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5719  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5720  
5721  * 
5722  * @constructor
5723  * Create a new Table
5724  * @param {Object} config The config object
5725  */
5726
5727 Roo.bootstrap.Table = function(config){
5728     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5729     
5730   
5731     
5732     // BC...
5733     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5734     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5735     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5736     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5737     
5738     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5739     if (this.sm) {
5740         this.sm.grid = this;
5741         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5742         this.sm = this.selModel;
5743         this.sm.xmodule = this.xmodule || false;
5744     }
5745     
5746     if (this.cm && typeof(this.cm.config) == 'undefined') {
5747         this.colModel = new Roo.grid.ColumnModel(this.cm);
5748         this.cm = this.colModel;
5749         this.cm.xmodule = this.xmodule || false;
5750     }
5751     if (this.store) {
5752         this.store= Roo.factory(this.store, Roo.data);
5753         this.ds = this.store;
5754         this.ds.xmodule = this.xmodule || false;
5755          
5756     }
5757     if (this.footer && this.store) {
5758         this.footer.dataSource = this.ds;
5759         this.footer = Roo.factory(this.footer);
5760     }
5761     
5762     /** @private */
5763     this.addEvents({
5764         /**
5765          * @event cellclick
5766          * Fires when a cell is clicked
5767          * @param {Roo.bootstrap.Table} this
5768          * @param {Roo.Element} el
5769          * @param {Number} rowIndex
5770          * @param {Number} columnIndex
5771          * @param {Roo.EventObject} e
5772          */
5773         "cellclick" : true,
5774         /**
5775          * @event celldblclick
5776          * Fires when a cell is double clicked
5777          * @param {Roo.bootstrap.Table} this
5778          * @param {Roo.Element} el
5779          * @param {Number} rowIndex
5780          * @param {Number} columnIndex
5781          * @param {Roo.EventObject} e
5782          */
5783         "celldblclick" : true,
5784         /**
5785          * @event rowclick
5786          * Fires when a row is clicked
5787          * @param {Roo.bootstrap.Table} this
5788          * @param {Roo.Element} el
5789          * @param {Number} rowIndex
5790          * @param {Roo.EventObject} e
5791          */
5792         "rowclick" : true,
5793         /**
5794          * @event rowdblclick
5795          * Fires when a row is double clicked
5796          * @param {Roo.bootstrap.Table} this
5797          * @param {Roo.Element} el
5798          * @param {Number} rowIndex
5799          * @param {Roo.EventObject} e
5800          */
5801         "rowdblclick" : true,
5802         /**
5803          * @event mouseover
5804          * Fires when a mouseover occur
5805          * @param {Roo.bootstrap.Table} this
5806          * @param {Roo.Element} el
5807          * @param {Number} rowIndex
5808          * @param {Number} columnIndex
5809          * @param {Roo.EventObject} e
5810          */
5811         "mouseover" : true,
5812         /**
5813          * @event mouseout
5814          * Fires when a mouseout occur
5815          * @param {Roo.bootstrap.Table} this
5816          * @param {Roo.Element} el
5817          * @param {Number} rowIndex
5818          * @param {Number} columnIndex
5819          * @param {Roo.EventObject} e
5820          */
5821         "mouseout" : true,
5822         /**
5823          * @event rowclass
5824          * Fires when a row is rendered, so you can change add a style to it.
5825          * @param {Roo.bootstrap.Table} this
5826          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5827          */
5828         'rowclass' : true,
5829           /**
5830          * @event rowsrendered
5831          * Fires when all the  rows have been rendered
5832          * @param {Roo.bootstrap.Table} this
5833          */
5834         'rowsrendered' : true,
5835         /**
5836          * @event contextmenu
5837          * The raw contextmenu event for the entire grid.
5838          * @param {Roo.EventObject} e
5839          */
5840         "contextmenu" : true,
5841         /**
5842          * @event rowcontextmenu
5843          * Fires when a row is right clicked
5844          * @param {Roo.bootstrap.Table} this
5845          * @param {Number} rowIndex
5846          * @param {Roo.EventObject} e
5847          */
5848         "rowcontextmenu" : true,
5849         /**
5850          * @event cellcontextmenu
5851          * Fires when a cell is right clicked
5852          * @param {Roo.bootstrap.Table} this
5853          * @param {Number} rowIndex
5854          * @param {Number} cellIndex
5855          * @param {Roo.EventObject} e
5856          */
5857          "cellcontextmenu" : true,
5858          /**
5859          * @event headercontextmenu
5860          * Fires when a header is right clicked
5861          * @param {Roo.bootstrap.Table} this
5862          * @param {Number} columnIndex
5863          * @param {Roo.EventObject} e
5864          */
5865         "headercontextmenu" : true
5866     });
5867 };
5868
5869 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5870     
5871     cls: false,
5872     align: false,
5873     bgcolor: false,
5874     border: false,
5875     cellpadding: false,
5876     cellspacing: false,
5877     frame: false,
5878     rules: false,
5879     sortable: false,
5880     summary: false,
5881     width: false,
5882     striped : false,
5883     scrollBody : false,
5884     bordered: false,
5885     hover:  false,
5886     condensed : false,
5887     responsive : false,
5888     sm : false,
5889     cm : false,
5890     store : false,
5891     loadMask : false,
5892     footerShow : true,
5893     headerShow : true,
5894   
5895     rowSelection : false,
5896     cellSelection : false,
5897     layout : false,
5898     
5899     // Roo.Element - the tbody
5900     mainBody: false,
5901     // Roo.Element - thead element
5902     mainHead: false,
5903     
5904     container: false, // used by gridpanel...
5905     
5906     getAutoCreate : function()
5907     {
5908         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5909         
5910         cfg = {
5911             tag: 'table',
5912             cls : 'table',
5913             cn : []
5914         };
5915         if (this.scrollBody) {
5916             cfg.cls += ' table-body-fixed';
5917         }    
5918         if (this.striped) {
5919             cfg.cls += ' table-striped';
5920         }
5921         
5922         if (this.hover) {
5923             cfg.cls += ' table-hover';
5924         }
5925         if (this.bordered) {
5926             cfg.cls += ' table-bordered';
5927         }
5928         if (this.condensed) {
5929             cfg.cls += ' table-condensed';
5930         }
5931         if (this.responsive) {
5932             cfg.cls += ' table-responsive';
5933         }
5934         
5935         if (this.cls) {
5936             cfg.cls+=  ' ' +this.cls;
5937         }
5938         
5939         // this lot should be simplifed...
5940         
5941         if (this.align) {
5942             cfg.align=this.align;
5943         }
5944         if (this.bgcolor) {
5945             cfg.bgcolor=this.bgcolor;
5946         }
5947         if (this.border) {
5948             cfg.border=this.border;
5949         }
5950         if (this.cellpadding) {
5951             cfg.cellpadding=this.cellpadding;
5952         }
5953         if (this.cellspacing) {
5954             cfg.cellspacing=this.cellspacing;
5955         }
5956         if (this.frame) {
5957             cfg.frame=this.frame;
5958         }
5959         if (this.rules) {
5960             cfg.rules=this.rules;
5961         }
5962         if (this.sortable) {
5963             cfg.sortable=this.sortable;
5964         }
5965         if (this.summary) {
5966             cfg.summary=this.summary;
5967         }
5968         if (this.width) {
5969             cfg.width=this.width;
5970         }
5971         if (this.layout) {
5972             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5973         }
5974         
5975         if(this.store || this.cm){
5976             if(this.headerShow){
5977                 cfg.cn.push(this.renderHeader());
5978             }
5979             
5980             cfg.cn.push(this.renderBody());
5981             
5982             if(this.footerShow){
5983                 cfg.cn.push(this.renderFooter());
5984             }
5985             // where does this come from?
5986             //cfg.cls+=  ' TableGrid';
5987         }
5988         
5989         return { cn : [ cfg ] };
5990     },
5991     
5992     initEvents : function()
5993     {   
5994         if(!this.store || !this.cm){
5995             return;
5996         }
5997         if (this.selModel) {
5998             this.selModel.initEvents();
5999         }
6000         
6001         
6002         //Roo.log('initEvents with ds!!!!');
6003         
6004         this.mainBody = this.el.select('tbody', true).first();
6005         this.mainHead = this.el.select('thead', true).first();
6006         
6007         
6008         
6009         
6010         var _this = this;
6011         
6012         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6013             e.on('click', _this.sort, _this);
6014         });
6015         
6016         this.mainBody.on("click", this.onClick, this);
6017         this.mainBody.on("dblclick", this.onDblClick, this);
6018         
6019         // why is this done????? = it breaks dialogs??
6020         //this.parent().el.setStyle('position', 'relative');
6021         
6022         
6023         if (this.footer) {
6024             this.footer.parentId = this.id;
6025             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
6026         } 
6027         
6028         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6029         
6030         this.store.on('load', this.onLoad, this);
6031         this.store.on('beforeload', this.onBeforeLoad, this);
6032         this.store.on('update', this.onUpdate, this);
6033         this.store.on('add', this.onAdd, this);
6034         this.store.on("clear", this.clear, this);
6035         
6036         this.el.on("contextmenu", this.onContextMenu, this);
6037         
6038         this.mainBody.on('scroll', this.onBodyScroll, this);
6039         
6040         
6041     },
6042     
6043     onContextMenu : function(e, t)
6044     {
6045         this.processEvent("contextmenu", e);
6046     },
6047     
6048     processEvent : function(name, e)
6049     {
6050         if (name != 'touchstart' ) {
6051             this.fireEvent(name, e);    
6052         }
6053         
6054         var t = e.getTarget();
6055         
6056         var cell = Roo.get(t);
6057         
6058         if(!cell){
6059             return;
6060         }
6061         
6062         if(cell.findParent('tfoot', false, true)){
6063             return;
6064         }
6065         
6066         if(cell.findParent('thead', false, true)){
6067             
6068             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6069                 cell = Roo.get(t).findParent('th', false, true);
6070                 if (!cell) {
6071                     Roo.log("failed to find th in thead?");
6072                     Roo.log(e.getTarget());
6073                     return;
6074                 }
6075             }
6076             
6077             var cellIndex = cell.dom.cellIndex;
6078             
6079             var ename = name == 'touchstart' ? 'click' : name;
6080             this.fireEvent("header" + ename, this, cellIndex, e);
6081             
6082             return;
6083         }
6084         
6085         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6086             cell = Roo.get(t).findParent('td', false, true);
6087             if (!cell) {
6088                 Roo.log("failed to find th in tbody?");
6089                 Roo.log(e.getTarget());
6090                 return;
6091             }
6092         }
6093         
6094         var row = cell.findParent('tr', false, true);
6095         var cellIndex = cell.dom.cellIndex;
6096         var rowIndex = row.dom.rowIndex - 1;
6097         
6098         if(row !== false){
6099             
6100             this.fireEvent("row" + name, this, rowIndex, e);
6101             
6102             if(cell !== false){
6103             
6104                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6105             }
6106         }
6107         
6108     },
6109     
6110     onMouseover : function(e, el)
6111     {
6112         var cell = Roo.get(el);
6113         
6114         if(!cell){
6115             return;
6116         }
6117         
6118         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6119             cell = cell.findParent('td', false, true);
6120         }
6121         
6122         var row = cell.findParent('tr', false, true);
6123         var cellIndex = cell.dom.cellIndex;
6124         var rowIndex = row.dom.rowIndex - 1; // start from 0
6125         
6126         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6127         
6128     },
6129     
6130     onMouseout : function(e, el)
6131     {
6132         var cell = Roo.get(el);
6133         
6134         if(!cell){
6135             return;
6136         }
6137         
6138         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6139             cell = cell.findParent('td', false, true);
6140         }
6141         
6142         var row = cell.findParent('tr', false, true);
6143         var cellIndex = cell.dom.cellIndex;
6144         var rowIndex = row.dom.rowIndex - 1; // start from 0
6145         
6146         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6147         
6148     },
6149     
6150     onClick : function(e, el)
6151     {
6152         var cell = Roo.get(el);
6153         
6154         if(!cell || (!this.cellSelection && !this.rowSelection)){
6155             return;
6156         }
6157         
6158         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6159             cell = cell.findParent('td', false, true);
6160         }
6161         
6162         if(!cell || typeof(cell) == 'undefined'){
6163             return;
6164         }
6165         
6166         var row = cell.findParent('tr', false, true);
6167         
6168         if(!row || typeof(row) == 'undefined'){
6169             return;
6170         }
6171         
6172         var cellIndex = cell.dom.cellIndex;
6173         var rowIndex = this.getRowIndex(row);
6174         
6175         // why??? - should these not be based on SelectionModel?
6176         if(this.cellSelection){
6177             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6178         }
6179         
6180         if(this.rowSelection){
6181             this.fireEvent('rowclick', this, row, rowIndex, e);
6182         }
6183         
6184         
6185     },
6186         
6187     onDblClick : function(e,el)
6188     {
6189         var cell = Roo.get(el);
6190         
6191         if(!cell || (!this.cellSelection && !this.rowSelection)){
6192             return;
6193         }
6194         
6195         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6196             cell = cell.findParent('td', false, true);
6197         }
6198         
6199         if(!cell || typeof(cell) == 'undefined'){
6200             return;
6201         }
6202         
6203         var row = cell.findParent('tr', false, true);
6204         
6205         if(!row || typeof(row) == 'undefined'){
6206             return;
6207         }
6208         
6209         var cellIndex = cell.dom.cellIndex;
6210         var rowIndex = this.getRowIndex(row);
6211         
6212         if(this.cellSelection){
6213             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6214         }
6215         
6216         if(this.rowSelection){
6217             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6218         }
6219     },
6220     
6221     sort : function(e,el)
6222     {
6223         var col = Roo.get(el);
6224         
6225         if(!col.hasClass('sortable')){
6226             return;
6227         }
6228         
6229         var sort = col.attr('sort');
6230         var dir = 'ASC';
6231         
6232         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6233             dir = 'DESC';
6234         }
6235         
6236         this.store.sortInfo = {field : sort, direction : dir};
6237         
6238         if (this.footer) {
6239             Roo.log("calling footer first");
6240             this.footer.onClick('first');
6241         } else {
6242         
6243             this.store.load({ params : { start : 0 } });
6244         }
6245     },
6246     
6247     renderHeader : function()
6248     {
6249         var header = {
6250             tag: 'thead',
6251             cn : []
6252         };
6253         
6254         var cm = this.cm;
6255         this.totalWidth = 0;
6256         
6257         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6258             
6259             var config = cm.config[i];
6260             
6261             var c = {
6262                 tag: 'th',
6263                 style : '',
6264                 html: cm.getColumnHeader(i)
6265             };
6266             
6267             var hh = '';
6268             
6269             if(typeof(config.sortable) != 'undefined' && config.sortable){
6270                 c.cls = 'sortable';
6271                 c.html = '<i class="glyphicon"></i>' + c.html;
6272             }
6273             
6274             if(typeof(config.lgHeader) != 'undefined'){
6275                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6276             }
6277             
6278             if(typeof(config.mdHeader) != 'undefined'){
6279                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6280             }
6281             
6282             if(typeof(config.smHeader) != 'undefined'){
6283                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6284             }
6285             
6286             if(typeof(config.xsHeader) != 'undefined'){
6287                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6288             }
6289             
6290             if(hh.length){
6291                 c.html = hh;
6292             }
6293             
6294             if(typeof(config.tooltip) != 'undefined'){
6295                 c.tooltip = config.tooltip;
6296             }
6297             
6298             if(typeof(config.colspan) != 'undefined'){
6299                 c.colspan = config.colspan;
6300             }
6301             
6302             if(typeof(config.hidden) != 'undefined' && config.hidden){
6303                 c.style += ' display:none;';
6304             }
6305             
6306             if(typeof(config.dataIndex) != 'undefined'){
6307                 c.sort = config.dataIndex;
6308             }
6309             
6310            
6311             
6312             if(typeof(config.align) != 'undefined' && config.align.length){
6313                 c.style += ' text-align:' + config.align + ';';
6314             }
6315             
6316             if(typeof(config.width) != 'undefined'){
6317                 c.style += ' width:' + config.width + 'px;';
6318                 this.totalWidth += config.width;
6319             } else {
6320                 this.totalWidth += 100; // assume minimum of 100 per column?
6321             }
6322             
6323             if(typeof(config.cls) != 'undefined'){
6324                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6325             }
6326             
6327             ['xs','sm','md','lg'].map(function(size){
6328                 
6329                 if(typeof(config[size]) == 'undefined'){
6330                     return;
6331                 }
6332                 
6333                 if (!config[size]) { // 0 = hidden
6334                     c.cls += ' hidden-' + size;
6335                     return;
6336                 }
6337                 
6338                 c.cls += ' col-' + size + '-' + config[size];
6339
6340             });
6341             
6342             header.cn.push(c)
6343         }
6344         
6345         return header;
6346     },
6347     
6348     renderBody : function()
6349     {
6350         var body = {
6351             tag: 'tbody',
6352             cn : [
6353                 {
6354                     tag: 'tr',
6355                     cn : [
6356                         {
6357                             tag : 'td',
6358                             colspan :  this.cm.getColumnCount()
6359                         }
6360                     ]
6361                 }
6362             ]
6363         };
6364         
6365         return body;
6366     },
6367     
6368     renderFooter : function()
6369     {
6370         var footer = {
6371             tag: 'tfoot',
6372             cn : [
6373                 {
6374                     tag: 'tr',
6375                     cn : [
6376                         {
6377                             tag : 'td',
6378                             colspan :  this.cm.getColumnCount()
6379                         }
6380                     ]
6381                 }
6382             ]
6383         };
6384         
6385         return footer;
6386     },
6387     
6388     
6389     
6390     onLoad : function()
6391     {
6392 //        Roo.log('ds onload');
6393         this.clear();
6394         
6395         var _this = this;
6396         var cm = this.cm;
6397         var ds = this.store;
6398         
6399         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6400             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6401             if (_this.store.sortInfo) {
6402                     
6403                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6404                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6405                 }
6406                 
6407                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6408                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6409                 }
6410             }
6411         });
6412         
6413         var tbody =  this.mainBody;
6414               
6415         if(ds.getCount() > 0){
6416             ds.data.each(function(d,rowIndex){
6417                 var row =  this.renderRow(cm, ds, rowIndex);
6418                 
6419                 tbody.createChild(row);
6420                 
6421                 var _this = this;
6422                 
6423                 if(row.cellObjects.length){
6424                     Roo.each(row.cellObjects, function(r){
6425                         _this.renderCellObject(r);
6426                     })
6427                 }
6428                 
6429             }, this);
6430         }
6431         
6432         Roo.each(this.el.select('tbody td', true).elements, function(e){
6433             e.on('mouseover', _this.onMouseover, _this);
6434         });
6435         
6436         Roo.each(this.el.select('tbody td', true).elements, function(e){
6437             e.on('mouseout', _this.onMouseout, _this);
6438         });
6439         this.fireEvent('rowsrendered', this);
6440         //if(this.loadMask){
6441         //    this.maskEl.hide();
6442         //}
6443         
6444         this.autoSize();
6445     },
6446     
6447     
6448     onUpdate : function(ds,record)
6449     {
6450         this.refreshRow(record);
6451         this.autoSize();
6452     },
6453     
6454     onRemove : function(ds, record, index, isUpdate){
6455         if(isUpdate !== true){
6456             this.fireEvent("beforerowremoved", this, index, record);
6457         }
6458         var bt = this.mainBody.dom;
6459         
6460         var rows = this.el.select('tbody > tr', true).elements;
6461         
6462         if(typeof(rows[index]) != 'undefined'){
6463             bt.removeChild(rows[index].dom);
6464         }
6465         
6466 //        if(bt.rows[index]){
6467 //            bt.removeChild(bt.rows[index]);
6468 //        }
6469         
6470         if(isUpdate !== true){
6471             //this.stripeRows(index);
6472             //this.syncRowHeights(index, index);
6473             //this.layout();
6474             this.fireEvent("rowremoved", this, index, record);
6475         }
6476     },
6477     
6478     onAdd : function(ds, records, rowIndex)
6479     {
6480         //Roo.log('on Add called');
6481         // - note this does not handle multiple adding very well..
6482         var bt = this.mainBody.dom;
6483         for (var i =0 ; i < records.length;i++) {
6484             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6485             //Roo.log(records[i]);
6486             //Roo.log(this.store.getAt(rowIndex+i));
6487             this.insertRow(this.store, rowIndex + i, false);
6488             return;
6489         }
6490         
6491     },
6492     
6493     
6494     refreshRow : function(record){
6495         var ds = this.store, index;
6496         if(typeof record == 'number'){
6497             index = record;
6498             record = ds.getAt(index);
6499         }else{
6500             index = ds.indexOf(record);
6501         }
6502         this.insertRow(ds, index, true);
6503         this.autoSize();
6504         this.onRemove(ds, record, index+1, true);
6505         this.autoSize();
6506         //this.syncRowHeights(index, index);
6507         //this.layout();
6508         this.fireEvent("rowupdated", this, index, record);
6509     },
6510     
6511     insertRow : function(dm, rowIndex, isUpdate){
6512         
6513         if(!isUpdate){
6514             this.fireEvent("beforerowsinserted", this, rowIndex);
6515         }
6516             //var s = this.getScrollState();
6517         var row = this.renderRow(this.cm, this.store, rowIndex);
6518         // insert before rowIndex..
6519         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6520         
6521         var _this = this;
6522                 
6523         if(row.cellObjects.length){
6524             Roo.each(row.cellObjects, function(r){
6525                 _this.renderCellObject(r);
6526             })
6527         }
6528             
6529         if(!isUpdate){
6530             this.fireEvent("rowsinserted", this, rowIndex);
6531             //this.syncRowHeights(firstRow, lastRow);
6532             //this.stripeRows(firstRow);
6533             //this.layout();
6534         }
6535         
6536     },
6537     
6538     
6539     getRowDom : function(rowIndex)
6540     {
6541         var rows = this.el.select('tbody > tr', true).elements;
6542         
6543         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6544         
6545     },
6546     // returns the object tree for a tr..
6547   
6548     
6549     renderRow : function(cm, ds, rowIndex) 
6550     {
6551         
6552         var d = ds.getAt(rowIndex);
6553         
6554         var row = {
6555             tag : 'tr',
6556             cn : []
6557         };
6558             
6559         var cellObjects = [];
6560         
6561         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6562             var config = cm.config[i];
6563             
6564             var renderer = cm.getRenderer(i);
6565             var value = '';
6566             var id = false;
6567             
6568             if(typeof(renderer) !== 'undefined'){
6569                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6570             }
6571             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6572             // and are rendered into the cells after the row is rendered - using the id for the element.
6573             
6574             if(typeof(value) === 'object'){
6575                 id = Roo.id();
6576                 cellObjects.push({
6577                     container : id,
6578                     cfg : value 
6579                 })
6580             }
6581             
6582             var rowcfg = {
6583                 record: d,
6584                 rowIndex : rowIndex,
6585                 colIndex : i,
6586                 rowClass : ''
6587             };
6588
6589             this.fireEvent('rowclass', this, rowcfg);
6590             
6591             var td = {
6592                 tag: 'td',
6593                 cls : rowcfg.rowClass,
6594                 style: '',
6595                 html: (typeof(value) === 'object') ? '' : value
6596             };
6597             
6598             if (id) {
6599                 td.id = id;
6600             }
6601             
6602             if(typeof(config.colspan) != 'undefined'){
6603                 td.colspan = config.colspan;
6604             }
6605             
6606             if(typeof(config.hidden) != 'undefined' && config.hidden){
6607                 td.style += ' display:none;';
6608             }
6609             
6610             if(typeof(config.align) != 'undefined' && config.align.length){
6611                 td.style += ' text-align:' + config.align + ';';
6612             }
6613             
6614             if(typeof(config.width) != 'undefined'){
6615                 td.style += ' width:' +  config.width + 'px;';
6616             }
6617             
6618             if(typeof(config.cursor) != 'undefined'){
6619                 td.style += ' cursor:' +  config.cursor + ';';
6620             }
6621             
6622             if(typeof(config.cls) != 'undefined'){
6623                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6624             }
6625             
6626             ['xs','sm','md','lg'].map(function(size){
6627                 
6628                 if(typeof(config[size]) == 'undefined'){
6629                     return;
6630                 }
6631                 
6632                 if (!config[size]) { // 0 = hidden
6633                     td.cls += ' hidden-' + size;
6634                     return;
6635                 }
6636                 
6637                 td.cls += ' col-' + size + '-' + config[size];
6638
6639             });
6640              
6641             row.cn.push(td);
6642            
6643         }
6644         
6645         row.cellObjects = cellObjects;
6646         
6647         return row;
6648           
6649     },
6650     
6651     
6652     
6653     onBeforeLoad : function()
6654     {
6655         //Roo.log('ds onBeforeLoad');
6656         
6657         //this.clear();
6658         
6659         //if(this.loadMask){
6660         //    this.maskEl.show();
6661         //}
6662     },
6663      /**
6664      * Remove all rows
6665      */
6666     clear : function()
6667     {
6668         this.el.select('tbody', true).first().dom.innerHTML = '';
6669     },
6670     /**
6671      * Show or hide a row.
6672      * @param {Number} rowIndex to show or hide
6673      * @param {Boolean} state hide
6674      */
6675     setRowVisibility : function(rowIndex, state)
6676     {
6677         var bt = this.mainBody.dom;
6678         
6679         var rows = this.el.select('tbody > tr', true).elements;
6680         
6681         if(typeof(rows[rowIndex]) == 'undefined'){
6682             return;
6683         }
6684         rows[rowIndex].dom.style.display = state ? '' : 'none';
6685     },
6686     
6687     
6688     getSelectionModel : function(){
6689         if(!this.selModel){
6690             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6691         }
6692         return this.selModel;
6693     },
6694     /*
6695      * Render the Roo.bootstrap object from renderder
6696      */
6697     renderCellObject : function(r)
6698     {
6699         var _this = this;
6700         
6701         var t = r.cfg.render(r.container);
6702         
6703         if(r.cfg.cn){
6704             Roo.each(r.cfg.cn, function(c){
6705                 var child = {
6706                     container: t.getChildContainer(),
6707                     cfg: c
6708                 };
6709                 _this.renderCellObject(child);
6710             })
6711         }
6712     },
6713     
6714     getRowIndex : function(row)
6715     {
6716         var rowIndex = -1;
6717         
6718         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6719             if(el != row){
6720                 return;
6721             }
6722             
6723             rowIndex = index;
6724         });
6725         
6726         return rowIndex;
6727     },
6728      /**
6729      * Returns the grid's underlying element = used by panel.Grid
6730      * @return {Element} The element
6731      */
6732     getGridEl : function(){
6733         return this.el;
6734     },
6735      /**
6736      * Forces a resize - used by panel.Grid
6737      * @return {Element} The element
6738      */
6739     autoSize : function()
6740     {
6741         //var ctr = Roo.get(this.container.dom.parentElement);
6742         var ctr = Roo.get(this.el.dom);
6743         
6744         var thd = this.getGridEl().select('thead',true).first();
6745         var tbd = this.getGridEl().select('tbody', true).first();
6746         var tfd = this.getGridEl().select('tfoot', true).first();
6747         
6748         var cw = ctr.getWidth();
6749         
6750         if (tbd) {
6751             
6752             tbd.setSize(ctr.getWidth(),
6753                         ctr.getHeight() - (thd.getHeight() + (tfd ? tfd.getHeight() : 0))
6754             );
6755             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6756             cw -= barsize;
6757         }
6758         cw = Math.max(cw, this.totalWidth);
6759         this.getGridEl().select('tr',true).setWidth(cw);
6760         // resize 'expandable coloumn?
6761         
6762         return; // we doe not have a view in this design..
6763         
6764     },
6765     onBodyScroll: function()
6766     {
6767         
6768         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6769         this.mainHead.setStyle({
6770                     'position' : 'relative',
6771                     'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6772         });
6773         
6774         
6775     }
6776 });
6777
6778  
6779
6780  /*
6781  * - LGPL
6782  *
6783  * table cell
6784  * 
6785  */
6786
6787 /**
6788  * @class Roo.bootstrap.TableCell
6789  * @extends Roo.bootstrap.Component
6790  * Bootstrap TableCell class
6791  * @cfg {String} html cell contain text
6792  * @cfg {String} cls cell class
6793  * @cfg {String} tag cell tag (td|th) default td
6794  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6795  * @cfg {String} align Aligns the content in a cell
6796  * @cfg {String} axis Categorizes cells
6797  * @cfg {String} bgcolor Specifies the background color of a cell
6798  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6799  * @cfg {Number} colspan Specifies the number of columns a cell should span
6800  * @cfg {String} headers Specifies one or more header cells a cell is related to
6801  * @cfg {Number} height Sets the height of a cell
6802  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6803  * @cfg {Number} rowspan Sets the number of rows a cell should span
6804  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6805  * @cfg {String} valign Vertical aligns the content in a cell
6806  * @cfg {Number} width Specifies the width of a cell
6807  * 
6808  * @constructor
6809  * Create a new TableCell
6810  * @param {Object} config The config object
6811  */
6812
6813 Roo.bootstrap.TableCell = function(config){
6814     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6815 };
6816
6817 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6818     
6819     html: false,
6820     cls: false,
6821     tag: false,
6822     abbr: false,
6823     align: false,
6824     axis: false,
6825     bgcolor: false,
6826     charoff: false,
6827     colspan: false,
6828     headers: false,
6829     height: false,
6830     nowrap: false,
6831     rowspan: false,
6832     scope: false,
6833     valign: false,
6834     width: false,
6835     
6836     
6837     getAutoCreate : function(){
6838         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6839         
6840         cfg = {
6841             tag: 'td'
6842         };
6843         
6844         if(this.tag){
6845             cfg.tag = this.tag;
6846         }
6847         
6848         if (this.html) {
6849             cfg.html=this.html
6850         }
6851         if (this.cls) {
6852             cfg.cls=this.cls
6853         }
6854         if (this.abbr) {
6855             cfg.abbr=this.abbr
6856         }
6857         if (this.align) {
6858             cfg.align=this.align
6859         }
6860         if (this.axis) {
6861             cfg.axis=this.axis
6862         }
6863         if (this.bgcolor) {
6864             cfg.bgcolor=this.bgcolor
6865         }
6866         if (this.charoff) {
6867             cfg.charoff=this.charoff
6868         }
6869         if (this.colspan) {
6870             cfg.colspan=this.colspan
6871         }
6872         if (this.headers) {
6873             cfg.headers=this.headers
6874         }
6875         if (this.height) {
6876             cfg.height=this.height
6877         }
6878         if (this.nowrap) {
6879             cfg.nowrap=this.nowrap
6880         }
6881         if (this.rowspan) {
6882             cfg.rowspan=this.rowspan
6883         }
6884         if (this.scope) {
6885             cfg.scope=this.scope
6886         }
6887         if (this.valign) {
6888             cfg.valign=this.valign
6889         }
6890         if (this.width) {
6891             cfg.width=this.width
6892         }
6893         
6894         
6895         return cfg;
6896     }
6897    
6898 });
6899
6900  
6901
6902  /*
6903  * - LGPL
6904  *
6905  * table row
6906  * 
6907  */
6908
6909 /**
6910  * @class Roo.bootstrap.TableRow
6911  * @extends Roo.bootstrap.Component
6912  * Bootstrap TableRow class
6913  * @cfg {String} cls row class
6914  * @cfg {String} align Aligns the content in a table row
6915  * @cfg {String} bgcolor Specifies a background color for a table row
6916  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6917  * @cfg {String} valign Vertical aligns the content in a table row
6918  * 
6919  * @constructor
6920  * Create a new TableRow
6921  * @param {Object} config The config object
6922  */
6923
6924 Roo.bootstrap.TableRow = function(config){
6925     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6926 };
6927
6928 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6929     
6930     cls: false,
6931     align: false,
6932     bgcolor: false,
6933     charoff: false,
6934     valign: false,
6935     
6936     getAutoCreate : function(){
6937         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6938         
6939         cfg = {
6940             tag: 'tr'
6941         };
6942             
6943         if(this.cls){
6944             cfg.cls = this.cls;
6945         }
6946         if(this.align){
6947             cfg.align = this.align;
6948         }
6949         if(this.bgcolor){
6950             cfg.bgcolor = this.bgcolor;
6951         }
6952         if(this.charoff){
6953             cfg.charoff = this.charoff;
6954         }
6955         if(this.valign){
6956             cfg.valign = this.valign;
6957         }
6958         
6959         return cfg;
6960     }
6961    
6962 });
6963
6964  
6965
6966  /*
6967  * - LGPL
6968  *
6969  * table body
6970  * 
6971  */
6972
6973 /**
6974  * @class Roo.bootstrap.TableBody
6975  * @extends Roo.bootstrap.Component
6976  * Bootstrap TableBody class
6977  * @cfg {String} cls element class
6978  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6979  * @cfg {String} align Aligns the content inside the element
6980  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6981  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6982  * 
6983  * @constructor
6984  * Create a new TableBody
6985  * @param {Object} config The config object
6986  */
6987
6988 Roo.bootstrap.TableBody = function(config){
6989     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6990 };
6991
6992 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6993     
6994     cls: false,
6995     tag: false,
6996     align: false,
6997     charoff: false,
6998     valign: false,
6999     
7000     getAutoCreate : function(){
7001         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7002         
7003         cfg = {
7004             tag: 'tbody'
7005         };
7006             
7007         if (this.cls) {
7008             cfg.cls=this.cls
7009         }
7010         if(this.tag){
7011             cfg.tag = this.tag;
7012         }
7013         
7014         if(this.align){
7015             cfg.align = this.align;
7016         }
7017         if(this.charoff){
7018             cfg.charoff = this.charoff;
7019         }
7020         if(this.valign){
7021             cfg.valign = this.valign;
7022         }
7023         
7024         return cfg;
7025     }
7026     
7027     
7028 //    initEvents : function()
7029 //    {
7030 //        
7031 //        if(!this.store){
7032 //            return;
7033 //        }
7034 //        
7035 //        this.store = Roo.factory(this.store, Roo.data);
7036 //        this.store.on('load', this.onLoad, this);
7037 //        
7038 //        this.store.load();
7039 //        
7040 //    },
7041 //    
7042 //    onLoad: function () 
7043 //    {   
7044 //        this.fireEvent('load', this);
7045 //    }
7046 //    
7047 //   
7048 });
7049
7050  
7051
7052  /*
7053  * Based on:
7054  * Ext JS Library 1.1.1
7055  * Copyright(c) 2006-2007, Ext JS, LLC.
7056  *
7057  * Originally Released Under LGPL - original licence link has changed is not relivant.
7058  *
7059  * Fork - LGPL
7060  * <script type="text/javascript">
7061  */
7062
7063 // as we use this in bootstrap.
7064 Roo.namespace('Roo.form');
7065  /**
7066  * @class Roo.form.Action
7067  * Internal Class used to handle form actions
7068  * @constructor
7069  * @param {Roo.form.BasicForm} el The form element or its id
7070  * @param {Object} config Configuration options
7071  */
7072
7073  
7074  
7075 // define the action interface
7076 Roo.form.Action = function(form, options){
7077     this.form = form;
7078     this.options = options || {};
7079 };
7080 /**
7081  * Client Validation Failed
7082  * @const 
7083  */
7084 Roo.form.Action.CLIENT_INVALID = 'client';
7085 /**
7086  * Server Validation Failed
7087  * @const 
7088  */
7089 Roo.form.Action.SERVER_INVALID = 'server';
7090  /**
7091  * Connect to Server Failed
7092  * @const 
7093  */
7094 Roo.form.Action.CONNECT_FAILURE = 'connect';
7095 /**
7096  * Reading Data from Server Failed
7097  * @const 
7098  */
7099 Roo.form.Action.LOAD_FAILURE = 'load';
7100
7101 Roo.form.Action.prototype = {
7102     type : 'default',
7103     failureType : undefined,
7104     response : undefined,
7105     result : undefined,
7106
7107     // interface method
7108     run : function(options){
7109
7110     },
7111
7112     // interface method
7113     success : function(response){
7114
7115     },
7116
7117     // interface method
7118     handleResponse : function(response){
7119
7120     },
7121
7122     // default connection failure
7123     failure : function(response){
7124         
7125         this.response = response;
7126         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7127         this.form.afterAction(this, false);
7128     },
7129
7130     processResponse : function(response){
7131         this.response = response;
7132         if(!response.responseText){
7133             return true;
7134         }
7135         this.result = this.handleResponse(response);
7136         return this.result;
7137     },
7138
7139     // utility functions used internally
7140     getUrl : function(appendParams){
7141         var url = this.options.url || this.form.url || this.form.el.dom.action;
7142         if(appendParams){
7143             var p = this.getParams();
7144             if(p){
7145                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7146             }
7147         }
7148         return url;
7149     },
7150
7151     getMethod : function(){
7152         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7153     },
7154
7155     getParams : function(){
7156         var bp = this.form.baseParams;
7157         var p = this.options.params;
7158         if(p){
7159             if(typeof p == "object"){
7160                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7161             }else if(typeof p == 'string' && bp){
7162                 p += '&' + Roo.urlEncode(bp);
7163             }
7164         }else if(bp){
7165             p = Roo.urlEncode(bp);
7166         }
7167         return p;
7168     },
7169
7170     createCallback : function(){
7171         return {
7172             success: this.success,
7173             failure: this.failure,
7174             scope: this,
7175             timeout: (this.form.timeout*1000),
7176             upload: this.form.fileUpload ? this.success : undefined
7177         };
7178     }
7179 };
7180
7181 Roo.form.Action.Submit = function(form, options){
7182     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7183 };
7184
7185 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7186     type : 'submit',
7187
7188     haveProgress : false,
7189     uploadComplete : false,
7190     
7191     // uploadProgress indicator.
7192     uploadProgress : function()
7193     {
7194         if (!this.form.progressUrl) {
7195             return;
7196         }
7197         
7198         if (!this.haveProgress) {
7199             Roo.MessageBox.progress("Uploading", "Uploading");
7200         }
7201         if (this.uploadComplete) {
7202            Roo.MessageBox.hide();
7203            return;
7204         }
7205         
7206         this.haveProgress = true;
7207    
7208         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7209         
7210         var c = new Roo.data.Connection();
7211         c.request({
7212             url : this.form.progressUrl,
7213             params: {
7214                 id : uid
7215             },
7216             method: 'GET',
7217             success : function(req){
7218                //console.log(data);
7219                 var rdata = false;
7220                 var edata;
7221                 try  {
7222                    rdata = Roo.decode(req.responseText)
7223                 } catch (e) {
7224                     Roo.log("Invalid data from server..");
7225                     Roo.log(edata);
7226                     return;
7227                 }
7228                 if (!rdata || !rdata.success) {
7229                     Roo.log(rdata);
7230                     Roo.MessageBox.alert(Roo.encode(rdata));
7231                     return;
7232                 }
7233                 var data = rdata.data;
7234                 
7235                 if (this.uploadComplete) {
7236                    Roo.MessageBox.hide();
7237                    return;
7238                 }
7239                    
7240                 if (data){
7241                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7242                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7243                     );
7244                 }
7245                 this.uploadProgress.defer(2000,this);
7246             },
7247        
7248             failure: function(data) {
7249                 Roo.log('progress url failed ');
7250                 Roo.log(data);
7251             },
7252             scope : this
7253         });
7254            
7255     },
7256     
7257     
7258     run : function()
7259     {
7260         // run get Values on the form, so it syncs any secondary forms.
7261         this.form.getValues();
7262         
7263         var o = this.options;
7264         var method = this.getMethod();
7265         var isPost = method == 'POST';
7266         if(o.clientValidation === false || this.form.isValid()){
7267             
7268             if (this.form.progressUrl) {
7269                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7270                     (new Date() * 1) + '' + Math.random());
7271                     
7272             } 
7273             
7274             
7275             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7276                 form:this.form.el.dom,
7277                 url:this.getUrl(!isPost),
7278                 method: method,
7279                 params:isPost ? this.getParams() : null,
7280                 isUpload: this.form.fileUpload
7281             }));
7282             
7283             this.uploadProgress();
7284
7285         }else if (o.clientValidation !== false){ // client validation failed
7286             this.failureType = Roo.form.Action.CLIENT_INVALID;
7287             this.form.afterAction(this, false);
7288         }
7289     },
7290
7291     success : function(response)
7292     {
7293         this.uploadComplete= true;
7294         if (this.haveProgress) {
7295             Roo.MessageBox.hide();
7296         }
7297         
7298         
7299         var result = this.processResponse(response);
7300         if(result === true || result.success){
7301             this.form.afterAction(this, true);
7302             return;
7303         }
7304         if(result.errors){
7305             this.form.markInvalid(result.errors);
7306             this.failureType = Roo.form.Action.SERVER_INVALID;
7307         }
7308         this.form.afterAction(this, false);
7309     },
7310     failure : function(response)
7311     {
7312         this.uploadComplete= true;
7313         if (this.haveProgress) {
7314             Roo.MessageBox.hide();
7315         }
7316         
7317         this.response = response;
7318         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7319         this.form.afterAction(this, false);
7320     },
7321     
7322     handleResponse : function(response){
7323         if(this.form.errorReader){
7324             var rs = this.form.errorReader.read(response);
7325             var errors = [];
7326             if(rs.records){
7327                 for(var i = 0, len = rs.records.length; i < len; i++) {
7328                     var r = rs.records[i];
7329                     errors[i] = r.data;
7330                 }
7331             }
7332             if(errors.length < 1){
7333                 errors = null;
7334             }
7335             return {
7336                 success : rs.success,
7337                 errors : errors
7338             };
7339         }
7340         var ret = false;
7341         try {
7342             ret = Roo.decode(response.responseText);
7343         } catch (e) {
7344             ret = {
7345                 success: false,
7346                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7347                 errors : []
7348             };
7349         }
7350         return ret;
7351         
7352     }
7353 });
7354
7355
7356 Roo.form.Action.Load = function(form, options){
7357     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7358     this.reader = this.form.reader;
7359 };
7360
7361 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7362     type : 'load',
7363
7364     run : function(){
7365         
7366         Roo.Ajax.request(Roo.apply(
7367                 this.createCallback(), {
7368                     method:this.getMethod(),
7369                     url:this.getUrl(false),
7370                     params:this.getParams()
7371         }));
7372     },
7373
7374     success : function(response){
7375         
7376         var result = this.processResponse(response);
7377         if(result === true || !result.success || !result.data){
7378             this.failureType = Roo.form.Action.LOAD_FAILURE;
7379             this.form.afterAction(this, false);
7380             return;
7381         }
7382         this.form.clearInvalid();
7383         this.form.setValues(result.data);
7384         this.form.afterAction(this, true);
7385     },
7386
7387     handleResponse : function(response){
7388         if(this.form.reader){
7389             var rs = this.form.reader.read(response);
7390             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7391             return {
7392                 success : rs.success,
7393                 data : data
7394             };
7395         }
7396         return Roo.decode(response.responseText);
7397     }
7398 });
7399
7400 Roo.form.Action.ACTION_TYPES = {
7401     'load' : Roo.form.Action.Load,
7402     'submit' : Roo.form.Action.Submit
7403 };/*
7404  * - LGPL
7405  *
7406  * form
7407  *
7408  */
7409
7410 /**
7411  * @class Roo.bootstrap.Form
7412  * @extends Roo.bootstrap.Component
7413  * Bootstrap Form class
7414  * @cfg {String} method  GET | POST (default POST)
7415  * @cfg {String} labelAlign top | left (default top)
7416  * @cfg {String} align left  | right - for navbars
7417  * @cfg {Boolean} loadMask load mask when submit (default true)
7418
7419  *
7420  * @constructor
7421  * Create a new Form
7422  * @param {Object} config The config object
7423  */
7424
7425
7426 Roo.bootstrap.Form = function(config){
7427     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7428     this.addEvents({
7429         /**
7430          * @event clientvalidation
7431          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7432          * @param {Form} this
7433          * @param {Boolean} valid true if the form has passed client-side validation
7434          */
7435         clientvalidation: true,
7436         /**
7437          * @event beforeaction
7438          * Fires before any action is performed. Return false to cancel the action.
7439          * @param {Form} this
7440          * @param {Action} action The action to be performed
7441          */
7442         beforeaction: true,
7443         /**
7444          * @event actionfailed
7445          * Fires when an action fails.
7446          * @param {Form} this
7447          * @param {Action} action The action that failed
7448          */
7449         actionfailed : true,
7450         /**
7451          * @event actioncomplete
7452          * Fires when an action is completed.
7453          * @param {Form} this
7454          * @param {Action} action The action that completed
7455          */
7456         actioncomplete : true
7457     });
7458
7459 };
7460
7461 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7462
7463      /**
7464      * @cfg {String} method
7465      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7466      */
7467     method : 'POST',
7468     /**
7469      * @cfg {String} url
7470      * The URL to use for form actions if one isn't supplied in the action options.
7471      */
7472     /**
7473      * @cfg {Boolean} fileUpload
7474      * Set to true if this form is a file upload.
7475      */
7476
7477     /**
7478      * @cfg {Object} baseParams
7479      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7480      */
7481
7482     /**
7483      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7484      */
7485     timeout: 30,
7486     /**
7487      * @cfg {Sting} align (left|right) for navbar forms
7488      */
7489     align : 'left',
7490
7491     // private
7492     activeAction : null,
7493
7494     /**
7495      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7496      * element by passing it or its id or mask the form itself by passing in true.
7497      * @type Mixed
7498      */
7499     waitMsgTarget : false,
7500
7501     loadMask : true,
7502
7503     getAutoCreate : function(){
7504
7505         var cfg = {
7506             tag: 'form',
7507             method : this.method || 'POST',
7508             id : this.id || Roo.id(),
7509             cls : ''
7510         };
7511         if (this.parent().xtype.match(/^Nav/)) {
7512             cfg.cls = 'navbar-form navbar-' + this.align;
7513
7514         }
7515
7516         if (this.labelAlign == 'left' ) {
7517             cfg.cls += ' form-horizontal';
7518         }
7519
7520
7521         return cfg;
7522     },
7523     initEvents : function()
7524     {
7525         this.el.on('submit', this.onSubmit, this);
7526         // this was added as random key presses on the form where triggering form submit.
7527         this.el.on('keypress', function(e) {
7528             if (e.getCharCode() != 13) {
7529                 return true;
7530             }
7531             // we might need to allow it for textareas.. and some other items.
7532             // check e.getTarget().
7533
7534             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7535                 return true;
7536             }
7537
7538             Roo.log("keypress blocked");
7539
7540             e.preventDefault();
7541             return false;
7542         });
7543
7544     },
7545     // private
7546     onSubmit : function(e){
7547         e.stopEvent();
7548     },
7549
7550      /**
7551      * Returns true if client-side validation on the form is successful.
7552      * @return Boolean
7553      */
7554     isValid : function(){
7555         var items = this.getItems();
7556         var valid = true;
7557         items.each(function(f){
7558            if(!f.validate()){
7559                valid = false;
7560
7561            }
7562         });
7563         return valid;
7564     },
7565     /**
7566      * Returns true if any fields in this form have changed since their original load.
7567      * @return Boolean
7568      */
7569     isDirty : function(){
7570         var dirty = false;
7571         var items = this.getItems();
7572         items.each(function(f){
7573            if(f.isDirty()){
7574                dirty = true;
7575                return false;
7576            }
7577            return true;
7578         });
7579         return dirty;
7580     },
7581      /**
7582      * Performs a predefined action (submit or load) or custom actions you define on this form.
7583      * @param {String} actionName The name of the action type
7584      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7585      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7586      * accept other config options):
7587      * <pre>
7588 Property          Type             Description
7589 ----------------  ---------------  ----------------------------------------------------------------------------------
7590 url               String           The url for the action (defaults to the form's url)
7591 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7592 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7593 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7594                                    validate the form on the client (defaults to false)
7595      * </pre>
7596      * @return {BasicForm} this
7597      */
7598     doAction : function(action, options){
7599         if(typeof action == 'string'){
7600             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7601         }
7602         if(this.fireEvent('beforeaction', this, action) !== false){
7603             this.beforeAction(action);
7604             action.run.defer(100, action);
7605         }
7606         return this;
7607     },
7608
7609     // private
7610     beforeAction : function(action){
7611         var o = action.options;
7612
7613         if(this.loadMask){
7614             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7615         }
7616         // not really supported yet.. ??
7617
7618         //if(this.waitMsgTarget === true){
7619         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7620         //}else if(this.waitMsgTarget){
7621         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7622         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7623         //}else {
7624         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7625        // }
7626
7627     },
7628
7629     // private
7630     afterAction : function(action, success){
7631         this.activeAction = null;
7632         var o = action.options;
7633
7634         //if(this.waitMsgTarget === true){
7635             this.el.unmask();
7636         //}else if(this.waitMsgTarget){
7637         //    this.waitMsgTarget.unmask();
7638         //}else{
7639         //    Roo.MessageBox.updateProgress(1);
7640         //    Roo.MessageBox.hide();
7641        // }
7642         //
7643         if(success){
7644             if(o.reset){
7645                 this.reset();
7646             }
7647             Roo.callback(o.success, o.scope, [this, action]);
7648             this.fireEvent('actioncomplete', this, action);
7649
7650         }else{
7651
7652             // failure condition..
7653             // we have a scenario where updates need confirming.
7654             // eg. if a locking scenario exists..
7655             // we look for { errors : { needs_confirm : true }} in the response.
7656             if (
7657                 (typeof(action.result) != 'undefined')  &&
7658                 (typeof(action.result.errors) != 'undefined')  &&
7659                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7660            ){
7661                 var _t = this;
7662                 Roo.log("not supported yet");
7663                  /*
7664
7665                 Roo.MessageBox.confirm(
7666                     "Change requires confirmation",
7667                     action.result.errorMsg,
7668                     function(r) {
7669                         if (r != 'yes') {
7670                             return;
7671                         }
7672                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7673                     }
7674
7675                 );
7676                 */
7677
7678
7679                 return;
7680             }
7681
7682             Roo.callback(o.failure, o.scope, [this, action]);
7683             // show an error message if no failed handler is set..
7684             if (!this.hasListener('actionfailed')) {
7685                 Roo.log("need to add dialog support");
7686                 /*
7687                 Roo.MessageBox.alert("Error",
7688                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7689                         action.result.errorMsg :
7690                         "Saving Failed, please check your entries or try again"
7691                 );
7692                 */
7693             }
7694
7695             this.fireEvent('actionfailed', this, action);
7696         }
7697
7698     },
7699     /**
7700      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7701      * @param {String} id The value to search for
7702      * @return Field
7703      */
7704     findField : function(id){
7705         var items = this.getItems();
7706         var field = items.get(id);
7707         if(!field){
7708              items.each(function(f){
7709                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7710                     field = f;
7711                     return false;
7712                 }
7713                 return true;
7714             });
7715         }
7716         return field || null;
7717     },
7718      /**
7719      * Mark fields in this form invalid in bulk.
7720      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7721      * @return {BasicForm} this
7722      */
7723     markInvalid : function(errors){
7724         if(errors instanceof Array){
7725             for(var i = 0, len = errors.length; i < len; i++){
7726                 var fieldError = errors[i];
7727                 var f = this.findField(fieldError.id);
7728                 if(f){
7729                     f.markInvalid(fieldError.msg);
7730                 }
7731             }
7732         }else{
7733             var field, id;
7734             for(id in errors){
7735                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7736                     field.markInvalid(errors[id]);
7737                 }
7738             }
7739         }
7740         //Roo.each(this.childForms || [], function (f) {
7741         //    f.markInvalid(errors);
7742         //});
7743
7744         return this;
7745     },
7746
7747     /**
7748      * Set values for fields in this form in bulk.
7749      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7750      * @return {BasicForm} this
7751      */
7752     setValues : function(values){
7753         if(values instanceof Array){ // array of objects
7754             for(var i = 0, len = values.length; i < len; i++){
7755                 var v = values[i];
7756                 var f = this.findField(v.id);
7757                 if(f){
7758                     f.setValue(v.value);
7759                     if(this.trackResetOnLoad){
7760                         f.originalValue = f.getValue();
7761                     }
7762                 }
7763             }
7764         }else{ // object hash
7765             var field, id;
7766             for(id in values){
7767                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7768
7769                     if (field.setFromData &&
7770                         field.valueField &&
7771                         field.displayField &&
7772                         // combos' with local stores can
7773                         // be queried via setValue()
7774                         // to set their value..
7775                         (field.store && !field.store.isLocal)
7776                         ) {
7777                         // it's a combo
7778                         var sd = { };
7779                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7780                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7781                         field.setFromData(sd);
7782
7783                     } else {
7784                         field.setValue(values[id]);
7785                     }
7786
7787
7788                     if(this.trackResetOnLoad){
7789                         field.originalValue = field.getValue();
7790                     }
7791                 }
7792             }
7793         }
7794
7795         //Roo.each(this.childForms || [], function (f) {
7796         //    f.setValues(values);
7797         //});
7798
7799         return this;
7800     },
7801
7802     /**
7803      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7804      * they are returned as an array.
7805      * @param {Boolean} asString
7806      * @return {Object}
7807      */
7808     getValues : function(asString){
7809         //if (this.childForms) {
7810             // copy values from the child forms
7811         //    Roo.each(this.childForms, function (f) {
7812         //        this.setValues(f.getValues());
7813         //    }, this);
7814         //}
7815
7816
7817
7818         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7819         if(asString === true){
7820             return fs;
7821         }
7822         return Roo.urlDecode(fs);
7823     },
7824
7825     /**
7826      * Returns the fields in this form as an object with key/value pairs.
7827      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7828      * @return {Object}
7829      */
7830     getFieldValues : function(with_hidden)
7831     {
7832         var items = this.getItems();
7833         var ret = {};
7834         items.each(function(f){
7835             if (!f.getName()) {
7836                 return;
7837             }
7838             var v = f.getValue();
7839             if (f.inputType =='radio') {
7840                 if (typeof(ret[f.getName()]) == 'undefined') {
7841                     ret[f.getName()] = ''; // empty..
7842                 }
7843
7844                 if (!f.el.dom.checked) {
7845                     return;
7846
7847                 }
7848                 v = f.el.dom.value;
7849
7850             }
7851
7852             // not sure if this supported any more..
7853             if ((typeof(v) == 'object') && f.getRawValue) {
7854                 v = f.getRawValue() ; // dates..
7855             }
7856             // combo boxes where name != hiddenName...
7857             if (f.name != f.getName()) {
7858                 ret[f.name] = f.getRawValue();
7859             }
7860             ret[f.getName()] = v;
7861         });
7862
7863         return ret;
7864     },
7865
7866     /**
7867      * Clears all invalid messages in this form.
7868      * @return {BasicForm} this
7869      */
7870     clearInvalid : function(){
7871         var items = this.getItems();
7872
7873         items.each(function(f){
7874            f.clearInvalid();
7875         });
7876
7877
7878
7879         return this;
7880     },
7881
7882     /**
7883      * Resets this form.
7884      * @return {BasicForm} this
7885      */
7886     reset : function(){
7887         var items = this.getItems();
7888         items.each(function(f){
7889             f.reset();
7890         });
7891
7892         Roo.each(this.childForms || [], function (f) {
7893             f.reset();
7894         });
7895
7896
7897         return this;
7898     },
7899     getItems : function()
7900     {
7901         var r=new Roo.util.MixedCollection(false, function(o){
7902             return o.id || (o.id = Roo.id());
7903         });
7904         var iter = function(el) {
7905             if (el.inputEl) {
7906                 r.add(el);
7907             }
7908             if (!el.items) {
7909                 return;
7910             }
7911             Roo.each(el.items,function(e) {
7912                 iter(e);
7913             });
7914
7915
7916         };
7917
7918         iter(this);
7919         return r;
7920
7921
7922
7923
7924     }
7925
7926 });
7927 /*
7928  * Based on:
7929  * Ext JS Library 1.1.1
7930  * Copyright(c) 2006-2007, Ext JS, LLC.
7931  *
7932  * Originally Released Under LGPL - original licence link has changed is not relivant.
7933  *
7934  * Fork - LGPL
7935  * <script type="text/javascript">
7936  */
7937 /**
7938  * @class Roo.form.VTypes
7939  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7940  * @singleton
7941  */
7942 Roo.form.VTypes = function(){
7943     // closure these in so they are only created once.
7944     var alpha = /^[a-zA-Z_]+$/;
7945     var alphanum = /^[a-zA-Z0-9_]+$/;
7946     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7947     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7948
7949     // All these messages and functions are configurable
7950     return {
7951         /**
7952          * The function used to validate email addresses
7953          * @param {String} value The email address
7954          */
7955         'email' : function(v){
7956             return email.test(v);
7957         },
7958         /**
7959          * The error text to display when the email validation function returns false
7960          * @type String
7961          */
7962         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7963         /**
7964          * The keystroke filter mask to be applied on email input
7965          * @type RegExp
7966          */
7967         'emailMask' : /[a-z0-9_\.\-@]/i,
7968
7969         /**
7970          * The function used to validate URLs
7971          * @param {String} value The URL
7972          */
7973         'url' : function(v){
7974             return url.test(v);
7975         },
7976         /**
7977          * The error text to display when the url validation function returns false
7978          * @type String
7979          */
7980         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7981         
7982         /**
7983          * The function used to validate alpha values
7984          * @param {String} value The value
7985          */
7986         'alpha' : function(v){
7987             return alpha.test(v);
7988         },
7989         /**
7990          * The error text to display when the alpha validation function returns false
7991          * @type String
7992          */
7993         'alphaText' : 'This field should only contain letters and _',
7994         /**
7995          * The keystroke filter mask to be applied on alpha input
7996          * @type RegExp
7997          */
7998         'alphaMask' : /[a-z_]/i,
7999
8000         /**
8001          * The function used to validate alphanumeric values
8002          * @param {String} value The value
8003          */
8004         'alphanum' : function(v){
8005             return alphanum.test(v);
8006         },
8007         /**
8008          * The error text to display when the alphanumeric validation function returns false
8009          * @type String
8010          */
8011         'alphanumText' : 'This field should only contain letters, numbers and _',
8012         /**
8013          * The keystroke filter mask to be applied on alphanumeric input
8014          * @type RegExp
8015          */
8016         'alphanumMask' : /[a-z0-9_]/i
8017     };
8018 }();/*
8019  * - LGPL
8020  *
8021  * Input
8022  * 
8023  */
8024
8025 /**
8026  * @class Roo.bootstrap.Input
8027  * @extends Roo.bootstrap.Component
8028  * Bootstrap Input class
8029  * @cfg {Boolean} disabled is it disabled
8030  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8031  * @cfg {String} name name of the input
8032  * @cfg {string} fieldLabel - the label associated
8033  * @cfg {string} placeholder - placeholder to put in text.
8034  * @cfg {string}  before - input group add on before
8035  * @cfg {string} after - input group add on after
8036  * @cfg {string} size - (lg|sm) or leave empty..
8037  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8038  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8039  * @cfg {Number} md colspan out of 12 for computer-sized screens
8040  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8041  * @cfg {string} value default value of the input
8042  * @cfg {Number} labelWidth set the width of label (0-12)
8043  * @cfg {String} labelAlign (top|left)
8044  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8045  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8046  * @cfg {String} indicatorpos (left|right) default left
8047
8048  * @cfg {String} align (left|center|right) Default left
8049  * @cfg {Boolean} forceFeedback (true|false) Default false
8050  * 
8051  * 
8052  * 
8053  * 
8054  * @constructor
8055  * Create a new Input
8056  * @param {Object} config The config object
8057  */
8058
8059 Roo.bootstrap.Input = function(config){
8060     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8061    
8062         this.addEvents({
8063             /**
8064              * @event focus
8065              * Fires when this field receives input focus.
8066              * @param {Roo.form.Field} this
8067              */
8068             focus : true,
8069             /**
8070              * @event blur
8071              * Fires when this field loses input focus.
8072              * @param {Roo.form.Field} this
8073              */
8074             blur : true,
8075             /**
8076              * @event specialkey
8077              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8078              * {@link Roo.EventObject#getKey} to determine which key was pressed.
8079              * @param {Roo.form.Field} this
8080              * @param {Roo.EventObject} e The event object
8081              */
8082             specialkey : true,
8083             /**
8084              * @event change
8085              * Fires just before the field blurs if the field value has changed.
8086              * @param {Roo.form.Field} this
8087              * @param {Mixed} newValue The new value
8088              * @param {Mixed} oldValue The original value
8089              */
8090             change : true,
8091             /**
8092              * @event invalid
8093              * Fires after the field has been marked as invalid.
8094              * @param {Roo.form.Field} this
8095              * @param {String} msg The validation message
8096              */
8097             invalid : true,
8098             /**
8099              * @event valid
8100              * Fires after the field has been validated with no errors.
8101              * @param {Roo.form.Field} this
8102              */
8103             valid : true,
8104              /**
8105              * @event keyup
8106              * Fires after the key up
8107              * @param {Roo.form.Field} this
8108              * @param {Roo.EventObject}  e The event Object
8109              */
8110             keyup : true
8111         });
8112 };
8113
8114 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8115      /**
8116      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8117       automatic validation (defaults to "keyup").
8118      */
8119     validationEvent : "keyup",
8120      /**
8121      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8122      */
8123     validateOnBlur : true,
8124     /**
8125      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8126      */
8127     validationDelay : 250,
8128      /**
8129      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8130      */
8131     focusClass : "x-form-focus",  // not needed???
8132     
8133        
8134     /**
8135      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8136      */
8137     invalidClass : "has-warning",
8138     
8139     /**
8140      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8141      */
8142     validClass : "has-success",
8143     
8144     /**
8145      * @cfg {Boolean} hasFeedback (true|false) default true
8146      */
8147     hasFeedback : true,
8148     
8149     /**
8150      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8151      */
8152     invalidFeedbackClass : "glyphicon-warning-sign",
8153     
8154     /**
8155      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8156      */
8157     validFeedbackClass : "glyphicon-ok",
8158     
8159     /**
8160      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8161      */
8162     selectOnFocus : false,
8163     
8164      /**
8165      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8166      */
8167     maskRe : null,
8168        /**
8169      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8170      */
8171     vtype : null,
8172     
8173       /**
8174      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8175      */
8176     disableKeyFilter : false,
8177     
8178        /**
8179      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8180      */
8181     disabled : false,
8182      /**
8183      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8184      */
8185     allowBlank : true,
8186     /**
8187      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8188      */
8189     blankText : "This field is required",
8190     
8191      /**
8192      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8193      */
8194     minLength : 0,
8195     /**
8196      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8197      */
8198     maxLength : Number.MAX_VALUE,
8199     /**
8200      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8201      */
8202     minLengthText : "The minimum length for this field is {0}",
8203     /**
8204      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8205      */
8206     maxLengthText : "The maximum length for this field is {0}",
8207   
8208     
8209     /**
8210      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8211      * If available, this function will be called only after the basic validators all return true, and will be passed the
8212      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8213      */
8214     validator : null,
8215     /**
8216      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8217      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8218      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8219      */
8220     regex : null,
8221     /**
8222      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8223      */
8224     regexText : "",
8225     
8226     autocomplete: false,
8227     
8228     
8229     fieldLabel : '',
8230     inputType : 'text',
8231     
8232     name : false,
8233     placeholder: false,
8234     before : false,
8235     after : false,
8236     size : false,
8237     hasFocus : false,
8238     preventMark: false,
8239     isFormField : true,
8240     value : '',
8241     labelWidth : 2,
8242     labelAlign : false,
8243     readOnly : false,
8244     align : false,
8245     formatedValue : false,
8246     forceFeedback : false,
8247     
8248     indicatorpos : 'left',
8249     
8250     parentLabelAlign : function()
8251     {
8252         var parent = this;
8253         while (parent.parent()) {
8254             parent = parent.parent();
8255             if (typeof(parent.labelAlign) !='undefined') {
8256                 return parent.labelAlign;
8257             }
8258         }
8259         return 'left';
8260         
8261     },
8262     
8263     getAutoCreate : function()
8264     {
8265         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8266         
8267         var id = Roo.id();
8268         
8269         var cfg = {};
8270         
8271         if(this.inputType != 'hidden'){
8272             cfg.cls = 'form-group' //input-group
8273         }
8274         
8275         var input =  {
8276             tag: 'input',
8277             id : id,
8278             type : this.inputType,
8279             value : this.value,
8280             cls : 'form-control',
8281             placeholder : this.placeholder || '',
8282             autocomplete : this.autocomplete || 'new-password'
8283         };
8284         
8285         if(this.align){
8286             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8287         }
8288         
8289         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8290             input.maxLength = this.maxLength;
8291         }
8292         
8293         if (this.disabled) {
8294             input.disabled=true;
8295         }
8296         
8297         if (this.readOnly) {
8298             input.readonly=true;
8299         }
8300         
8301         if (this.name) {
8302             input.name = this.name;
8303         }
8304         
8305         if (this.size) {
8306             input.cls += ' input-' + this.size;
8307         }
8308         
8309         var settings=this;
8310         ['xs','sm','md','lg'].map(function(size){
8311             if (settings[size]) {
8312                 cfg.cls += ' col-' + size + '-' + settings[size];
8313             }
8314         });
8315         
8316         var inputblock = input;
8317         
8318         var feedback = {
8319             tag: 'span',
8320             cls: 'glyphicon form-control-feedback'
8321         };
8322             
8323         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8324             
8325             inputblock = {
8326                 cls : 'has-feedback',
8327                 cn :  [
8328                     input,
8329                     feedback
8330                 ] 
8331             };  
8332         }
8333         
8334         if (this.before || this.after) {
8335             
8336             inputblock = {
8337                 cls : 'input-group',
8338                 cn :  [] 
8339             };
8340             
8341             if (this.before && typeof(this.before) == 'string') {
8342                 
8343                 inputblock.cn.push({
8344                     tag :'span',
8345                     cls : 'roo-input-before input-group-addon',
8346                     html : this.before
8347                 });
8348             }
8349             if (this.before && typeof(this.before) == 'object') {
8350                 this.before = Roo.factory(this.before);
8351                 
8352                 inputblock.cn.push({
8353                     tag :'span',
8354                     cls : 'roo-input-before input-group-' +
8355                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8356                 });
8357             }
8358             
8359             inputblock.cn.push(input);
8360             
8361             if (this.after && typeof(this.after) == 'string') {
8362                 inputblock.cn.push({
8363                     tag :'span',
8364                     cls : 'roo-input-after input-group-addon',
8365                     html : this.after
8366                 });
8367             }
8368             if (this.after && typeof(this.after) == 'object') {
8369                 this.after = Roo.factory(this.after);
8370                 
8371                 inputblock.cn.push({
8372                     tag :'span',
8373                     cls : 'roo-input-after input-group-' +
8374                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8375                 });
8376             }
8377             
8378             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8379                 inputblock.cls += ' has-feedback';
8380                 inputblock.cn.push(feedback);
8381             }
8382         };
8383         
8384         if (align ==='left' && this.fieldLabel.length) {
8385             
8386             cfg.cn = [
8387                 {
8388                     tag : 'i',
8389                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8390                     tooltip : 'This field is required'
8391                 },
8392                 {
8393                     tag: 'label',
8394                     'for' :  id,
8395                     cls : 'control-label col-sm-' + this.labelWidth,
8396                     html : this.fieldLabel
8397
8398                 },
8399                 {
8400                     cls : "col-sm-" + (12 - this.labelWidth), 
8401                     cn: [
8402                         inputblock
8403                     ]
8404                 }
8405
8406             ];
8407             
8408             if(this.indicatorpos == 'right'){
8409                 cfg.cn = [
8410                     {
8411                         tag: 'label',
8412                         'for' :  id,
8413                         cls : 'control-label col-sm-' + this.labelWidth,
8414                         html : this.fieldLabel
8415
8416                     },
8417                     {
8418                         tag : 'i',
8419                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8420                         tooltip : 'This field is required'
8421                     },
8422                     {
8423                         cls : "col-sm-" + (12 - this.labelWidth), 
8424                         cn: [
8425                             inputblock
8426                         ]
8427                     }
8428
8429                 ];
8430             }
8431             
8432         } else if ( this.fieldLabel.length) {
8433                 
8434             cfg.cn = [
8435                 {
8436                     tag : 'i',
8437                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8438                     tooltip : 'This field is required'
8439                 },
8440                 {
8441                     tag: 'label',
8442                    //cls : 'input-group-addon',
8443                     html : this.fieldLabel
8444
8445                 },
8446
8447                inputblock
8448
8449            ];
8450            
8451            if(this.indicatorpos == 'right'){
8452                 
8453                 cfg.cn = [
8454                     {
8455                         tag: 'label',
8456                        //cls : 'input-group-addon',
8457                         html : this.fieldLabel
8458
8459                     },
8460                     {
8461                         tag : 'i',
8462                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8463                         tooltip : 'This field is required'
8464                     },
8465
8466                    inputblock
8467
8468                ];
8469
8470             }
8471
8472         } else {
8473             
8474             cfg.cn = [
8475
8476                     inputblock
8477
8478             ];
8479                 
8480                 
8481         };
8482         
8483         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8484            cfg.cls += ' navbar-form';
8485         }
8486         
8487         if (this.parentType === 'NavGroup') {
8488            cfg.cls += ' navbar-form';
8489            cfg.tag = 'li';
8490         }
8491         
8492         return cfg;
8493         
8494     },
8495     /**
8496      * return the real input element.
8497      */
8498     inputEl: function ()
8499     {
8500         return this.el.select('input.form-control',true).first();
8501     },
8502     
8503     tooltipEl : function()
8504     {
8505         return this.inputEl();
8506     },
8507     
8508     indicatorEl : function()
8509     {
8510         var indicator = this.el.select('i.roo-required-indicator',true).first();
8511         
8512         if(!indicator){
8513             return false;
8514         }
8515         
8516         return indicator;
8517         
8518     },
8519     
8520     setDisabled : function(v)
8521     {
8522         var i  = this.inputEl().dom;
8523         if (!v) {
8524             i.removeAttribute('disabled');
8525             return;
8526             
8527         }
8528         i.setAttribute('disabled','true');
8529     },
8530     initEvents : function()
8531     {
8532           
8533         this.inputEl().on("keydown" , this.fireKey,  this);
8534         this.inputEl().on("focus", this.onFocus,  this);
8535         this.inputEl().on("blur", this.onBlur,  this);
8536         
8537         this.inputEl().relayEvent('keyup', this);
8538         
8539         this.indicator = this.indicatorEl();
8540         
8541         if(this.indicator){
8542             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8543             this.indicator.hide();
8544         }
8545  
8546         // reference to original value for reset
8547         this.originalValue = this.getValue();
8548         //Roo.form.TextField.superclass.initEvents.call(this);
8549         if(this.validationEvent == 'keyup'){
8550             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8551             this.inputEl().on('keyup', this.filterValidation, this);
8552         }
8553         else if(this.validationEvent !== false){
8554             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8555         }
8556         
8557         if(this.selectOnFocus){
8558             this.on("focus", this.preFocus, this);
8559             
8560         }
8561         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8562             this.inputEl().on("keypress", this.filterKeys, this);
8563         } else {
8564             this.inputEl().relayEvent('keypress', this);
8565         }
8566        /* if(this.grow){
8567             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8568             this.el.on("click", this.autoSize,  this);
8569         }
8570         */
8571         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8572             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8573         }
8574         
8575         if (typeof(this.before) == 'object') {
8576             this.before.render(this.el.select('.roo-input-before',true).first());
8577         }
8578         if (typeof(this.after) == 'object') {
8579             this.after.render(this.el.select('.roo-input-after',true).first());
8580         }
8581         
8582         
8583     },
8584     filterValidation : function(e){
8585         if(!e.isNavKeyPress()){
8586             this.validationTask.delay(this.validationDelay);
8587         }
8588     },
8589      /**
8590      * Validates the field value
8591      * @return {Boolean} True if the value is valid, else false
8592      */
8593     validate : function(){
8594         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8595         if(this.disabled || this.validateValue(this.getRawValue())){
8596             this.markValid();
8597             return true;
8598         }
8599         
8600         this.markInvalid();
8601         return false;
8602     },
8603     
8604     
8605     /**
8606      * Validates a value according to the field's validation rules and marks the field as invalid
8607      * if the validation fails
8608      * @param {Mixed} value The value to validate
8609      * @return {Boolean} True if the value is valid, else false
8610      */
8611     validateValue : function(value){
8612         if(value.length < 1)  { // if it's blank
8613             if(this.allowBlank){
8614                 return true;
8615             }
8616             return false;
8617         }
8618         
8619         if(value.length < this.minLength){
8620             return false;
8621         }
8622         if(value.length > this.maxLength){
8623             return false;
8624         }
8625         if(this.vtype){
8626             var vt = Roo.form.VTypes;
8627             if(!vt[this.vtype](value, this)){
8628                 return false;
8629             }
8630         }
8631         if(typeof this.validator == "function"){
8632             var msg = this.validator(value);
8633             if(msg !== true){
8634                 return false;
8635             }
8636         }
8637         
8638         if(this.regex && !this.regex.test(value)){
8639             return false;
8640         }
8641         
8642         return true;
8643     },
8644
8645     
8646     
8647      // private
8648     fireKey : function(e){
8649         //Roo.log('field ' + e.getKey());
8650         if(e.isNavKeyPress()){
8651             this.fireEvent("specialkey", this, e);
8652         }
8653     },
8654     focus : function (selectText){
8655         if(this.rendered){
8656             this.inputEl().focus();
8657             if(selectText === true){
8658                 this.inputEl().dom.select();
8659             }
8660         }
8661         return this;
8662     } ,
8663     
8664     onFocus : function(){
8665         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8666            // this.el.addClass(this.focusClass);
8667         }
8668         if(!this.hasFocus){
8669             this.hasFocus = true;
8670             this.startValue = this.getValue();
8671             this.fireEvent("focus", this);
8672         }
8673     },
8674     
8675     beforeBlur : Roo.emptyFn,
8676
8677     
8678     // private
8679     onBlur : function(){
8680         this.beforeBlur();
8681         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8682             //this.el.removeClass(this.focusClass);
8683         }
8684         this.hasFocus = false;
8685         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8686             this.validate();
8687         }
8688         var v = this.getValue();
8689         if(String(v) !== String(this.startValue)){
8690             this.fireEvent('change', this, v, this.startValue);
8691         }
8692         this.fireEvent("blur", this);
8693     },
8694     
8695     /**
8696      * Resets the current field value to the originally loaded value and clears any validation messages
8697      */
8698     reset : function(){
8699         this.setValue(this.originalValue);
8700         this.validate();
8701     },
8702      /**
8703      * Returns the name of the field
8704      * @return {Mixed} name The name field
8705      */
8706     getName: function(){
8707         return this.name;
8708     },
8709      /**
8710      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8711      * @return {Mixed} value The field value
8712      */
8713     getValue : function(){
8714         
8715         var v = this.inputEl().getValue();
8716         
8717         return v;
8718     },
8719     /**
8720      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8721      * @return {Mixed} value The field value
8722      */
8723     getRawValue : function(){
8724         var v = this.inputEl().getValue();
8725         
8726         return v;
8727     },
8728     
8729     /**
8730      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8731      * @param {Mixed} value The value to set
8732      */
8733     setRawValue : function(v){
8734         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8735     },
8736     
8737     selectText : function(start, end){
8738         var v = this.getRawValue();
8739         if(v.length > 0){
8740             start = start === undefined ? 0 : start;
8741             end = end === undefined ? v.length : end;
8742             var d = this.inputEl().dom;
8743             if(d.setSelectionRange){
8744                 d.setSelectionRange(start, end);
8745             }else if(d.createTextRange){
8746                 var range = d.createTextRange();
8747                 range.moveStart("character", start);
8748                 range.moveEnd("character", v.length-end);
8749                 range.select();
8750             }
8751         }
8752     },
8753     
8754     /**
8755      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8756      * @param {Mixed} value The value to set
8757      */
8758     setValue : function(v){
8759         this.value = v;
8760         if(this.rendered){
8761             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8762             this.validate();
8763         }
8764     },
8765     
8766     /*
8767     processValue : function(value){
8768         if(this.stripCharsRe){
8769             var newValue = value.replace(this.stripCharsRe, '');
8770             if(newValue !== value){
8771                 this.setRawValue(newValue);
8772                 return newValue;
8773             }
8774         }
8775         return value;
8776     },
8777   */
8778     preFocus : function(){
8779         
8780         if(this.selectOnFocus){
8781             this.inputEl().dom.select();
8782         }
8783     },
8784     filterKeys : function(e){
8785         var k = e.getKey();
8786         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8787             return;
8788         }
8789         var c = e.getCharCode(), cc = String.fromCharCode(c);
8790         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8791             return;
8792         }
8793         if(!this.maskRe.test(cc)){
8794             e.stopEvent();
8795         }
8796     },
8797      /**
8798      * Clear any invalid styles/messages for this field
8799      */
8800     clearInvalid : function(){
8801         
8802         if(!this.el || this.preventMark){ // not rendered
8803             return;
8804         }
8805         
8806         if(this.indicator){
8807             this.indicator.hide();
8808         }
8809         
8810         this.el.removeClass(this.invalidClass);
8811         
8812         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8813             
8814             var feedback = this.el.select('.form-control-feedback', true).first();
8815             
8816             if(feedback){
8817                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8818             }
8819             
8820         }
8821         
8822         this.fireEvent('valid', this);
8823     },
8824     
8825      /**
8826      * Mark this field as valid
8827      */
8828     markValid : function()
8829     {
8830         if(!this.el  || this.preventMark){ // not rendered
8831             return;
8832         }
8833         
8834         this.el.removeClass([this.invalidClass, this.validClass]);
8835         
8836         var feedback = this.el.select('.form-control-feedback', true).first();
8837             
8838         if(feedback){
8839             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8840         }
8841
8842         if(this.disabled || this.allowBlank){
8843             return;
8844         }
8845         
8846         if(this.indicator){
8847             this.indicator.hide();
8848         }
8849         
8850         this.el.addClass(this.validClass);
8851         
8852         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8853             
8854             var feedback = this.el.select('.form-control-feedback', true).first();
8855             
8856             if(feedback){
8857                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8858                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8859             }
8860             
8861         }
8862         
8863         this.fireEvent('valid', this);
8864     },
8865     
8866      /**
8867      * Mark this field as invalid
8868      * @param {String} msg The validation message
8869      */
8870     markInvalid : function(msg)
8871     {
8872         if(!this.el  || this.preventMark){ // not rendered
8873             return;
8874         }
8875         
8876         this.el.removeClass([this.invalidClass, this.validClass]);
8877         
8878         var feedback = this.el.select('.form-control-feedback', true).first();
8879             
8880         if(feedback){
8881             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8882         }
8883
8884         if(this.disabled || this.allowBlank){
8885             return;
8886         }
8887         
8888         if(this.indicator){
8889             this.indicator.show();
8890         }
8891         
8892         this.el.addClass(this.invalidClass);
8893         
8894         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8895             
8896             var feedback = this.el.select('.form-control-feedback', true).first();
8897             
8898             if(feedback){
8899                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8900                 
8901                 if(this.getValue().length || this.forceFeedback){
8902                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8903                 }
8904                 
8905             }
8906             
8907         }
8908         
8909         this.fireEvent('invalid', this, msg);
8910     },
8911     // private
8912     SafariOnKeyDown : function(event)
8913     {
8914         // this is a workaround for a password hang bug on chrome/ webkit.
8915         if (this.inputEl().dom.type != 'password') {
8916             return;
8917         }
8918         
8919         var isSelectAll = false;
8920         
8921         if(this.inputEl().dom.selectionEnd > 0){
8922             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8923         }
8924         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8925             event.preventDefault();
8926             this.setValue('');
8927             return;
8928         }
8929         
8930         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
8931             
8932             event.preventDefault();
8933             // this is very hacky as keydown always get's upper case.
8934             //
8935             var cc = String.fromCharCode(event.getCharCode());
8936             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8937             
8938         }
8939     },
8940     adjustWidth : function(tag, w){
8941         tag = tag.toLowerCase();
8942         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8943             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8944                 if(tag == 'input'){
8945                     return w + 2;
8946                 }
8947                 if(tag == 'textarea'){
8948                     return w-2;
8949                 }
8950             }else if(Roo.isOpera){
8951                 if(tag == 'input'){
8952                     return w + 2;
8953                 }
8954                 if(tag == 'textarea'){
8955                     return w-2;
8956                 }
8957             }
8958         }
8959         return w;
8960     }
8961     
8962 });
8963
8964  
8965 /*
8966  * - LGPL
8967  *
8968  * Input
8969  * 
8970  */
8971
8972 /**
8973  * @class Roo.bootstrap.TextArea
8974  * @extends Roo.bootstrap.Input
8975  * Bootstrap TextArea class
8976  * @cfg {Number} cols Specifies the visible width of a text area
8977  * @cfg {Number} rows Specifies the visible number of lines in a text area
8978  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8979  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8980  * @cfg {string} html text
8981  * 
8982  * @constructor
8983  * Create a new TextArea
8984  * @param {Object} config The config object
8985  */
8986
8987 Roo.bootstrap.TextArea = function(config){
8988     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8989    
8990 };
8991
8992 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8993      
8994     cols : false,
8995     rows : 5,
8996     readOnly : false,
8997     warp : 'soft',
8998     resize : false,
8999     value: false,
9000     html: false,
9001     
9002     getAutoCreate : function(){
9003         
9004         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9005         
9006         var id = Roo.id();
9007         
9008         var cfg = {};
9009         
9010         var input =  {
9011             tag: 'textarea',
9012             id : id,
9013             warp : this.warp,
9014             rows : this.rows,
9015             value : this.value || '',
9016             html: this.html || '',
9017             cls : 'form-control',
9018             placeholder : this.placeholder || '' 
9019             
9020         };
9021         
9022         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9023             input.maxLength = this.maxLength;
9024         }
9025         
9026         if(this.resize){
9027             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9028         }
9029         
9030         if(this.cols){
9031             input.cols = this.cols;
9032         }
9033         
9034         if (this.readOnly) {
9035             input.readonly = true;
9036         }
9037         
9038         if (this.name) {
9039             input.name = this.name;
9040         }
9041         
9042         if (this.size) {
9043             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9044         }
9045         
9046         var settings=this;
9047         ['xs','sm','md','lg'].map(function(size){
9048             if (settings[size]) {
9049                 cfg.cls += ' col-' + size + '-' + settings[size];
9050             }
9051         });
9052         
9053         var inputblock = input;
9054         
9055         if(this.hasFeedback && !this.allowBlank){
9056             
9057             var feedback = {
9058                 tag: 'span',
9059                 cls: 'glyphicon form-control-feedback'
9060             };
9061
9062             inputblock = {
9063                 cls : 'has-feedback',
9064                 cn :  [
9065                     input,
9066                     feedback
9067                 ] 
9068             };  
9069         }
9070         
9071         
9072         if (this.before || this.after) {
9073             
9074             inputblock = {
9075                 cls : 'input-group',
9076                 cn :  [] 
9077             };
9078             if (this.before) {
9079                 inputblock.cn.push({
9080                     tag :'span',
9081                     cls : 'input-group-addon',
9082                     html : this.before
9083                 });
9084             }
9085             
9086             inputblock.cn.push(input);
9087             
9088             if(this.hasFeedback && !this.allowBlank){
9089                 inputblock.cls += ' has-feedback';
9090                 inputblock.cn.push(feedback);
9091             }
9092             
9093             if (this.after) {
9094                 inputblock.cn.push({
9095                     tag :'span',
9096                     cls : 'input-group-addon',
9097                     html : this.after
9098                 });
9099             }
9100             
9101         }
9102         
9103         if (align ==='left' && this.fieldLabel.length) {
9104 //                Roo.log("left and has label");
9105                 cfg.cn = [
9106                     
9107                     {
9108                         tag: 'label',
9109                         'for' :  id,
9110                         cls : 'control-label col-sm-' + this.labelWidth,
9111                         html : this.fieldLabel
9112                         
9113                     },
9114                     {
9115                         cls : "col-sm-" + (12 - this.labelWidth), 
9116                         cn: [
9117                             inputblock
9118                         ]
9119                     }
9120                     
9121                 ];
9122         } else if ( this.fieldLabel.length) {
9123 //                Roo.log(" label");
9124                  cfg.cn = [
9125                    
9126                     {
9127                         tag: 'label',
9128                         //cls : 'input-group-addon',
9129                         html : this.fieldLabel
9130                         
9131                     },
9132                     
9133                     inputblock
9134                     
9135                 ];
9136
9137         } else {
9138             
9139 //                   Roo.log(" no label && no align");
9140                 cfg.cn = [
9141                     
9142                         inputblock
9143                     
9144                 ];
9145                 
9146                 
9147         }
9148         
9149         if (this.disabled) {
9150             input.disabled=true;
9151         }
9152         
9153         return cfg;
9154         
9155     },
9156     /**
9157      * return the real textarea element.
9158      */
9159     inputEl: function ()
9160     {
9161         return this.el.select('textarea.form-control',true).first();
9162     },
9163     
9164     /**
9165      * Clear any invalid styles/messages for this field
9166      */
9167     clearInvalid : function()
9168     {
9169         
9170         if(!this.el || this.preventMark){ // not rendered
9171             return;
9172         }
9173         
9174         var label = this.el.select('label', true).first();
9175         var icon = this.el.select('i.fa-star', true).first();
9176         
9177         if(label && icon){
9178             icon.remove();
9179         }
9180         
9181         this.el.removeClass(this.invalidClass);
9182         
9183         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9184             
9185             var feedback = this.el.select('.form-control-feedback', true).first();
9186             
9187             if(feedback){
9188                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9189             }
9190             
9191         }
9192         
9193         this.fireEvent('valid', this);
9194     },
9195     
9196      /**
9197      * Mark this field as valid
9198      */
9199     markValid : function()
9200     {
9201         if(!this.el  || this.preventMark){ // not rendered
9202             return;
9203         }
9204         
9205         this.el.removeClass([this.invalidClass, this.validClass]);
9206         
9207         var feedback = this.el.select('.form-control-feedback', true).first();
9208             
9209         if(feedback){
9210             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9211         }
9212
9213         if(this.disabled || this.allowBlank){
9214             return;
9215         }
9216         
9217         var label = this.el.select('label', true).first();
9218         var icon = this.el.select('i.fa-star', true).first();
9219         
9220         if(label && icon){
9221             icon.remove();
9222         }
9223         
9224         this.el.addClass(this.validClass);
9225         
9226         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9227             
9228             var feedback = this.el.select('.form-control-feedback', true).first();
9229             
9230             if(feedback){
9231                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9232                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9233             }
9234             
9235         }
9236         
9237         this.fireEvent('valid', this);
9238     },
9239     
9240      /**
9241      * Mark this field as invalid
9242      * @param {String} msg The validation message
9243      */
9244     markInvalid : function(msg)
9245     {
9246         if(!this.el  || this.preventMark){ // not rendered
9247             return;
9248         }
9249         
9250         this.el.removeClass([this.invalidClass, this.validClass]);
9251         
9252         var feedback = this.el.select('.form-control-feedback', true).first();
9253             
9254         if(feedback){
9255             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9256         }
9257
9258         if(this.disabled || this.allowBlank){
9259             return;
9260         }
9261         
9262         var label = this.el.select('label', true).first();
9263         var icon = this.el.select('i.fa-star', true).first();
9264         
9265         if(!this.getValue().length && label && !icon){
9266             this.el.createChild({
9267                 tag : 'i',
9268                 cls : 'text-danger fa fa-lg fa-star',
9269                 tooltip : 'This field is required',
9270                 style : 'margin-right:5px;'
9271             }, label, true);
9272         }
9273
9274         this.el.addClass(this.invalidClass);
9275         
9276         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9277             
9278             var feedback = this.el.select('.form-control-feedback', true).first();
9279             
9280             if(feedback){
9281                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9282                 
9283                 if(this.getValue().length || this.forceFeedback){
9284                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9285                 }
9286                 
9287             }
9288             
9289         }
9290         
9291         this.fireEvent('invalid', this, msg);
9292     }
9293 });
9294
9295  
9296 /*
9297  * - LGPL
9298  *
9299  * trigger field - base class for combo..
9300  * 
9301  */
9302  
9303 /**
9304  * @class Roo.bootstrap.TriggerField
9305  * @extends Roo.bootstrap.Input
9306  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9307  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9308  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9309  * for which you can provide a custom implementation.  For example:
9310  * <pre><code>
9311 var trigger = new Roo.bootstrap.TriggerField();
9312 trigger.onTriggerClick = myTriggerFn;
9313 trigger.applyTo('my-field');
9314 </code></pre>
9315  *
9316  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9317  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9318  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9319  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9320  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9321
9322  * @constructor
9323  * Create a new TriggerField.
9324  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9325  * to the base TextField)
9326  */
9327 Roo.bootstrap.TriggerField = function(config){
9328     this.mimicing = false;
9329     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9330 };
9331
9332 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9333     /**
9334      * @cfg {String} triggerClass A CSS class to apply to the trigger
9335      */
9336      /**
9337      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9338      */
9339     hideTrigger:false,
9340
9341     /**
9342      * @cfg {Boolean} removable (true|false) special filter default false
9343      */
9344     removable : false,
9345     
9346     /** @cfg {Boolean} grow @hide */
9347     /** @cfg {Number} growMin @hide */
9348     /** @cfg {Number} growMax @hide */
9349
9350     /**
9351      * @hide 
9352      * @method
9353      */
9354     autoSize: Roo.emptyFn,
9355     // private
9356     monitorTab : true,
9357     // private
9358     deferHeight : true,
9359
9360     
9361     actionMode : 'wrap',
9362     
9363     caret : false,
9364     
9365     
9366     getAutoCreate : function(){
9367        
9368         var align = this.labelAlign || this.parentLabelAlign();
9369         
9370         var id = Roo.id();
9371         
9372         var cfg = {
9373             cls: 'form-group' //input-group
9374         };
9375         
9376         
9377         var input =  {
9378             tag: 'input',
9379             id : id,
9380             type : this.inputType,
9381             cls : 'form-control',
9382             autocomplete: 'new-password',
9383             placeholder : this.placeholder || '' 
9384             
9385         };
9386         if (this.name) {
9387             input.name = this.name;
9388         }
9389         if (this.size) {
9390             input.cls += ' input-' + this.size;
9391         }
9392         
9393         if (this.disabled) {
9394             input.disabled=true;
9395         }
9396         
9397         var inputblock = input;
9398         
9399         if(this.hasFeedback && !this.allowBlank){
9400             
9401             var feedback = {
9402                 tag: 'span',
9403                 cls: 'glyphicon form-control-feedback'
9404             };
9405             
9406             if(this.removable && !this.editable && !this.tickable){
9407                 inputblock = {
9408                     cls : 'has-feedback',
9409                     cn :  [
9410                         inputblock,
9411                         {
9412                             tag: 'button',
9413                             html : 'x',
9414                             cls : 'roo-combo-removable-btn close'
9415                         },
9416                         feedback
9417                     ] 
9418                 };
9419             } else {
9420                 inputblock = {
9421                     cls : 'has-feedback',
9422                     cn :  [
9423                         inputblock,
9424                         feedback
9425                     ] 
9426                 };
9427             }
9428
9429         } else {
9430             if(this.removable && !this.editable && !this.tickable){
9431                 inputblock = {
9432                     cls : 'roo-removable',
9433                     cn :  [
9434                         inputblock,
9435                         {
9436                             tag: 'button',
9437                             html : 'x',
9438                             cls : 'roo-combo-removable-btn close'
9439                         }
9440                     ] 
9441                 };
9442             }
9443         }
9444         
9445         if (this.before || this.after) {
9446             
9447             inputblock = {
9448                 cls : 'input-group',
9449                 cn :  [] 
9450             };
9451             if (this.before) {
9452                 inputblock.cn.push({
9453                     tag :'span',
9454                     cls : 'input-group-addon',
9455                     html : this.before
9456                 });
9457             }
9458             
9459             inputblock.cn.push(input);
9460             
9461             if(this.hasFeedback && !this.allowBlank){
9462                 inputblock.cls += ' has-feedback';
9463                 inputblock.cn.push(feedback);
9464             }
9465             
9466             if (this.after) {
9467                 inputblock.cn.push({
9468                     tag :'span',
9469                     cls : 'input-group-addon',
9470                     html : this.after
9471                 });
9472             }
9473             
9474         };
9475         
9476         var box = {
9477             tag: 'div',
9478             cn: [
9479                 {
9480                     tag: 'input',
9481                     type : 'hidden',
9482                     cls: 'form-hidden-field'
9483                 },
9484                 inputblock
9485             ]
9486             
9487         };
9488         
9489         if(this.multiple){
9490             box = {
9491                 tag: 'div',
9492                 cn: [
9493                     {
9494                         tag: 'input',
9495                         type : 'hidden',
9496                         cls: 'form-hidden-field'
9497                     },
9498                     {
9499                         tag: 'ul',
9500                         cls: 'roo-select2-choices',
9501                         cn:[
9502                             {
9503                                 tag: 'li',
9504                                 cls: 'roo-select2-search-field',
9505                                 cn: [
9506
9507                                     inputblock
9508                                 ]
9509                             }
9510                         ]
9511                     }
9512                 ]
9513             }
9514         };
9515         
9516         var combobox = {
9517             cls: 'roo-select2-container input-group',
9518             cn: [
9519                 box
9520 //                {
9521 //                    tag: 'ul',
9522 //                    cls: 'typeahead typeahead-long dropdown-menu',
9523 //                    style: 'display:none'
9524 //                }
9525             ]
9526         };
9527         
9528         if(!this.multiple && this.showToggleBtn){
9529             
9530             var caret = {
9531                         tag: 'span',
9532                         cls: 'caret'
9533              };
9534             if (this.caret != false) {
9535                 caret = {
9536                      tag: 'i',
9537                      cls: 'fa fa-' + this.caret
9538                 };
9539                 
9540             }
9541             
9542             combobox.cn.push({
9543                 tag :'span',
9544                 cls : 'input-group-addon btn dropdown-toggle',
9545                 cn : [
9546                     caret,
9547                     {
9548                         tag: 'span',
9549                         cls: 'combobox-clear',
9550                         cn  : [
9551                             {
9552                                 tag : 'i',
9553                                 cls: 'icon-remove'
9554                             }
9555                         ]
9556                     }
9557                 ]
9558
9559             })
9560         }
9561         
9562         if(this.multiple){
9563             combobox.cls += ' roo-select2-container-multi';
9564         }
9565         
9566         if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
9567             
9568 //                Roo.log("left and has label");
9569             cfg.cn = [
9570                 {
9571                     tag : 'i',
9572                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9573                     tooltip : 'This field is required'
9574                 },
9575                 {
9576                     tag: 'label',
9577                     'for' :  id,
9578                     cls : 'control-label col-sm-' + this.labelWidth,
9579                     html : this.fieldLabel
9580
9581                 },
9582                 {
9583                     cls : "col-sm-" + (12 - this.labelWidth), 
9584                     cn: [
9585                         combobox
9586                     ]
9587                 }
9588
9589             ];
9590             
9591             if(this.indicatorpos == 'right'){
9592                 cfg.cn = [
9593                     {
9594                         tag: 'label',
9595                         'for' :  id,
9596                         cls : 'control-label col-sm-' + this.labelWidth,
9597                         html : this.fieldLabel
9598
9599                     },
9600                     {
9601                         tag : 'i',
9602                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9603                         tooltip : 'This field is required'
9604                     },
9605                     {
9606                         cls : "col-sm-" + (12 - this.labelWidth), 
9607                         cn: [
9608                             combobox
9609                         ]
9610                     }
9611
9612                 ];
9613             }
9614             
9615         } else if ( this.fieldLabel.length) {
9616 //                Roo.log(" label");
9617             cfg.cn = [
9618                 {
9619                    tag : 'i',
9620                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9621                    tooltip : 'This field is required'
9622                },
9623                {
9624                    tag: 'label',
9625                    //cls : 'input-group-addon',
9626                    html : this.fieldLabel
9627
9628                },
9629
9630                combobox
9631
9632             ];
9633             
9634             if(this.indicatorpos == 'right'){
9635                 
9636                 cfg.cn = [
9637                     {
9638                        tag: 'label',
9639                        //cls : 'input-group-addon',
9640                        html : this.fieldLabel
9641
9642                     },
9643                     {
9644                        tag : 'i',
9645                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9646                        tooltip : 'This field is required'
9647                     },
9648                     
9649                     combobox
9650
9651                 ];
9652
9653             }
9654
9655         } else {
9656             
9657 //                Roo.log(" no label && no align");
9658                 cfg = combobox
9659                      
9660                 
9661         }
9662          
9663         var settings=this;
9664         ['xs','sm','md','lg'].map(function(size){
9665             if (settings[size]) {
9666                 cfg.cls += ' col-' + size + '-' + settings[size];
9667             }
9668         });
9669         
9670         return cfg;
9671         
9672     },
9673     
9674     
9675     
9676     // private
9677     onResize : function(w, h){
9678 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9679 //        if(typeof w == 'number'){
9680 //            var x = w - this.trigger.getWidth();
9681 //            this.inputEl().setWidth(this.adjustWidth('input', x));
9682 //            this.trigger.setStyle('left', x+'px');
9683 //        }
9684     },
9685
9686     // private
9687     adjustSize : Roo.BoxComponent.prototype.adjustSize,
9688
9689     // private
9690     getResizeEl : function(){
9691         return this.inputEl();
9692     },
9693
9694     // private
9695     getPositionEl : function(){
9696         return this.inputEl();
9697     },
9698
9699     // private
9700     alignErrorIcon : function(){
9701         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9702     },
9703
9704     // private
9705     initEvents : function(){
9706         
9707         this.createList();
9708         
9709         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9710         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9711         if(!this.multiple && this.showToggleBtn){
9712             this.trigger = this.el.select('span.dropdown-toggle',true).first();
9713             if(this.hideTrigger){
9714                 this.trigger.setDisplayed(false);
9715             }
9716             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9717         }
9718         
9719         if(this.multiple){
9720             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9721         }
9722         
9723         if(this.removable && !this.editable && !this.tickable){
9724             var close = this.closeTriggerEl();
9725             
9726             if(close){
9727                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9728                 close.on('click', this.removeBtnClick, this, close);
9729             }
9730         }
9731         
9732         //this.trigger.addClassOnOver('x-form-trigger-over');
9733         //this.trigger.addClassOnClick('x-form-trigger-click');
9734         
9735         //if(!this.width){
9736         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9737         //}
9738     },
9739     
9740     closeTriggerEl : function()
9741     {
9742         var close = this.el.select('.roo-combo-removable-btn', true).first();
9743         return close ? close : false;
9744     },
9745     
9746     removeBtnClick : function(e, h, el)
9747     {
9748         e.preventDefault();
9749         
9750         if(this.fireEvent("remove", this) !== false){
9751             this.reset();
9752             this.fireEvent("afterremove", this)
9753         }
9754     },
9755     
9756     createList : function()
9757     {
9758         this.list = Roo.get(document.body).createChild({
9759             tag: 'ul',
9760             cls: 'typeahead typeahead-long dropdown-menu',
9761             style: 'display:none'
9762         });
9763         
9764         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9765         
9766     },
9767
9768     // private
9769     initTrigger : function(){
9770        
9771     },
9772
9773     // private
9774     onDestroy : function(){
9775         if(this.trigger){
9776             this.trigger.removeAllListeners();
9777           //  this.trigger.remove();
9778         }
9779         //if(this.wrap){
9780         //    this.wrap.remove();
9781         //}
9782         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9783     },
9784
9785     // private
9786     onFocus : function(){
9787         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9788         /*
9789         if(!this.mimicing){
9790             this.wrap.addClass('x-trigger-wrap-focus');
9791             this.mimicing = true;
9792             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9793             if(this.monitorTab){
9794                 this.el.on("keydown", this.checkTab, this);
9795             }
9796         }
9797         */
9798     },
9799
9800     // private
9801     checkTab : function(e){
9802         if(e.getKey() == e.TAB){
9803             this.triggerBlur();
9804         }
9805     },
9806
9807     // private
9808     onBlur : function(){
9809         // do nothing
9810     },
9811
9812     // private
9813     mimicBlur : function(e, t){
9814         /*
9815         if(!this.wrap.contains(t) && this.validateBlur()){
9816             this.triggerBlur();
9817         }
9818         */
9819     },
9820
9821     // private
9822     triggerBlur : function(){
9823         this.mimicing = false;
9824         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9825         if(this.monitorTab){
9826             this.el.un("keydown", this.checkTab, this);
9827         }
9828         //this.wrap.removeClass('x-trigger-wrap-focus');
9829         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9830     },
9831
9832     // private
9833     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9834     validateBlur : function(e, t){
9835         return true;
9836     },
9837
9838     // private
9839     onDisable : function(){
9840         this.inputEl().dom.disabled = true;
9841         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9842         //if(this.wrap){
9843         //    this.wrap.addClass('x-item-disabled');
9844         //}
9845     },
9846
9847     // private
9848     onEnable : function(){
9849         this.inputEl().dom.disabled = false;
9850         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9851         //if(this.wrap){
9852         //    this.el.removeClass('x-item-disabled');
9853         //}
9854     },
9855
9856     // private
9857     onShow : function(){
9858         var ae = this.getActionEl();
9859         
9860         if(ae){
9861             ae.dom.style.display = '';
9862             ae.dom.style.visibility = 'visible';
9863         }
9864     },
9865
9866     // private
9867     
9868     onHide : function(){
9869         var ae = this.getActionEl();
9870         ae.dom.style.display = 'none';
9871     },
9872
9873     /**
9874      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9875      * by an implementing function.
9876      * @method
9877      * @param {EventObject} e
9878      */
9879     onTriggerClick : Roo.emptyFn
9880 });
9881  /*
9882  * Based on:
9883  * Ext JS Library 1.1.1
9884  * Copyright(c) 2006-2007, Ext JS, LLC.
9885  *
9886  * Originally Released Under LGPL - original licence link has changed is not relivant.
9887  *
9888  * Fork - LGPL
9889  * <script type="text/javascript">
9890  */
9891
9892
9893 /**
9894  * @class Roo.data.SortTypes
9895  * @singleton
9896  * Defines the default sorting (casting?) comparison functions used when sorting data.
9897  */
9898 Roo.data.SortTypes = {
9899     /**
9900      * Default sort that does nothing
9901      * @param {Mixed} s The value being converted
9902      * @return {Mixed} The comparison value
9903      */
9904     none : function(s){
9905         return s;
9906     },
9907     
9908     /**
9909      * The regular expression used to strip tags
9910      * @type {RegExp}
9911      * @property
9912      */
9913     stripTagsRE : /<\/?[^>]+>/gi,
9914     
9915     /**
9916      * Strips all HTML tags to sort on text only
9917      * @param {Mixed} s The value being converted
9918      * @return {String} The comparison value
9919      */
9920     asText : function(s){
9921         return String(s).replace(this.stripTagsRE, "");
9922     },
9923     
9924     /**
9925      * Strips all HTML tags to sort on text only - Case insensitive
9926      * @param {Mixed} s The value being converted
9927      * @return {String} The comparison value
9928      */
9929     asUCText : function(s){
9930         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9931     },
9932     
9933     /**
9934      * Case insensitive string
9935      * @param {Mixed} s The value being converted
9936      * @return {String} The comparison value
9937      */
9938     asUCString : function(s) {
9939         return String(s).toUpperCase();
9940     },
9941     
9942     /**
9943      * Date sorting
9944      * @param {Mixed} s The value being converted
9945      * @return {Number} The comparison value
9946      */
9947     asDate : function(s) {
9948         if(!s){
9949             return 0;
9950         }
9951         if(s instanceof Date){
9952             return s.getTime();
9953         }
9954         return Date.parse(String(s));
9955     },
9956     
9957     /**
9958      * Float sorting
9959      * @param {Mixed} s The value being converted
9960      * @return {Float} The comparison value
9961      */
9962     asFloat : function(s) {
9963         var val = parseFloat(String(s).replace(/,/g, ""));
9964         if(isNaN(val)) {
9965             val = 0;
9966         }
9967         return val;
9968     },
9969     
9970     /**
9971      * Integer sorting
9972      * @param {Mixed} s The value being converted
9973      * @return {Number} The comparison value
9974      */
9975     asInt : function(s) {
9976         var val = parseInt(String(s).replace(/,/g, ""));
9977         if(isNaN(val)) {
9978             val = 0;
9979         }
9980         return val;
9981     }
9982 };/*
9983  * Based on:
9984  * Ext JS Library 1.1.1
9985  * Copyright(c) 2006-2007, Ext JS, LLC.
9986  *
9987  * Originally Released Under LGPL - original licence link has changed is not relivant.
9988  *
9989  * Fork - LGPL
9990  * <script type="text/javascript">
9991  */
9992
9993 /**
9994 * @class Roo.data.Record
9995  * Instances of this class encapsulate both record <em>definition</em> information, and record
9996  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9997  * to access Records cached in an {@link Roo.data.Store} object.<br>
9998  * <p>
9999  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10000  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10001  * objects.<br>
10002  * <p>
10003  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10004  * @constructor
10005  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10006  * {@link #create}. The parameters are the same.
10007  * @param {Array} data An associative Array of data values keyed by the field name.
10008  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10009  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10010  * not specified an integer id is generated.
10011  */
10012 Roo.data.Record = function(data, id){
10013     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10014     this.data = data;
10015 };
10016
10017 /**
10018  * Generate a constructor for a specific record layout.
10019  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10020  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10021  * Each field definition object may contain the following properties: <ul>
10022  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
10023  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10024  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10025  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10026  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10027  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10028  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10029  * this may be omitted.</p></li>
10030  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10031  * <ul><li>auto (Default, implies no conversion)</li>
10032  * <li>string</li>
10033  * <li>int</li>
10034  * <li>float</li>
10035  * <li>boolean</li>
10036  * <li>date</li></ul></p></li>
10037  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10038  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10039  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10040  * by the Reader into an object that will be stored in the Record. It is passed the
10041  * following parameters:<ul>
10042  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10043  * </ul></p></li>
10044  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10045  * </ul>
10046  * <br>usage:<br><pre><code>
10047 var TopicRecord = Roo.data.Record.create(
10048     {name: 'title', mapping: 'topic_title'},
10049     {name: 'author', mapping: 'username'},
10050     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10051     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10052     {name: 'lastPoster', mapping: 'user2'},
10053     {name: 'excerpt', mapping: 'post_text'}
10054 );
10055
10056 var myNewRecord = new TopicRecord({
10057     title: 'Do my job please',
10058     author: 'noobie',
10059     totalPosts: 1,
10060     lastPost: new Date(),
10061     lastPoster: 'Animal',
10062     excerpt: 'No way dude!'
10063 });
10064 myStore.add(myNewRecord);
10065 </code></pre>
10066  * @method create
10067  * @static
10068  */
10069 Roo.data.Record.create = function(o){
10070     var f = function(){
10071         f.superclass.constructor.apply(this, arguments);
10072     };
10073     Roo.extend(f, Roo.data.Record);
10074     var p = f.prototype;
10075     p.fields = new Roo.util.MixedCollection(false, function(field){
10076         return field.name;
10077     });
10078     for(var i = 0, len = o.length; i < len; i++){
10079         p.fields.add(new Roo.data.Field(o[i]));
10080     }
10081     f.getField = function(name){
10082         return p.fields.get(name);  
10083     };
10084     return f;
10085 };
10086
10087 Roo.data.Record.AUTO_ID = 1000;
10088 Roo.data.Record.EDIT = 'edit';
10089 Roo.data.Record.REJECT = 'reject';
10090 Roo.data.Record.COMMIT = 'commit';
10091
10092 Roo.data.Record.prototype = {
10093     /**
10094      * Readonly flag - true if this record has been modified.
10095      * @type Boolean
10096      */
10097     dirty : false,
10098     editing : false,
10099     error: null,
10100     modified: null,
10101
10102     // private
10103     join : function(store){
10104         this.store = store;
10105     },
10106
10107     /**
10108      * Set the named field to the specified value.
10109      * @param {String} name The name of the field to set.
10110      * @param {Object} value The value to set the field to.
10111      */
10112     set : function(name, value){
10113         if(this.data[name] == value){
10114             return;
10115         }
10116         this.dirty = true;
10117         if(!this.modified){
10118             this.modified = {};
10119         }
10120         if(typeof this.modified[name] == 'undefined'){
10121             this.modified[name] = this.data[name];
10122         }
10123         this.data[name] = value;
10124         if(!this.editing && this.store){
10125             this.store.afterEdit(this);
10126         }       
10127     },
10128
10129     /**
10130      * Get the value of the named field.
10131      * @param {String} name The name of the field to get the value of.
10132      * @return {Object} The value of the field.
10133      */
10134     get : function(name){
10135         return this.data[name]; 
10136     },
10137
10138     // private
10139     beginEdit : function(){
10140         this.editing = true;
10141         this.modified = {}; 
10142     },
10143
10144     // private
10145     cancelEdit : function(){
10146         this.editing = false;
10147         delete this.modified;
10148     },
10149
10150     // private
10151     endEdit : function(){
10152         this.editing = false;
10153         if(this.dirty && this.store){
10154             this.store.afterEdit(this);
10155         }
10156     },
10157
10158     /**
10159      * Usually called by the {@link Roo.data.Store} which owns the Record.
10160      * Rejects all changes made to the Record since either creation, or the last commit operation.
10161      * Modified fields are reverted to their original values.
10162      * <p>
10163      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10164      * of reject operations.
10165      */
10166     reject : function(){
10167         var m = this.modified;
10168         for(var n in m){
10169             if(typeof m[n] != "function"){
10170                 this.data[n] = m[n];
10171             }
10172         }
10173         this.dirty = false;
10174         delete this.modified;
10175         this.editing = false;
10176         if(this.store){
10177             this.store.afterReject(this);
10178         }
10179     },
10180
10181     /**
10182      * Usually called by the {@link Roo.data.Store} which owns the Record.
10183      * Commits all changes made to the Record since either creation, or the last commit operation.
10184      * <p>
10185      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10186      * of commit operations.
10187      */
10188     commit : function(){
10189         this.dirty = false;
10190         delete this.modified;
10191         this.editing = false;
10192         if(this.store){
10193             this.store.afterCommit(this);
10194         }
10195     },
10196
10197     // private
10198     hasError : function(){
10199         return this.error != null;
10200     },
10201
10202     // private
10203     clearError : function(){
10204         this.error = null;
10205     },
10206
10207     /**
10208      * Creates a copy of this record.
10209      * @param {String} id (optional) A new record id if you don't want to use this record's id
10210      * @return {Record}
10211      */
10212     copy : function(newId) {
10213         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10214     }
10215 };/*
10216  * Based on:
10217  * Ext JS Library 1.1.1
10218  * Copyright(c) 2006-2007, Ext JS, LLC.
10219  *
10220  * Originally Released Under LGPL - original licence link has changed is not relivant.
10221  *
10222  * Fork - LGPL
10223  * <script type="text/javascript">
10224  */
10225
10226
10227
10228 /**
10229  * @class Roo.data.Store
10230  * @extends Roo.util.Observable
10231  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10232  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10233  * <p>
10234  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
10235  * has no knowledge of the format of the data returned by the Proxy.<br>
10236  * <p>
10237  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10238  * instances from the data object. These records are cached and made available through accessor functions.
10239  * @constructor
10240  * Creates a new Store.
10241  * @param {Object} config A config object containing the objects needed for the Store to access data,
10242  * and read the data into Records.
10243  */
10244 Roo.data.Store = function(config){
10245     this.data = new Roo.util.MixedCollection(false);
10246     this.data.getKey = function(o){
10247         return o.id;
10248     };
10249     this.baseParams = {};
10250     // private
10251     this.paramNames = {
10252         "start" : "start",
10253         "limit" : "limit",
10254         "sort" : "sort",
10255         "dir" : "dir",
10256         "multisort" : "_multisort"
10257     };
10258
10259     if(config && config.data){
10260         this.inlineData = config.data;
10261         delete config.data;
10262     }
10263
10264     Roo.apply(this, config);
10265     
10266     if(this.reader){ // reader passed
10267         this.reader = Roo.factory(this.reader, Roo.data);
10268         this.reader.xmodule = this.xmodule || false;
10269         if(!this.recordType){
10270             this.recordType = this.reader.recordType;
10271         }
10272         if(this.reader.onMetaChange){
10273             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10274         }
10275     }
10276
10277     if(this.recordType){
10278         this.fields = this.recordType.prototype.fields;
10279     }
10280     this.modified = [];
10281
10282     this.addEvents({
10283         /**
10284          * @event datachanged
10285          * Fires when the data cache has changed, and a widget which is using this Store
10286          * as a Record cache should refresh its view.
10287          * @param {Store} this
10288          */
10289         datachanged : true,
10290         /**
10291          * @event metachange
10292          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10293          * @param {Store} this
10294          * @param {Object} meta The JSON metadata
10295          */
10296         metachange : true,
10297         /**
10298          * @event add
10299          * Fires when Records have been added to the Store
10300          * @param {Store} this
10301          * @param {Roo.data.Record[]} records The array of Records added
10302          * @param {Number} index The index at which the record(s) were added
10303          */
10304         add : true,
10305         /**
10306          * @event remove
10307          * Fires when a Record has been removed from the Store
10308          * @param {Store} this
10309          * @param {Roo.data.Record} record The Record that was removed
10310          * @param {Number} index The index at which the record was removed
10311          */
10312         remove : true,
10313         /**
10314          * @event update
10315          * Fires when a Record has been updated
10316          * @param {Store} this
10317          * @param {Roo.data.Record} record The Record that was updated
10318          * @param {String} operation The update operation being performed.  Value may be one of:
10319          * <pre><code>
10320  Roo.data.Record.EDIT
10321  Roo.data.Record.REJECT
10322  Roo.data.Record.COMMIT
10323          * </code></pre>
10324          */
10325         update : true,
10326         /**
10327          * @event clear
10328          * Fires when the data cache has been cleared.
10329          * @param {Store} this
10330          */
10331         clear : true,
10332         /**
10333          * @event beforeload
10334          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10335          * the load action will be canceled.
10336          * @param {Store} this
10337          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10338          */
10339         beforeload : true,
10340         /**
10341          * @event beforeloadadd
10342          * Fires after a new set of Records has been loaded.
10343          * @param {Store} this
10344          * @param {Roo.data.Record[]} records The Records that were loaded
10345          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10346          */
10347         beforeloadadd : true,
10348         /**
10349          * @event load
10350          * Fires after a new set of Records has been loaded, before they are added to the store.
10351          * @param {Store} this
10352          * @param {Roo.data.Record[]} records The Records that were loaded
10353          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10354          * @params {Object} return from reader
10355          */
10356         load : true,
10357         /**
10358          * @event loadexception
10359          * Fires if an exception occurs in the Proxy during loading.
10360          * Called with the signature of the Proxy's "loadexception" event.
10361          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10362          * 
10363          * @param {Proxy} 
10364          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10365          * @param {Object} load options 
10366          * @param {Object} jsonData from your request (normally this contains the Exception)
10367          */
10368         loadexception : true
10369     });
10370     
10371     if(this.proxy){
10372         this.proxy = Roo.factory(this.proxy, Roo.data);
10373         this.proxy.xmodule = this.xmodule || false;
10374         this.relayEvents(this.proxy,  ["loadexception"]);
10375     }
10376     this.sortToggle = {};
10377     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10378
10379     Roo.data.Store.superclass.constructor.call(this);
10380
10381     if(this.inlineData){
10382         this.loadData(this.inlineData);
10383         delete this.inlineData;
10384     }
10385 };
10386
10387 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10388      /**
10389     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10390     * without a remote query - used by combo/forms at present.
10391     */
10392     
10393     /**
10394     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10395     */
10396     /**
10397     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10398     */
10399     /**
10400     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10401     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10402     */
10403     /**
10404     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10405     * on any HTTP request
10406     */
10407     /**
10408     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10409     */
10410     /**
10411     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10412     */
10413     multiSort: false,
10414     /**
10415     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10416     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10417     */
10418     remoteSort : false,
10419
10420     /**
10421     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10422      * loaded or when a record is removed. (defaults to false).
10423     */
10424     pruneModifiedRecords : false,
10425
10426     // private
10427     lastOptions : null,
10428
10429     /**
10430      * Add Records to the Store and fires the add event.
10431      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10432      */
10433     add : function(records){
10434         records = [].concat(records);
10435         for(var i = 0, len = records.length; i < len; i++){
10436             records[i].join(this);
10437         }
10438         var index = this.data.length;
10439         this.data.addAll(records);
10440         this.fireEvent("add", this, records, index);
10441     },
10442
10443     /**
10444      * Remove a Record from the Store and fires the remove event.
10445      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10446      */
10447     remove : function(record){
10448         var index = this.data.indexOf(record);
10449         this.data.removeAt(index);
10450         if(this.pruneModifiedRecords){
10451             this.modified.remove(record);
10452         }
10453         this.fireEvent("remove", this, record, index);
10454     },
10455
10456     /**
10457      * Remove all Records from the Store and fires the clear event.
10458      */
10459     removeAll : function(){
10460         this.data.clear();
10461         if(this.pruneModifiedRecords){
10462             this.modified = [];
10463         }
10464         this.fireEvent("clear", this);
10465     },
10466
10467     /**
10468      * Inserts Records to the Store at the given index and fires the add event.
10469      * @param {Number} index The start index at which to insert the passed Records.
10470      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10471      */
10472     insert : function(index, records){
10473         records = [].concat(records);
10474         for(var i = 0, len = records.length; i < len; i++){
10475             this.data.insert(index, records[i]);
10476             records[i].join(this);
10477         }
10478         this.fireEvent("add", this, records, index);
10479     },
10480
10481     /**
10482      * Get the index within the cache of the passed Record.
10483      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10484      * @return {Number} The index of the passed Record. Returns -1 if not found.
10485      */
10486     indexOf : function(record){
10487         return this.data.indexOf(record);
10488     },
10489
10490     /**
10491      * Get the index within the cache of the Record with the passed id.
10492      * @param {String} id The id of the Record to find.
10493      * @return {Number} The index of the Record. Returns -1 if not found.
10494      */
10495     indexOfId : function(id){
10496         return this.data.indexOfKey(id);
10497     },
10498
10499     /**
10500      * Get the Record with the specified id.
10501      * @param {String} id The id of the Record to find.
10502      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10503      */
10504     getById : function(id){
10505         return this.data.key(id);
10506     },
10507
10508     /**
10509      * Get the Record at the specified index.
10510      * @param {Number} index The index of the Record to find.
10511      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10512      */
10513     getAt : function(index){
10514         return this.data.itemAt(index);
10515     },
10516
10517     /**
10518      * Returns a range of Records between specified indices.
10519      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10520      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10521      * @return {Roo.data.Record[]} An array of Records
10522      */
10523     getRange : function(start, end){
10524         return this.data.getRange(start, end);
10525     },
10526
10527     // private
10528     storeOptions : function(o){
10529         o = Roo.apply({}, o);
10530         delete o.callback;
10531         delete o.scope;
10532         this.lastOptions = o;
10533     },
10534
10535     /**
10536      * Loads the Record cache from the configured Proxy using the configured Reader.
10537      * <p>
10538      * If using remote paging, then the first load call must specify the <em>start</em>
10539      * and <em>limit</em> properties in the options.params property to establish the initial
10540      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10541      * <p>
10542      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10543      * and this call will return before the new data has been loaded. Perform any post-processing
10544      * in a callback function, or in a "load" event handler.</strong>
10545      * <p>
10546      * @param {Object} options An object containing properties which control loading options:<ul>
10547      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10548      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10549      * passed the following arguments:<ul>
10550      * <li>r : Roo.data.Record[]</li>
10551      * <li>options: Options object from the load call</li>
10552      * <li>success: Boolean success indicator</li></ul></li>
10553      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10554      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10555      * </ul>
10556      */
10557     load : function(options){
10558         options = options || {};
10559         if(this.fireEvent("beforeload", this, options) !== false){
10560             this.storeOptions(options);
10561             var p = Roo.apply(options.params || {}, this.baseParams);
10562             // if meta was not loaded from remote source.. try requesting it.
10563             if (!this.reader.metaFromRemote) {
10564                 p._requestMeta = 1;
10565             }
10566             if(this.sortInfo && this.remoteSort){
10567                 var pn = this.paramNames;
10568                 p[pn["sort"]] = this.sortInfo.field;
10569                 p[pn["dir"]] = this.sortInfo.direction;
10570             }
10571             if (this.multiSort) {
10572                 var pn = this.paramNames;
10573                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10574             }
10575             
10576             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10577         }
10578     },
10579
10580     /**
10581      * Reloads the Record cache from the configured Proxy using the configured Reader and
10582      * the options from the last load operation performed.
10583      * @param {Object} options (optional) An object containing properties which may override the options
10584      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10585      * the most recently used options are reused).
10586      */
10587     reload : function(options){
10588         this.load(Roo.applyIf(options||{}, this.lastOptions));
10589     },
10590
10591     // private
10592     // Called as a callback by the Reader during a load operation.
10593     loadRecords : function(o, options, success){
10594         if(!o || success === false){
10595             if(success !== false){
10596                 this.fireEvent("load", this, [], options, o);
10597             }
10598             if(options.callback){
10599                 options.callback.call(options.scope || this, [], options, false);
10600             }
10601             return;
10602         }
10603         // if data returned failure - throw an exception.
10604         if (o.success === false) {
10605             // show a message if no listener is registered.
10606             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10607                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10608             }
10609             // loadmask wil be hooked into this..
10610             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10611             return;
10612         }
10613         var r = o.records, t = o.totalRecords || r.length;
10614         
10615         this.fireEvent("beforeloadadd", this, r, options, o);
10616         
10617         if(!options || options.add !== true){
10618             if(this.pruneModifiedRecords){
10619                 this.modified = [];
10620             }
10621             for(var i = 0, len = r.length; i < len; i++){
10622                 r[i].join(this);
10623             }
10624             if(this.snapshot){
10625                 this.data = this.snapshot;
10626                 delete this.snapshot;
10627             }
10628             this.data.clear();
10629             this.data.addAll(r);
10630             this.totalLength = t;
10631             this.applySort();
10632             this.fireEvent("datachanged", this);
10633         }else{
10634             this.totalLength = Math.max(t, this.data.length+r.length);
10635             this.add(r);
10636         }
10637         this.fireEvent("load", this, r, options, o);
10638         if(options.callback){
10639             options.callback.call(options.scope || this, r, options, true);
10640         }
10641     },
10642
10643
10644     /**
10645      * Loads data from a passed data block. A Reader which understands the format of the data
10646      * must have been configured in the constructor.
10647      * @param {Object} data The data block from which to read the Records.  The format of the data expected
10648      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10649      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10650      */
10651     loadData : function(o, append){
10652         var r = this.reader.readRecords(o);
10653         this.loadRecords(r, {add: append}, true);
10654     },
10655
10656     /**
10657      * Gets the number of cached records.
10658      * <p>
10659      * <em>If using paging, this may not be the total size of the dataset. If the data object
10660      * used by the Reader contains the dataset size, then the getTotalCount() function returns
10661      * the data set size</em>
10662      */
10663     getCount : function(){
10664         return this.data.length || 0;
10665     },
10666
10667     /**
10668      * Gets the total number of records in the dataset as returned by the server.
10669      * <p>
10670      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10671      * the dataset size</em>
10672      */
10673     getTotalCount : function(){
10674         return this.totalLength || 0;
10675     },
10676
10677     /**
10678      * Returns the sort state of the Store as an object with two properties:
10679      * <pre><code>
10680  field {String} The name of the field by which the Records are sorted
10681  direction {String} The sort order, "ASC" or "DESC"
10682      * </code></pre>
10683      */
10684     getSortState : function(){
10685         return this.sortInfo;
10686     },
10687
10688     // private
10689     applySort : function(){
10690         if(this.sortInfo && !this.remoteSort){
10691             var s = this.sortInfo, f = s.field;
10692             var st = this.fields.get(f).sortType;
10693             var fn = function(r1, r2){
10694                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10695                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10696             };
10697             this.data.sort(s.direction, fn);
10698             if(this.snapshot && this.snapshot != this.data){
10699                 this.snapshot.sort(s.direction, fn);
10700             }
10701         }
10702     },
10703
10704     /**
10705      * Sets the default sort column and order to be used by the next load operation.
10706      * @param {String} fieldName The name of the field to sort by.
10707      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10708      */
10709     setDefaultSort : function(field, dir){
10710         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10711     },
10712
10713     /**
10714      * Sort the Records.
10715      * If remote sorting is used, the sort is performed on the server, and the cache is
10716      * reloaded. If local sorting is used, the cache is sorted internally.
10717      * @param {String} fieldName The name of the field to sort by.
10718      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10719      */
10720     sort : function(fieldName, dir){
10721         var f = this.fields.get(fieldName);
10722         if(!dir){
10723             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10724             
10725             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10726                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10727             }else{
10728                 dir = f.sortDir;
10729             }
10730         }
10731         this.sortToggle[f.name] = dir;
10732         this.sortInfo = {field: f.name, direction: dir};
10733         if(!this.remoteSort){
10734             this.applySort();
10735             this.fireEvent("datachanged", this);
10736         }else{
10737             this.load(this.lastOptions);
10738         }
10739     },
10740
10741     /**
10742      * Calls the specified function for each of the Records in the cache.
10743      * @param {Function} fn The function to call. The Record is passed as the first parameter.
10744      * Returning <em>false</em> aborts and exits the iteration.
10745      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10746      */
10747     each : function(fn, scope){
10748         this.data.each(fn, scope);
10749     },
10750
10751     /**
10752      * Gets all records modified since the last commit.  Modified records are persisted across load operations
10753      * (e.g., during paging).
10754      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10755      */
10756     getModifiedRecords : function(){
10757         return this.modified;
10758     },
10759
10760     // private
10761     createFilterFn : function(property, value, anyMatch){
10762         if(!value.exec){ // not a regex
10763             value = String(value);
10764             if(value.length == 0){
10765                 return false;
10766             }
10767             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10768         }
10769         return function(r){
10770             return value.test(r.data[property]);
10771         };
10772     },
10773
10774     /**
10775      * Sums the value of <i>property</i> for each record between start and end and returns the result.
10776      * @param {String} property A field on your records
10777      * @param {Number} start The record index to start at (defaults to 0)
10778      * @param {Number} end The last record index to include (defaults to length - 1)
10779      * @return {Number} The sum
10780      */
10781     sum : function(property, start, end){
10782         var rs = this.data.items, v = 0;
10783         start = start || 0;
10784         end = (end || end === 0) ? end : rs.length-1;
10785
10786         for(var i = start; i <= end; i++){
10787             v += (rs[i].data[property] || 0);
10788         }
10789         return v;
10790     },
10791
10792     /**
10793      * Filter the records by a specified property.
10794      * @param {String} field A field on your records
10795      * @param {String/RegExp} value Either a string that the field
10796      * should start with or a RegExp to test against the field
10797      * @param {Boolean} anyMatch True to match any part not just the beginning
10798      */
10799     filter : function(property, value, anyMatch){
10800         var fn = this.createFilterFn(property, value, anyMatch);
10801         return fn ? this.filterBy(fn) : this.clearFilter();
10802     },
10803
10804     /**
10805      * Filter by a function. The specified function will be called with each
10806      * record in this data source. If the function returns true the record is included,
10807      * otherwise it is filtered.
10808      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10809      * @param {Object} scope (optional) The scope of the function (defaults to this)
10810      */
10811     filterBy : function(fn, scope){
10812         this.snapshot = this.snapshot || this.data;
10813         this.data = this.queryBy(fn, scope||this);
10814         this.fireEvent("datachanged", this);
10815     },
10816
10817     /**
10818      * Query the records by a specified property.
10819      * @param {String} field A field on your records
10820      * @param {String/RegExp} value Either a string that the field
10821      * should start with or a RegExp to test against the field
10822      * @param {Boolean} anyMatch True to match any part not just the beginning
10823      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10824      */
10825     query : function(property, value, anyMatch){
10826         var fn = this.createFilterFn(property, value, anyMatch);
10827         return fn ? this.queryBy(fn) : this.data.clone();
10828     },
10829
10830     /**
10831      * Query by a function. The specified function will be called with each
10832      * record in this data source. If the function returns true the record is included
10833      * in the results.
10834      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10835      * @param {Object} scope (optional) The scope of the function (defaults to this)
10836       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10837      **/
10838     queryBy : function(fn, scope){
10839         var data = this.snapshot || this.data;
10840         return data.filterBy(fn, scope||this);
10841     },
10842
10843     /**
10844      * Collects unique values for a particular dataIndex from this store.
10845      * @param {String} dataIndex The property to collect
10846      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10847      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10848      * @return {Array} An array of the unique values
10849      **/
10850     collect : function(dataIndex, allowNull, bypassFilter){
10851         var d = (bypassFilter === true && this.snapshot) ?
10852                 this.snapshot.items : this.data.items;
10853         var v, sv, r = [], l = {};
10854         for(var i = 0, len = d.length; i < len; i++){
10855             v = d[i].data[dataIndex];
10856             sv = String(v);
10857             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10858                 l[sv] = true;
10859                 r[r.length] = v;
10860             }
10861         }
10862         return r;
10863     },
10864
10865     /**
10866      * Revert to a view of the Record cache with no filtering applied.
10867      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10868      */
10869     clearFilter : function(suppressEvent){
10870         if(this.snapshot && this.snapshot != this.data){
10871             this.data = this.snapshot;
10872             delete this.snapshot;
10873             if(suppressEvent !== true){
10874                 this.fireEvent("datachanged", this);
10875             }
10876         }
10877     },
10878
10879     // private
10880     afterEdit : function(record){
10881         if(this.modified.indexOf(record) == -1){
10882             this.modified.push(record);
10883         }
10884         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10885     },
10886     
10887     // private
10888     afterReject : function(record){
10889         this.modified.remove(record);
10890         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10891     },
10892
10893     // private
10894     afterCommit : function(record){
10895         this.modified.remove(record);
10896         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10897     },
10898
10899     /**
10900      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10901      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10902      */
10903     commitChanges : function(){
10904         var m = this.modified.slice(0);
10905         this.modified = [];
10906         for(var i = 0, len = m.length; i < len; i++){
10907             m[i].commit();
10908         }
10909     },
10910
10911     /**
10912      * Cancel outstanding changes on all changed records.
10913      */
10914     rejectChanges : function(){
10915         var m = this.modified.slice(0);
10916         this.modified = [];
10917         for(var i = 0, len = m.length; i < len; i++){
10918             m[i].reject();
10919         }
10920     },
10921
10922     onMetaChange : function(meta, rtype, o){
10923         this.recordType = rtype;
10924         this.fields = rtype.prototype.fields;
10925         delete this.snapshot;
10926         this.sortInfo = meta.sortInfo || this.sortInfo;
10927         this.modified = [];
10928         this.fireEvent('metachange', this, this.reader.meta);
10929     },
10930     
10931     moveIndex : function(data, type)
10932     {
10933         var index = this.indexOf(data);
10934         
10935         var newIndex = index + type;
10936         
10937         this.remove(data);
10938         
10939         this.insert(newIndex, data);
10940         
10941     }
10942 });/*
10943  * Based on:
10944  * Ext JS Library 1.1.1
10945  * Copyright(c) 2006-2007, Ext JS, LLC.
10946  *
10947  * Originally Released Under LGPL - original licence link has changed is not relivant.
10948  *
10949  * Fork - LGPL
10950  * <script type="text/javascript">
10951  */
10952
10953 /**
10954  * @class Roo.data.SimpleStore
10955  * @extends Roo.data.Store
10956  * Small helper class to make creating Stores from Array data easier.
10957  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10958  * @cfg {Array} fields An array of field definition objects, or field name strings.
10959  * @cfg {Array} data The multi-dimensional array of data
10960  * @constructor
10961  * @param {Object} config
10962  */
10963 Roo.data.SimpleStore = function(config){
10964     Roo.data.SimpleStore.superclass.constructor.call(this, {
10965         isLocal : true,
10966         reader: new Roo.data.ArrayReader({
10967                 id: config.id
10968             },
10969             Roo.data.Record.create(config.fields)
10970         ),
10971         proxy : new Roo.data.MemoryProxy(config.data)
10972     });
10973     this.load();
10974 };
10975 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10976  * Based on:
10977  * Ext JS Library 1.1.1
10978  * Copyright(c) 2006-2007, Ext JS, LLC.
10979  *
10980  * Originally Released Under LGPL - original licence link has changed is not relivant.
10981  *
10982  * Fork - LGPL
10983  * <script type="text/javascript">
10984  */
10985
10986 /**
10987 /**
10988  * @extends Roo.data.Store
10989  * @class Roo.data.JsonStore
10990  * Small helper class to make creating Stores for JSON data easier. <br/>
10991 <pre><code>
10992 var store = new Roo.data.JsonStore({
10993     url: 'get-images.php',
10994     root: 'images',
10995     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10996 });
10997 </code></pre>
10998  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10999  * JsonReader and HttpProxy (unless inline data is provided).</b>
11000  * @cfg {Array} fields An array of field definition objects, or field name strings.
11001  * @constructor
11002  * @param {Object} config
11003  */
11004 Roo.data.JsonStore = function(c){
11005     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11006         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11007         reader: new Roo.data.JsonReader(c, c.fields)
11008     }));
11009 };
11010 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11011  * Based on:
11012  * Ext JS Library 1.1.1
11013  * Copyright(c) 2006-2007, Ext JS, LLC.
11014  *
11015  * Originally Released Under LGPL - original licence link has changed is not relivant.
11016  *
11017  * Fork - LGPL
11018  * <script type="text/javascript">
11019  */
11020
11021  
11022 Roo.data.Field = function(config){
11023     if(typeof config == "string"){
11024         config = {name: config};
11025     }
11026     Roo.apply(this, config);
11027     
11028     if(!this.type){
11029         this.type = "auto";
11030     }
11031     
11032     var st = Roo.data.SortTypes;
11033     // named sortTypes are supported, here we look them up
11034     if(typeof this.sortType == "string"){
11035         this.sortType = st[this.sortType];
11036     }
11037     
11038     // set default sortType for strings and dates
11039     if(!this.sortType){
11040         switch(this.type){
11041             case "string":
11042                 this.sortType = st.asUCString;
11043                 break;
11044             case "date":
11045                 this.sortType = st.asDate;
11046                 break;
11047             default:
11048                 this.sortType = st.none;
11049         }
11050     }
11051
11052     // define once
11053     var stripRe = /[\$,%]/g;
11054
11055     // prebuilt conversion function for this field, instead of
11056     // switching every time we're reading a value
11057     if(!this.convert){
11058         var cv, dateFormat = this.dateFormat;
11059         switch(this.type){
11060             case "":
11061             case "auto":
11062             case undefined:
11063                 cv = function(v){ return v; };
11064                 break;
11065             case "string":
11066                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11067                 break;
11068             case "int":
11069                 cv = function(v){
11070                     return v !== undefined && v !== null && v !== '' ?
11071                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11072                     };
11073                 break;
11074             case "float":
11075                 cv = function(v){
11076                     return v !== undefined && v !== null && v !== '' ?
11077                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11078                     };
11079                 break;
11080             case "bool":
11081             case "boolean":
11082                 cv = function(v){ return v === true || v === "true" || v == 1; };
11083                 break;
11084             case "date":
11085                 cv = function(v){
11086                     if(!v){
11087                         return '';
11088                     }
11089                     if(v instanceof Date){
11090                         return v;
11091                     }
11092                     if(dateFormat){
11093                         if(dateFormat == "timestamp"){
11094                             return new Date(v*1000);
11095                         }
11096                         return Date.parseDate(v, dateFormat);
11097                     }
11098                     var parsed = Date.parse(v);
11099                     return parsed ? new Date(parsed) : null;
11100                 };
11101              break;
11102             
11103         }
11104         this.convert = cv;
11105     }
11106 };
11107
11108 Roo.data.Field.prototype = {
11109     dateFormat: null,
11110     defaultValue: "",
11111     mapping: null,
11112     sortType : null,
11113     sortDir : "ASC"
11114 };/*
11115  * Based on:
11116  * Ext JS Library 1.1.1
11117  * Copyright(c) 2006-2007, Ext JS, LLC.
11118  *
11119  * Originally Released Under LGPL - original licence link has changed is not relivant.
11120  *
11121  * Fork - LGPL
11122  * <script type="text/javascript">
11123  */
11124  
11125 // Base class for reading structured data from a data source.  This class is intended to be
11126 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11127
11128 /**
11129  * @class Roo.data.DataReader
11130  * Base class for reading structured data from a data source.  This class is intended to be
11131  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11132  */
11133
11134 Roo.data.DataReader = function(meta, recordType){
11135     
11136     this.meta = meta;
11137     
11138     this.recordType = recordType instanceof Array ? 
11139         Roo.data.Record.create(recordType) : recordType;
11140 };
11141
11142 Roo.data.DataReader.prototype = {
11143      /**
11144      * Create an empty record
11145      * @param {Object} data (optional) - overlay some values
11146      * @return {Roo.data.Record} record created.
11147      */
11148     newRow :  function(d) {
11149         var da =  {};
11150         this.recordType.prototype.fields.each(function(c) {
11151             switch( c.type) {
11152                 case 'int' : da[c.name] = 0; break;
11153                 case 'date' : da[c.name] = new Date(); break;
11154                 case 'float' : da[c.name] = 0.0; break;
11155                 case 'boolean' : da[c.name] = false; break;
11156                 default : da[c.name] = ""; break;
11157             }
11158             
11159         });
11160         return new this.recordType(Roo.apply(da, d));
11161     }
11162     
11163 };/*
11164  * Based on:
11165  * Ext JS Library 1.1.1
11166  * Copyright(c) 2006-2007, Ext JS, LLC.
11167  *
11168  * Originally Released Under LGPL - original licence link has changed is not relivant.
11169  *
11170  * Fork - LGPL
11171  * <script type="text/javascript">
11172  */
11173
11174 /**
11175  * @class Roo.data.DataProxy
11176  * @extends Roo.data.Observable
11177  * This class is an abstract base class for implementations which provide retrieval of
11178  * unformatted data objects.<br>
11179  * <p>
11180  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11181  * (of the appropriate type which knows how to parse the data object) to provide a block of
11182  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11183  * <p>
11184  * Custom implementations must implement the load method as described in
11185  * {@link Roo.data.HttpProxy#load}.
11186  */
11187 Roo.data.DataProxy = function(){
11188     this.addEvents({
11189         /**
11190          * @event beforeload
11191          * Fires before a network request is made to retrieve a data object.
11192          * @param {Object} This DataProxy object.
11193          * @param {Object} params The params parameter to the load function.
11194          */
11195         beforeload : true,
11196         /**
11197          * @event load
11198          * Fires before the load method's callback is called.
11199          * @param {Object} This DataProxy object.
11200          * @param {Object} o The data object.
11201          * @param {Object} arg The callback argument object passed to the load function.
11202          */
11203         load : true,
11204         /**
11205          * @event loadexception
11206          * Fires if an Exception occurs during data retrieval.
11207          * @param {Object} This DataProxy object.
11208          * @param {Object} o The data object.
11209          * @param {Object} arg The callback argument object passed to the load function.
11210          * @param {Object} e The Exception.
11211          */
11212         loadexception : true
11213     });
11214     Roo.data.DataProxy.superclass.constructor.call(this);
11215 };
11216
11217 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11218
11219     /**
11220      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11221      */
11222 /*
11223  * Based on:
11224  * Ext JS Library 1.1.1
11225  * Copyright(c) 2006-2007, Ext JS, LLC.
11226  *
11227  * Originally Released Under LGPL - original licence link has changed is not relivant.
11228  *
11229  * Fork - LGPL
11230  * <script type="text/javascript">
11231  */
11232 /**
11233  * @class Roo.data.MemoryProxy
11234  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11235  * to the Reader when its load method is called.
11236  * @constructor
11237  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11238  */
11239 Roo.data.MemoryProxy = function(data){
11240     if (data.data) {
11241         data = data.data;
11242     }
11243     Roo.data.MemoryProxy.superclass.constructor.call(this);
11244     this.data = data;
11245 };
11246
11247 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11248     
11249     /**
11250      * Load data from the requested source (in this case an in-memory
11251      * data object passed to the constructor), read the data object into
11252      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11253      * process that block using the passed callback.
11254      * @param {Object} params This parameter is not used by the MemoryProxy class.
11255      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11256      * object into a block of Roo.data.Records.
11257      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11258      * The function must be passed <ul>
11259      * <li>The Record block object</li>
11260      * <li>The "arg" argument from the load function</li>
11261      * <li>A boolean success indicator</li>
11262      * </ul>
11263      * @param {Object} scope The scope in which to call the callback
11264      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11265      */
11266     load : function(params, reader, callback, scope, arg){
11267         params = params || {};
11268         var result;
11269         try {
11270             result = reader.readRecords(this.data);
11271         }catch(e){
11272             this.fireEvent("loadexception", this, arg, null, e);
11273             callback.call(scope, null, arg, false);
11274             return;
11275         }
11276         callback.call(scope, result, arg, true);
11277     },
11278     
11279     // private
11280     update : function(params, records){
11281         
11282     }
11283 });/*
11284  * Based on:
11285  * Ext JS Library 1.1.1
11286  * Copyright(c) 2006-2007, Ext JS, LLC.
11287  *
11288  * Originally Released Under LGPL - original licence link has changed is not relivant.
11289  *
11290  * Fork - LGPL
11291  * <script type="text/javascript">
11292  */
11293 /**
11294  * @class Roo.data.HttpProxy
11295  * @extends Roo.data.DataProxy
11296  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11297  * configured to reference a certain URL.<br><br>
11298  * <p>
11299  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11300  * from which the running page was served.<br><br>
11301  * <p>
11302  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11303  * <p>
11304  * Be aware that to enable the browser to parse an XML document, the server must set
11305  * the Content-Type header in the HTTP response to "text/xml".
11306  * @constructor
11307  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11308  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11309  * will be used to make the request.
11310  */
11311 Roo.data.HttpProxy = function(conn){
11312     Roo.data.HttpProxy.superclass.constructor.call(this);
11313     // is conn a conn config or a real conn?
11314     this.conn = conn;
11315     this.useAjax = !conn || !conn.events;
11316   
11317 };
11318
11319 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11320     // thse are take from connection...
11321     
11322     /**
11323      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11324      */
11325     /**
11326      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11327      * extra parameters to each request made by this object. (defaults to undefined)
11328      */
11329     /**
11330      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11331      *  to each request made by this object. (defaults to undefined)
11332      */
11333     /**
11334      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11335      */
11336     /**
11337      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11338      */
11339      /**
11340      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11341      * @type Boolean
11342      */
11343   
11344
11345     /**
11346      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11347      * @type Boolean
11348      */
11349     /**
11350      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11351      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11352      * a finer-grained basis than the DataProxy events.
11353      */
11354     getConnection : function(){
11355         return this.useAjax ? Roo.Ajax : this.conn;
11356     },
11357
11358     /**
11359      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11360      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11361      * process that block using the passed callback.
11362      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11363      * for the request to the remote server.
11364      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11365      * object into a block of Roo.data.Records.
11366      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11367      * The function must be passed <ul>
11368      * <li>The Record block object</li>
11369      * <li>The "arg" argument from the load function</li>
11370      * <li>A boolean success indicator</li>
11371      * </ul>
11372      * @param {Object} scope The scope in which to call the callback
11373      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11374      */
11375     load : function(params, reader, callback, scope, arg){
11376         if(this.fireEvent("beforeload", this, params) !== false){
11377             var  o = {
11378                 params : params || {},
11379                 request: {
11380                     callback : callback,
11381                     scope : scope,
11382                     arg : arg
11383                 },
11384                 reader: reader,
11385                 callback : this.loadResponse,
11386                 scope: this
11387             };
11388             if(this.useAjax){
11389                 Roo.applyIf(o, this.conn);
11390                 if(this.activeRequest){
11391                     Roo.Ajax.abort(this.activeRequest);
11392                 }
11393                 this.activeRequest = Roo.Ajax.request(o);
11394             }else{
11395                 this.conn.request(o);
11396             }
11397         }else{
11398             callback.call(scope||this, null, arg, false);
11399         }
11400     },
11401
11402     // private
11403     loadResponse : function(o, success, response){
11404         delete this.activeRequest;
11405         if(!success){
11406             this.fireEvent("loadexception", this, o, response);
11407             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11408             return;
11409         }
11410         var result;
11411         try {
11412             result = o.reader.read(response);
11413         }catch(e){
11414             this.fireEvent("loadexception", this, o, response, e);
11415             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11416             return;
11417         }
11418         
11419         this.fireEvent("load", this, o, o.request.arg);
11420         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11421     },
11422
11423     // private
11424     update : function(dataSet){
11425
11426     },
11427
11428     // private
11429     updateResponse : function(dataSet){
11430
11431     }
11432 });/*
11433  * Based on:
11434  * Ext JS Library 1.1.1
11435  * Copyright(c) 2006-2007, Ext JS, LLC.
11436  *
11437  * Originally Released Under LGPL - original licence link has changed is not relivant.
11438  *
11439  * Fork - LGPL
11440  * <script type="text/javascript">
11441  */
11442
11443 /**
11444  * @class Roo.data.ScriptTagProxy
11445  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11446  * other than the originating domain of the running page.<br><br>
11447  * <p>
11448  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
11449  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11450  * <p>
11451  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11452  * source code that is used as the source inside a &lt;script> tag.<br><br>
11453  * <p>
11454  * In order for the browser to process the returned data, the server must wrap the data object
11455  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11456  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11457  * depending on whether the callback name was passed:
11458  * <p>
11459  * <pre><code>
11460 boolean scriptTag = false;
11461 String cb = request.getParameter("callback");
11462 if (cb != null) {
11463     scriptTag = true;
11464     response.setContentType("text/javascript");
11465 } else {
11466     response.setContentType("application/x-json");
11467 }
11468 Writer out = response.getWriter();
11469 if (scriptTag) {
11470     out.write(cb + "(");
11471 }
11472 out.print(dataBlock.toJsonString());
11473 if (scriptTag) {
11474     out.write(");");
11475 }
11476 </pre></code>
11477  *
11478  * @constructor
11479  * @param {Object} config A configuration object.
11480  */
11481 Roo.data.ScriptTagProxy = function(config){
11482     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11483     Roo.apply(this, config);
11484     this.head = document.getElementsByTagName("head")[0];
11485 };
11486
11487 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11488
11489 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11490     /**
11491      * @cfg {String} url The URL from which to request the data object.
11492      */
11493     /**
11494      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11495      */
11496     timeout : 30000,
11497     /**
11498      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11499      * the server the name of the callback function set up by the load call to process the returned data object.
11500      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11501      * javascript output which calls this named function passing the data object as its only parameter.
11502      */
11503     callbackParam : "callback",
11504     /**
11505      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11506      * name to the request.
11507      */
11508     nocache : true,
11509
11510     /**
11511      * Load data from the configured URL, read the data object into
11512      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11513      * process that block using the passed callback.
11514      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11515      * for the request to the remote server.
11516      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11517      * object into a block of Roo.data.Records.
11518      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11519      * The function must be passed <ul>
11520      * <li>The Record block object</li>
11521      * <li>The "arg" argument from the load function</li>
11522      * <li>A boolean success indicator</li>
11523      * </ul>
11524      * @param {Object} scope The scope in which to call the callback
11525      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11526      */
11527     load : function(params, reader, callback, scope, arg){
11528         if(this.fireEvent("beforeload", this, params) !== false){
11529
11530             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11531
11532             var url = this.url;
11533             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11534             if(this.nocache){
11535                 url += "&_dc=" + (new Date().getTime());
11536             }
11537             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11538             var trans = {
11539                 id : transId,
11540                 cb : "stcCallback"+transId,
11541                 scriptId : "stcScript"+transId,
11542                 params : params,
11543                 arg : arg,
11544                 url : url,
11545                 callback : callback,
11546                 scope : scope,
11547                 reader : reader
11548             };
11549             var conn = this;
11550
11551             window[trans.cb] = function(o){
11552                 conn.handleResponse(o, trans);
11553             };
11554
11555             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11556
11557             if(this.autoAbort !== false){
11558                 this.abort();
11559             }
11560
11561             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11562
11563             var script = document.createElement("script");
11564             script.setAttribute("src", url);
11565             script.setAttribute("type", "text/javascript");
11566             script.setAttribute("id", trans.scriptId);
11567             this.head.appendChild(script);
11568
11569             this.trans = trans;
11570         }else{
11571             callback.call(scope||this, null, arg, false);
11572         }
11573     },
11574
11575     // private
11576     isLoading : function(){
11577         return this.trans ? true : false;
11578     },
11579
11580     /**
11581      * Abort the current server request.
11582      */
11583     abort : function(){
11584         if(this.isLoading()){
11585             this.destroyTrans(this.trans);
11586         }
11587     },
11588
11589     // private
11590     destroyTrans : function(trans, isLoaded){
11591         this.head.removeChild(document.getElementById(trans.scriptId));
11592         clearTimeout(trans.timeoutId);
11593         if(isLoaded){
11594             window[trans.cb] = undefined;
11595             try{
11596                 delete window[trans.cb];
11597             }catch(e){}
11598         }else{
11599             // if hasn't been loaded, wait for load to remove it to prevent script error
11600             window[trans.cb] = function(){
11601                 window[trans.cb] = undefined;
11602                 try{
11603                     delete window[trans.cb];
11604                 }catch(e){}
11605             };
11606         }
11607     },
11608
11609     // private
11610     handleResponse : function(o, trans){
11611         this.trans = false;
11612         this.destroyTrans(trans, true);
11613         var result;
11614         try {
11615             result = trans.reader.readRecords(o);
11616         }catch(e){
11617             this.fireEvent("loadexception", this, o, trans.arg, e);
11618             trans.callback.call(trans.scope||window, null, trans.arg, false);
11619             return;
11620         }
11621         this.fireEvent("load", this, o, trans.arg);
11622         trans.callback.call(trans.scope||window, result, trans.arg, true);
11623     },
11624
11625     // private
11626     handleFailure : function(trans){
11627         this.trans = false;
11628         this.destroyTrans(trans, false);
11629         this.fireEvent("loadexception", this, null, trans.arg);
11630         trans.callback.call(trans.scope||window, null, trans.arg, false);
11631     }
11632 });/*
11633  * Based on:
11634  * Ext JS Library 1.1.1
11635  * Copyright(c) 2006-2007, Ext JS, LLC.
11636  *
11637  * Originally Released Under LGPL - original licence link has changed is not relivant.
11638  *
11639  * Fork - LGPL
11640  * <script type="text/javascript">
11641  */
11642
11643 /**
11644  * @class Roo.data.JsonReader
11645  * @extends Roo.data.DataReader
11646  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11647  * based on mappings in a provided Roo.data.Record constructor.
11648  * 
11649  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11650  * in the reply previously. 
11651  * 
11652  * <p>
11653  * Example code:
11654  * <pre><code>
11655 var RecordDef = Roo.data.Record.create([
11656     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
11657     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
11658 ]);
11659 var myReader = new Roo.data.JsonReader({
11660     totalProperty: "results",    // The property which contains the total dataset size (optional)
11661     root: "rows",                // The property which contains an Array of row objects
11662     id: "id"                     // The property within each row object that provides an ID for the record (optional)
11663 }, RecordDef);
11664 </code></pre>
11665  * <p>
11666  * This would consume a JSON file like this:
11667  * <pre><code>
11668 { 'results': 2, 'rows': [
11669     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11670     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11671 }
11672 </code></pre>
11673  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11674  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11675  * paged from the remote server.
11676  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11677  * @cfg {String} root name of the property which contains the Array of row objects.
11678  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11679  * @cfg {Array} fields Array of field definition objects
11680  * @constructor
11681  * Create a new JsonReader
11682  * @param {Object} meta Metadata configuration options
11683  * @param {Object} recordType Either an Array of field definition objects,
11684  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11685  */
11686 Roo.data.JsonReader = function(meta, recordType){
11687     
11688     meta = meta || {};
11689     // set some defaults:
11690     Roo.applyIf(meta, {
11691         totalProperty: 'total',
11692         successProperty : 'success',
11693         root : 'data',
11694         id : 'id'
11695     });
11696     
11697     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11698 };
11699 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11700     
11701     /**
11702      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
11703      * Used by Store query builder to append _requestMeta to params.
11704      * 
11705      */
11706     metaFromRemote : false,
11707     /**
11708      * This method is only used by a DataProxy which has retrieved data from a remote server.
11709      * @param {Object} response The XHR object which contains the JSON data in its responseText.
11710      * @return {Object} data A data block which is used by an Roo.data.Store object as
11711      * a cache of Roo.data.Records.
11712      */
11713     read : function(response){
11714         var json = response.responseText;
11715        
11716         var o = /* eval:var:o */ eval("("+json+")");
11717         if(!o) {
11718             throw {message: "JsonReader.read: Json object not found"};
11719         }
11720         
11721         if(o.metaData){
11722             
11723             delete this.ef;
11724             this.metaFromRemote = true;
11725             this.meta = o.metaData;
11726             this.recordType = Roo.data.Record.create(o.metaData.fields);
11727             this.onMetaChange(this.meta, this.recordType, o);
11728         }
11729         return this.readRecords(o);
11730     },
11731
11732     // private function a store will implement
11733     onMetaChange : function(meta, recordType, o){
11734
11735     },
11736
11737     /**
11738          * @ignore
11739          */
11740     simpleAccess: function(obj, subsc) {
11741         return obj[subsc];
11742     },
11743
11744         /**
11745          * @ignore
11746          */
11747     getJsonAccessor: function(){
11748         var re = /[\[\.]/;
11749         return function(expr) {
11750             try {
11751                 return(re.test(expr))
11752                     ? new Function("obj", "return obj." + expr)
11753                     : function(obj){
11754                         return obj[expr];
11755                     };
11756             } catch(e){}
11757             return Roo.emptyFn;
11758         };
11759     }(),
11760
11761     /**
11762      * Create a data block containing Roo.data.Records from an XML document.
11763      * @param {Object} o An object which contains an Array of row objects in the property specified
11764      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11765      * which contains the total size of the dataset.
11766      * @return {Object} data A data block which is used by an Roo.data.Store object as
11767      * a cache of Roo.data.Records.
11768      */
11769     readRecords : function(o){
11770         /**
11771          * After any data loads, the raw JSON data is available for further custom processing.
11772          * @type Object
11773          */
11774         this.o = o;
11775         var s = this.meta, Record = this.recordType,
11776             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11777
11778 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
11779         if (!this.ef) {
11780             if(s.totalProperty) {
11781                     this.getTotal = this.getJsonAccessor(s.totalProperty);
11782                 }
11783                 if(s.successProperty) {
11784                     this.getSuccess = this.getJsonAccessor(s.successProperty);
11785                 }
11786                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11787                 if (s.id) {
11788                         var g = this.getJsonAccessor(s.id);
11789                         this.getId = function(rec) {
11790                                 var r = g(rec);  
11791                                 return (r === undefined || r === "") ? null : r;
11792                         };
11793                 } else {
11794                         this.getId = function(){return null;};
11795                 }
11796             this.ef = [];
11797             for(var jj = 0; jj < fl; jj++){
11798                 f = fi[jj];
11799                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11800                 this.ef[jj] = this.getJsonAccessor(map);
11801             }
11802         }
11803
11804         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11805         if(s.totalProperty){
11806             var vt = parseInt(this.getTotal(o), 10);
11807             if(!isNaN(vt)){
11808                 totalRecords = vt;
11809             }
11810         }
11811         if(s.successProperty){
11812             var vs = this.getSuccess(o);
11813             if(vs === false || vs === 'false'){
11814                 success = false;
11815             }
11816         }
11817         var records = [];
11818         for(var i = 0; i < c; i++){
11819                 var n = root[i];
11820             var values = {};
11821             var id = this.getId(n);
11822             for(var j = 0; j < fl; j++){
11823                 f = fi[j];
11824             var v = this.ef[j](n);
11825             if (!f.convert) {
11826                 Roo.log('missing convert for ' + f.name);
11827                 Roo.log(f);
11828                 continue;
11829             }
11830             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11831             }
11832             var record = new Record(values, id);
11833             record.json = n;
11834             records[i] = record;
11835         }
11836         return {
11837             raw : o,
11838             success : success,
11839             records : records,
11840             totalRecords : totalRecords
11841         };
11842     }
11843 });/*
11844  * Based on:
11845  * Ext JS Library 1.1.1
11846  * Copyright(c) 2006-2007, Ext JS, LLC.
11847  *
11848  * Originally Released Under LGPL - original licence link has changed is not relivant.
11849  *
11850  * Fork - LGPL
11851  * <script type="text/javascript">
11852  */
11853
11854 /**
11855  * @class Roo.data.ArrayReader
11856  * @extends Roo.data.DataReader
11857  * Data reader class to create an Array of Roo.data.Record objects from an Array.
11858  * Each element of that Array represents a row of data fields. The
11859  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11860  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11861  * <p>
11862  * Example code:.
11863  * <pre><code>
11864 var RecordDef = Roo.data.Record.create([
11865     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11866     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11867 ]);
11868 var myReader = new Roo.data.ArrayReader({
11869     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11870 }, RecordDef);
11871 </code></pre>
11872  * <p>
11873  * This would consume an Array like this:
11874  * <pre><code>
11875 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11876   </code></pre>
11877  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11878  * @constructor
11879  * Create a new JsonReader
11880  * @param {Object} meta Metadata configuration options.
11881  * @param {Object} recordType Either an Array of field definition objects
11882  * as specified to {@link Roo.data.Record#create},
11883  * or an {@link Roo.data.Record} object
11884  * created using {@link Roo.data.Record#create}.
11885  */
11886 Roo.data.ArrayReader = function(meta, recordType){
11887     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11888 };
11889
11890 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11891     /**
11892      * Create a data block containing Roo.data.Records from an XML document.
11893      * @param {Object} o An Array of row objects which represents the dataset.
11894      * @return {Object} data A data block which is used by an Roo.data.Store object as
11895      * a cache of Roo.data.Records.
11896      */
11897     readRecords : function(o){
11898         var sid = this.meta ? this.meta.id : null;
11899         var recordType = this.recordType, fields = recordType.prototype.fields;
11900         var records = [];
11901         var root = o;
11902             for(var i = 0; i < root.length; i++){
11903                     var n = root[i];
11904                 var values = {};
11905                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11906                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11907                 var f = fields.items[j];
11908                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11909                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11910                 v = f.convert(v);
11911                 values[f.name] = v;
11912             }
11913                 var record = new recordType(values, id);
11914                 record.json = n;
11915                 records[records.length] = record;
11916             }
11917             return {
11918                 records : records,
11919                 totalRecords : records.length
11920             };
11921     }
11922 });/*
11923  * - LGPL
11924  * * 
11925  */
11926
11927 /**
11928  * @class Roo.bootstrap.ComboBox
11929  * @extends Roo.bootstrap.TriggerField
11930  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11931  * @cfg {Boolean} append (true|false) default false
11932  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11933  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11934  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11935  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11936  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11937  * @cfg {Boolean} animate default true
11938  * @cfg {Boolean} emptyResultText only for touch device
11939  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11940  * @constructor
11941  * Create a new ComboBox.
11942  * @param {Object} config Configuration options
11943  */
11944 Roo.bootstrap.ComboBox = function(config){
11945     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11946     this.addEvents({
11947         /**
11948          * @event expand
11949          * Fires when the dropdown list is expanded
11950              * @param {Roo.bootstrap.ComboBox} combo This combo box
11951              */
11952         'expand' : true,
11953         /**
11954          * @event collapse
11955          * Fires when the dropdown list is collapsed
11956              * @param {Roo.bootstrap.ComboBox} combo This combo box
11957              */
11958         'collapse' : true,
11959         /**
11960          * @event beforeselect
11961          * Fires before a list item is selected. Return false to cancel the selection.
11962              * @param {Roo.bootstrap.ComboBox} combo This combo box
11963              * @param {Roo.data.Record} record The data record returned from the underlying store
11964              * @param {Number} index The index of the selected item in the dropdown list
11965              */
11966         'beforeselect' : true,
11967         /**
11968          * @event select
11969          * Fires when a list item is selected
11970              * @param {Roo.bootstrap.ComboBox} combo This combo box
11971              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11972              * @param {Number} index The index of the selected item in the dropdown list
11973              */
11974         'select' : true,
11975         /**
11976          * @event beforequery
11977          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11978          * The event object passed has these properties:
11979              * @param {Roo.bootstrap.ComboBox} combo This combo box
11980              * @param {String} query The query
11981              * @param {Boolean} forceAll true to force "all" query
11982              * @param {Boolean} cancel true to cancel the query
11983              * @param {Object} e The query event object
11984              */
11985         'beforequery': true,
11986          /**
11987          * @event add
11988          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11989              * @param {Roo.bootstrap.ComboBox} combo This combo box
11990              */
11991         'add' : true,
11992         /**
11993          * @event edit
11994          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11995              * @param {Roo.bootstrap.ComboBox} combo This combo box
11996              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11997              */
11998         'edit' : true,
11999         /**
12000          * @event remove
12001          * Fires when the remove value from the combobox array
12002              * @param {Roo.bootstrap.ComboBox} combo This combo box
12003              */
12004         'remove' : true,
12005         /**
12006          * @event afterremove
12007          * Fires when the remove value from the combobox array
12008              * @param {Roo.bootstrap.ComboBox} combo This combo box
12009              */
12010         'afterremove' : true,
12011         /**
12012          * @event specialfilter
12013          * Fires when specialfilter
12014             * @param {Roo.bootstrap.ComboBox} combo This combo box
12015             */
12016         'specialfilter' : true,
12017         /**
12018          * @event tick
12019          * Fires when tick the element
12020             * @param {Roo.bootstrap.ComboBox} combo This combo box
12021             */
12022         'tick' : true,
12023         /**
12024          * @event touchviewdisplay
12025          * Fires when touch view require special display (default is using displayField)
12026             * @param {Roo.bootstrap.ComboBox} combo This combo box
12027             * @param {Object} cfg set html .
12028             */
12029         'touchviewdisplay' : true
12030         
12031     });
12032     
12033     this.item = [];
12034     this.tickItems = [];
12035     
12036     this.selectedIndex = -1;
12037     if(this.mode == 'local'){
12038         if(config.queryDelay === undefined){
12039             this.queryDelay = 10;
12040         }
12041         if(config.minChars === undefined){
12042             this.minChars = 0;
12043         }
12044     }
12045 };
12046
12047 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12048      
12049     /**
12050      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12051      * rendering into an Roo.Editor, defaults to false)
12052      */
12053     /**
12054      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12055      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12056      */
12057     /**
12058      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12059      */
12060     /**
12061      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12062      * the dropdown list (defaults to undefined, with no header element)
12063      */
12064
12065      /**
12066      * @cfg {String/Roo.Template} tpl The template to use to render the output
12067      */
12068      
12069      /**
12070      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12071      */
12072     listWidth: undefined,
12073     /**
12074      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12075      * mode = 'remote' or 'text' if mode = 'local')
12076      */
12077     displayField: undefined,
12078     
12079     /**
12080      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12081      * mode = 'remote' or 'value' if mode = 'local'). 
12082      * Note: use of a valueField requires the user make a selection
12083      * in order for a value to be mapped.
12084      */
12085     valueField: undefined,
12086     /**
12087      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12088      */
12089     modalTitle : '',
12090     
12091     /**
12092      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12093      * field's data value (defaults to the underlying DOM element's name)
12094      */
12095     hiddenName: undefined,
12096     /**
12097      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12098      */
12099     listClass: '',
12100     /**
12101      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12102      */
12103     selectedClass: 'active',
12104     
12105     /**
12106      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12107      */
12108     shadow:'sides',
12109     /**
12110      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12111      * anchor positions (defaults to 'tl-bl')
12112      */
12113     listAlign: 'tl-bl?',
12114     /**
12115      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12116      */
12117     maxHeight: 300,
12118     /**
12119      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12120      * query specified by the allQuery config option (defaults to 'query')
12121      */
12122     triggerAction: 'query',
12123     /**
12124      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12125      * (defaults to 4, does not apply if editable = false)
12126      */
12127     minChars : 4,
12128     /**
12129      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12130      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12131      */
12132     typeAhead: false,
12133     /**
12134      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12135      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12136      */
12137     queryDelay: 500,
12138     /**
12139      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12140      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12141      */
12142     pageSize: 0,
12143     /**
12144      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12145      * when editable = true (defaults to false)
12146      */
12147     selectOnFocus:false,
12148     /**
12149      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12150      */
12151     queryParam: 'query',
12152     /**
12153      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12154      * when mode = 'remote' (defaults to 'Loading...')
12155      */
12156     loadingText: 'Loading...',
12157     /**
12158      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12159      */
12160     resizable: false,
12161     /**
12162      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12163      */
12164     handleHeight : 8,
12165     /**
12166      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12167      * traditional select (defaults to true)
12168      */
12169     editable: true,
12170     /**
12171      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12172      */
12173     allQuery: '',
12174     /**
12175      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12176      */
12177     mode: 'remote',
12178     /**
12179      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12180      * listWidth has a higher value)
12181      */
12182     minListWidth : 70,
12183     /**
12184      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12185      * allow the user to set arbitrary text into the field (defaults to false)
12186      */
12187     forceSelection:false,
12188     /**
12189      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12190      * if typeAhead = true (defaults to 250)
12191      */
12192     typeAheadDelay : 250,
12193     /**
12194      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12195      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12196      */
12197     valueNotFoundText : undefined,
12198     /**
12199      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12200      */
12201     blockFocus : false,
12202     
12203     /**
12204      * @cfg {Boolean} disableClear Disable showing of clear button.
12205      */
12206     disableClear : false,
12207     /**
12208      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12209      */
12210     alwaysQuery : false,
12211     
12212     /**
12213      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12214      */
12215     multiple : false,
12216     
12217     /**
12218      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12219      */
12220     invalidClass : "has-warning",
12221     
12222     /**
12223      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12224      */
12225     validClass : "has-success",
12226     
12227     /**
12228      * @cfg {Boolean} specialFilter (true|false) special filter default false
12229      */
12230     specialFilter : false,
12231     
12232     /**
12233      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12234      */
12235     mobileTouchView : true,
12236     
12237     /**
12238      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12239      */
12240     useNativeIOS : false,
12241     
12242     ios_options : false,
12243     
12244     //private
12245     addicon : false,
12246     editicon: false,
12247     
12248     page: 0,
12249     hasQuery: false,
12250     append: false,
12251     loadNext: false,
12252     autoFocus : true,
12253     tickable : false,
12254     btnPosition : 'right',
12255     triggerList : true,
12256     showToggleBtn : true,
12257     animate : true,
12258     emptyResultText: 'Empty',
12259     triggerText : 'Select',
12260     
12261     // element that contains real text value.. (when hidden is used..)
12262     
12263     getAutoCreate : function()
12264     {
12265         var cfg = false;
12266         
12267         /*
12268          * Render classic select for iso
12269          */
12270         
12271         if(Roo.isIOS && this.useNativeIOS){
12272             cfg = this.getAutoCreateNativeIOS();
12273             return cfg;
12274         }
12275         
12276         /*
12277          * Touch Devices
12278          */
12279         
12280         if(Roo.isTouch && this.mobileTouchView){
12281             cfg = this.getAutoCreateTouchView();
12282             return cfg;;
12283         }
12284         
12285         /*
12286          *  Normal ComboBox
12287          */
12288         if(!this.tickable){
12289             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12290             return cfg;
12291         }
12292         
12293         /*
12294          *  ComboBox with tickable selections
12295          */
12296              
12297         var align = this.labelAlign || this.parentLabelAlign();
12298         
12299         cfg = {
12300             cls : 'form-group roo-combobox-tickable' //input-group
12301         };
12302         
12303         var buttons = {
12304             tag : 'div',
12305             cls : 'tickable-buttons',
12306             cn : [
12307                 {
12308                     tag : 'button',
12309                     type : 'button',
12310                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12311                     html : this.triggerText
12312                 },
12313                 {
12314                     tag : 'button',
12315                     type : 'button',
12316                     name : 'ok',
12317                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12318                     html : 'Done'
12319                 },
12320                 {
12321                     tag : 'button',
12322                     type : 'button',
12323                     name : 'cancel',
12324                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12325                     html : 'Cancel'
12326                 }
12327             ]
12328         };
12329         
12330         if(this.editable){
12331             buttons.cn.unshift({
12332                 tag: 'input',
12333                 cls: 'roo-select2-search-field-input'
12334             });
12335         }
12336         
12337         var _this = this;
12338         
12339         Roo.each(buttons.cn, function(c){
12340             if (_this.size) {
12341                 c.cls += ' btn-' + _this.size;
12342             }
12343
12344             if (_this.disabled) {
12345                 c.disabled = true;
12346             }
12347         });
12348         
12349         var box = {
12350             tag: 'div',
12351             cn: [
12352                 {
12353                     tag: 'input',
12354                     type : 'hidden',
12355                     cls: 'form-hidden-field'
12356                 },
12357                 {
12358                     tag: 'ul',
12359                     cls: 'roo-select2-choices',
12360                     cn:[
12361                         {
12362                             tag: 'li',
12363                             cls: 'roo-select2-search-field',
12364                             cn: [
12365
12366                                 buttons
12367                             ]
12368                         }
12369                     ]
12370                 }
12371             ]
12372         };
12373         
12374         var combobox = {
12375             cls: 'roo-select2-container input-group roo-select2-container-multi',
12376             cn: [
12377                 box
12378 //                {
12379 //                    tag: 'ul',
12380 //                    cls: 'typeahead typeahead-long dropdown-menu',
12381 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12382 //                }
12383             ]
12384         };
12385         
12386         if(this.hasFeedback && !this.allowBlank){
12387             
12388             var feedback = {
12389                 tag: 'span',
12390                 cls: 'glyphicon form-control-feedback'
12391             };
12392
12393             combobox.cn.push(feedback);
12394         }
12395         
12396         if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
12397             
12398 //                Roo.log("left and has label");
12399             cfg.cn = [
12400                 {
12401                     tag : 'i',
12402                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12403                     tooltip : 'This field is required'
12404                 },
12405                 {
12406                     tag: 'label',
12407                     'for' :  id,
12408                     cls : 'control-label col-sm-' + this.labelWidth,
12409                     html : this.fieldLabel
12410
12411                 },
12412                 {
12413                     cls : "col-sm-" + (12 - this.labelWidth), 
12414                     cn: [
12415                         combobox
12416                     ]
12417                 }
12418
12419             ];
12420
12421             if(this.indicatorpos == 'right'){
12422                 
12423                 cfg.cn = [
12424                     {
12425                         tag: 'label',
12426                         'for' :  id,
12427                         cls : 'control-label col-sm-' + this.labelWidth,
12428                         html : this.fieldLabel
12429
12430                     },
12431                     {
12432                         tag : 'i',
12433                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12434                         tooltip : 'This field is required'
12435                     },
12436                     {
12437                         cls : "col-sm-" + (12 - this.labelWidth), 
12438                         cn: [
12439                             combobox
12440                         ]
12441                     }
12442
12443                 ];
12444             
12445             }
12446                 
12447                 
12448         } else if ( this.fieldLabel.length) {
12449 //                Roo.log(" label");
12450                  cfg.cn = [
12451                     {
12452                         tag : 'i',
12453                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12454                         tooltip : 'This field is required'
12455                     },
12456                     {
12457                         tag: 'label',
12458                         //cls : 'input-group-addon',
12459                         html : this.fieldLabel
12460                         
12461                     },
12462                     
12463                     combobox
12464                     
12465                 ];
12466                 
12467                 if(this.indicatorpos == 'right'){
12468                     
12469                     cfg.cn = [
12470                         {
12471                             tag: 'label',
12472                             //cls : 'input-group-addon',
12473                             html : this.fieldLabel
12474
12475                         },
12476                         
12477                         {
12478                             tag : 'i',
12479                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12480                             tooltip : 'This field is required'
12481                         },
12482                         
12483                         combobox
12484
12485                     ];
12486                 
12487                 }
12488
12489         } else {
12490             
12491 //                Roo.log(" no label && no align");
12492                 cfg = combobox
12493                      
12494                 
12495         }
12496          
12497         var settings=this;
12498         ['xs','sm','md','lg'].map(function(size){
12499             if (settings[size]) {
12500                 cfg.cls += ' col-' + size + '-' + settings[size];
12501             }
12502         });
12503         
12504         return cfg;
12505         
12506     },
12507     
12508     _initEventsCalled : false,
12509     
12510     // private
12511     initEvents: function()
12512     {   
12513         if (this._initEventsCalled) { // as we call render... prevent looping...
12514             return;
12515         }
12516         this._initEventsCalled = true;
12517         
12518         if (!this.store) {
12519             throw "can not find store for combo";
12520         }
12521         
12522         this.store = Roo.factory(this.store, Roo.data);
12523         
12524         // if we are building from html. then this element is so complex, that we can not really
12525         // use the rendered HTML.
12526         // so we have to trash and replace the previous code.
12527         if (Roo.XComponent.build_from_html) {
12528             
12529             // remove this element....
12530             var e = this.el.dom, k=0;
12531             while (e ) { e = e.previousSibling;  ++k;}
12532
12533             this.el.remove();
12534             
12535             this.el=false;
12536             this.rendered = false;
12537             
12538             this.render(this.parent().getChildContainer(true), k);
12539             
12540             
12541             
12542         }
12543         
12544         if(Roo.isIOS && this.useNativeIOS){
12545             this.initIOSView();
12546             return;
12547         }
12548         
12549         /*
12550          * Touch Devices
12551          */
12552         
12553         if(Roo.isTouch && this.mobileTouchView){
12554             this.initTouchView();
12555             return;
12556         }
12557         
12558         if(this.tickable){
12559             this.initTickableEvents();
12560             return;
12561         }
12562         
12563         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12564         
12565         if(this.hiddenName){
12566             
12567             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12568             
12569             this.hiddenField.dom.value =
12570                 this.hiddenValue !== undefined ? this.hiddenValue :
12571                 this.value !== undefined ? this.value : '';
12572
12573             // prevent input submission
12574             this.el.dom.removeAttribute('name');
12575             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12576              
12577              
12578         }
12579         //if(Roo.isGecko){
12580         //    this.el.dom.setAttribute('autocomplete', 'off');
12581         //}
12582         
12583         var cls = 'x-combo-list';
12584         
12585         //this.list = new Roo.Layer({
12586         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12587         //});
12588         
12589         var _this = this;
12590         
12591         (function(){
12592             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12593             _this.list.setWidth(lw);
12594         }).defer(100);
12595         
12596         this.list.on('mouseover', this.onViewOver, this);
12597         this.list.on('mousemove', this.onViewMove, this);
12598         
12599         this.list.on('scroll', this.onViewScroll, this);
12600         
12601         /*
12602         this.list.swallowEvent('mousewheel');
12603         this.assetHeight = 0;
12604
12605         if(this.title){
12606             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12607             this.assetHeight += this.header.getHeight();
12608         }
12609
12610         this.innerList = this.list.createChild({cls:cls+'-inner'});
12611         this.innerList.on('mouseover', this.onViewOver, this);
12612         this.innerList.on('mousemove', this.onViewMove, this);
12613         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12614         
12615         if(this.allowBlank && !this.pageSize && !this.disableClear){
12616             this.footer = this.list.createChild({cls:cls+'-ft'});
12617             this.pageTb = new Roo.Toolbar(this.footer);
12618            
12619         }
12620         if(this.pageSize){
12621             this.footer = this.list.createChild({cls:cls+'-ft'});
12622             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12623                     {pageSize: this.pageSize});
12624             
12625         }
12626         
12627         if (this.pageTb && this.allowBlank && !this.disableClear) {
12628             var _this = this;
12629             this.pageTb.add(new Roo.Toolbar.Fill(), {
12630                 cls: 'x-btn-icon x-btn-clear',
12631                 text: '&#160;',
12632                 handler: function()
12633                 {
12634                     _this.collapse();
12635                     _this.clearValue();
12636                     _this.onSelect(false, -1);
12637                 }
12638             });
12639         }
12640         if (this.footer) {
12641             this.assetHeight += this.footer.getHeight();
12642         }
12643         */
12644             
12645         if(!this.tpl){
12646             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12647         }
12648
12649         this.view = new Roo.View(this.list, this.tpl, {
12650             singleSelect:true, store: this.store, selectedClass: this.selectedClass
12651         });
12652         //this.view.wrapEl.setDisplayed(false);
12653         this.view.on('click', this.onViewClick, this);
12654         
12655         
12656         
12657         this.store.on('beforeload', this.onBeforeLoad, this);
12658         this.store.on('load', this.onLoad, this);
12659         this.store.on('loadexception', this.onLoadException, this);
12660         /*
12661         if(this.resizable){
12662             this.resizer = new Roo.Resizable(this.list,  {
12663                pinned:true, handles:'se'
12664             });
12665             this.resizer.on('resize', function(r, w, h){
12666                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12667                 this.listWidth = w;
12668                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12669                 this.restrictHeight();
12670             }, this);
12671             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12672         }
12673         */
12674         if(!this.editable){
12675             this.editable = true;
12676             this.setEditable(false);
12677         }
12678         
12679         /*
12680         
12681         if (typeof(this.events.add.listeners) != 'undefined') {
12682             
12683             this.addicon = this.wrap.createChild(
12684                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
12685        
12686             this.addicon.on('click', function(e) {
12687                 this.fireEvent('add', this);
12688             }, this);
12689         }
12690         if (typeof(this.events.edit.listeners) != 'undefined') {
12691             
12692             this.editicon = this.wrap.createChild(
12693                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
12694             if (this.addicon) {
12695                 this.editicon.setStyle('margin-left', '40px');
12696             }
12697             this.editicon.on('click', function(e) {
12698                 
12699                 // we fire even  if inothing is selected..
12700                 this.fireEvent('edit', this, this.lastData );
12701                 
12702             }, this);
12703         }
12704         */
12705         
12706         this.keyNav = new Roo.KeyNav(this.inputEl(), {
12707             "up" : function(e){
12708                 this.inKeyMode = true;
12709                 this.selectPrev();
12710             },
12711
12712             "down" : function(e){
12713                 if(!this.isExpanded()){
12714                     this.onTriggerClick();
12715                 }else{
12716                     this.inKeyMode = true;
12717                     this.selectNext();
12718                 }
12719             },
12720
12721             "enter" : function(e){
12722 //                this.onViewClick();
12723                 //return true;
12724                 this.collapse();
12725                 
12726                 if(this.fireEvent("specialkey", this, e)){
12727                     this.onViewClick(false);
12728                 }
12729                 
12730                 return true;
12731             },
12732
12733             "esc" : function(e){
12734                 this.collapse();
12735             },
12736
12737             "tab" : function(e){
12738                 this.collapse();
12739                 
12740                 if(this.fireEvent("specialkey", this, e)){
12741                     this.onViewClick(false);
12742                 }
12743                 
12744                 return true;
12745             },
12746
12747             scope : this,
12748
12749             doRelay : function(foo, bar, hname){
12750                 if(hname == 'down' || this.scope.isExpanded()){
12751                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12752                 }
12753                 return true;
12754             },
12755
12756             forceKeyDown: true
12757         });
12758         
12759         
12760         this.queryDelay = Math.max(this.queryDelay || 10,
12761                 this.mode == 'local' ? 10 : 250);
12762         
12763         
12764         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12765         
12766         if(this.typeAhead){
12767             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12768         }
12769         if(this.editable !== false){
12770             this.inputEl().on("keyup", this.onKeyUp, this);
12771         }
12772         if(this.forceSelection){
12773             this.inputEl().on('blur', this.doForce, this);
12774         }
12775         
12776         if(this.multiple){
12777             this.choices = this.el.select('ul.roo-select2-choices', true).first();
12778             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12779         }
12780     },
12781     
12782     initTickableEvents: function()
12783     {   
12784         this.createList();
12785         
12786         if(this.hiddenName){
12787             
12788             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12789             
12790             this.hiddenField.dom.value =
12791                 this.hiddenValue !== undefined ? this.hiddenValue :
12792                 this.value !== undefined ? this.value : '';
12793
12794             // prevent input submission
12795             this.el.dom.removeAttribute('name');
12796             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12797              
12798              
12799         }
12800         
12801 //        this.list = this.el.select('ul.dropdown-menu',true).first();
12802         
12803         this.choices = this.el.select('ul.roo-select2-choices', true).first();
12804         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12805         if(this.triggerList){
12806             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12807         }
12808          
12809         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12810         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12811         
12812         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12813         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12814         
12815         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12816         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12817         
12818         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12819         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12820         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12821         
12822         this.okBtn.hide();
12823         this.cancelBtn.hide();
12824         
12825         var _this = this;
12826         
12827         (function(){
12828             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12829             _this.list.setWidth(lw);
12830         }).defer(100);
12831         
12832         this.list.on('mouseover', this.onViewOver, this);
12833         this.list.on('mousemove', this.onViewMove, this);
12834         
12835         this.list.on('scroll', this.onViewScroll, this);
12836         
12837         if(!this.tpl){
12838             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}" type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></li>';
12839         }
12840
12841         this.view = new Roo.View(this.list, this.tpl, {
12842             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12843         });
12844         
12845         //this.view.wrapEl.setDisplayed(false);
12846         this.view.on('click', this.onViewClick, this);
12847         
12848         
12849         
12850         this.store.on('beforeload', this.onBeforeLoad, this);
12851         this.store.on('load', this.onLoad, this);
12852         this.store.on('loadexception', this.onLoadException, this);
12853         
12854         if(this.editable){
12855             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12856                 "up" : function(e){
12857                     this.inKeyMode = true;
12858                     this.selectPrev();
12859                 },
12860
12861                 "down" : function(e){
12862                     this.inKeyMode = true;
12863                     this.selectNext();
12864                 },
12865
12866                 "enter" : function(e){
12867                     if(this.fireEvent("specialkey", this, e)){
12868                         this.onViewClick(false);
12869                     }
12870                     
12871                     return true;
12872                 },
12873
12874                 "esc" : function(e){
12875                     this.onTickableFooterButtonClick(e, false, false);
12876                 },
12877
12878                 "tab" : function(e){
12879                     this.fireEvent("specialkey", this, e);
12880                     
12881                     this.onTickableFooterButtonClick(e, false, false);
12882                     
12883                     return true;
12884                 },
12885
12886                 scope : this,
12887
12888                 doRelay : function(e, fn, key){
12889                     if(this.scope.isExpanded()){
12890                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12891                     }
12892                     return true;
12893                 },
12894
12895                 forceKeyDown: true
12896             });
12897         }
12898         
12899         this.queryDelay = Math.max(this.queryDelay || 10,
12900                 this.mode == 'local' ? 10 : 250);
12901         
12902         
12903         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12904         
12905         if(this.typeAhead){
12906             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12907         }
12908         
12909         if(this.editable !== false){
12910             this.tickableInputEl().on("keyup", this.onKeyUp, this);
12911         }
12912         
12913     },
12914
12915     onDestroy : function(){
12916         if(this.view){
12917             this.view.setStore(null);
12918             this.view.el.removeAllListeners();
12919             this.view.el.remove();
12920             this.view.purgeListeners();
12921         }
12922         if(this.list){
12923             this.list.dom.innerHTML  = '';
12924         }
12925         
12926         if(this.store){
12927             this.store.un('beforeload', this.onBeforeLoad, this);
12928             this.store.un('load', this.onLoad, this);
12929             this.store.un('loadexception', this.onLoadException, this);
12930         }
12931         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12932     },
12933
12934     // private
12935     fireKey : function(e){
12936         if(e.isNavKeyPress() && !this.list.isVisible()){
12937             this.fireEvent("specialkey", this, e);
12938         }
12939     },
12940
12941     // private
12942     onResize: function(w, h){
12943 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12944 //        
12945 //        if(typeof w != 'number'){
12946 //            // we do not handle it!?!?
12947 //            return;
12948 //        }
12949 //        var tw = this.trigger.getWidth();
12950 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
12951 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
12952 //        var x = w - tw;
12953 //        this.inputEl().setWidth( this.adjustWidth('input', x));
12954 //            
12955 //        //this.trigger.setStyle('left', x+'px');
12956 //        
12957 //        if(this.list && this.listWidth === undefined){
12958 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12959 //            this.list.setWidth(lw);
12960 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12961 //        }
12962         
12963     
12964         
12965     },
12966
12967     /**
12968      * Allow or prevent the user from directly editing the field text.  If false is passed,
12969      * the user will only be able to select from the items defined in the dropdown list.  This method
12970      * is the runtime equivalent of setting the 'editable' config option at config time.
12971      * @param {Boolean} value True to allow the user to directly edit the field text
12972      */
12973     setEditable : function(value){
12974         if(value == this.editable){
12975             return;
12976         }
12977         this.editable = value;
12978         if(!value){
12979             this.inputEl().dom.setAttribute('readOnly', true);
12980             this.inputEl().on('mousedown', this.onTriggerClick,  this);
12981             this.inputEl().addClass('x-combo-noedit');
12982         }else{
12983             this.inputEl().dom.setAttribute('readOnly', false);
12984             this.inputEl().un('mousedown', this.onTriggerClick,  this);
12985             this.inputEl().removeClass('x-combo-noedit');
12986         }
12987     },
12988
12989     // private
12990     
12991     onBeforeLoad : function(combo,opts){
12992         if(!this.hasFocus){
12993             return;
12994         }
12995          if (!opts.add) {
12996             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12997          }
12998         this.restrictHeight();
12999         this.selectedIndex = -1;
13000     },
13001
13002     // private
13003     onLoad : function(){
13004         
13005         this.hasQuery = false;
13006         
13007         if(!this.hasFocus){
13008             return;
13009         }
13010         
13011         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13012             this.loading.hide();
13013         }
13014              
13015         if(this.store.getCount() > 0){
13016             this.expand();
13017             this.restrictHeight();
13018             if(this.lastQuery == this.allQuery){
13019                 if(this.editable && !this.tickable){
13020                     this.inputEl().dom.select();
13021                 }
13022                 
13023                 if(
13024                     !this.selectByValue(this.value, true) &&
13025                     this.autoFocus && 
13026                     (
13027                         !this.store.lastOptions ||
13028                         typeof(this.store.lastOptions.add) == 'undefined' || 
13029                         this.store.lastOptions.add != true
13030                     )
13031                 ){
13032                     this.select(0, true);
13033                 }
13034             }else{
13035                 if(this.autoFocus){
13036                     this.selectNext();
13037                 }
13038                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13039                     this.taTask.delay(this.typeAheadDelay);
13040                 }
13041             }
13042         }else{
13043             this.onEmptyResults();
13044         }
13045         
13046         //this.el.focus();
13047     },
13048     // private
13049     onLoadException : function()
13050     {
13051         this.hasQuery = false;
13052         
13053         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13054             this.loading.hide();
13055         }
13056         
13057         if(this.tickable && this.editable){
13058             return;
13059         }
13060         
13061         this.collapse();
13062         // only causes errors at present
13063         //Roo.log(this.store.reader.jsonData);
13064         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13065             // fixme
13066             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13067         //}
13068         
13069         
13070     },
13071     // private
13072     onTypeAhead : function(){
13073         if(this.store.getCount() > 0){
13074             var r = this.store.getAt(0);
13075             var newValue = r.data[this.displayField];
13076             var len = newValue.length;
13077             var selStart = this.getRawValue().length;
13078             
13079             if(selStart != len){
13080                 this.setRawValue(newValue);
13081                 this.selectText(selStart, newValue.length);
13082             }
13083         }
13084     },
13085
13086     // private
13087     onSelect : function(record, index){
13088         
13089         if(this.fireEvent('beforeselect', this, record, index) !== false){
13090         
13091             this.setFromData(index > -1 ? record.data : false);
13092             
13093             this.collapse();
13094             this.fireEvent('select', this, record, index);
13095         }
13096     },
13097
13098     /**
13099      * Returns the currently selected field value or empty string if no value is set.
13100      * @return {String} value The selected value
13101      */
13102     getValue : function()
13103     {
13104         if(Roo.isIOS && this.useNativeIOS){
13105             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13106         }
13107         
13108         if(this.multiple){
13109             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13110         }
13111         
13112         if(this.valueField){
13113             return typeof this.value != 'undefined' ? this.value : '';
13114         }else{
13115             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13116         }
13117     },
13118     
13119     getRawValue : function()
13120     {
13121         if(Roo.isIOS && this.useNativeIOS){
13122             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13123         }
13124         
13125         var v = this.inputEl().getValue();
13126         
13127         return v;
13128     },
13129
13130     /**
13131      * Clears any text/value currently set in the field
13132      */
13133     clearValue : function(){
13134         
13135         if(this.hiddenField){
13136             this.hiddenField.dom.value = '';
13137         }
13138         this.value = '';
13139         this.setRawValue('');
13140         this.lastSelectionText = '';
13141         this.lastData = false;
13142         
13143         var close = this.closeTriggerEl();
13144         
13145         if(close){
13146             close.hide();
13147         }
13148         
13149         this.validate();
13150         
13151     },
13152
13153     /**
13154      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13155      * will be displayed in the field.  If the value does not match the data value of an existing item,
13156      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13157      * Otherwise the field will be blank (although the value will still be set).
13158      * @param {String} value The value to match
13159      */
13160     setValue : function(v)
13161     {
13162         if(Roo.isIOS && this.useNativeIOS){
13163             this.setIOSValue(v);
13164             return;
13165         }
13166         
13167         if(this.multiple){
13168             this.syncValue();
13169             return;
13170         }
13171         
13172         var text = v;
13173         if(this.valueField){
13174             var r = this.findRecord(this.valueField, v);
13175             if(r){
13176                 text = r.data[this.displayField];
13177             }else if(this.valueNotFoundText !== undefined){
13178                 text = this.valueNotFoundText;
13179             }
13180         }
13181         this.lastSelectionText = text;
13182         if(this.hiddenField){
13183             this.hiddenField.dom.value = v;
13184         }
13185         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13186         this.value = v;
13187         
13188         var close = this.closeTriggerEl();
13189         
13190         if(close){
13191             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13192         }
13193         
13194         this.validate();
13195     },
13196     /**
13197      * @property {Object} the last set data for the element
13198      */
13199     
13200     lastData : false,
13201     /**
13202      * Sets the value of the field based on a object which is related to the record format for the store.
13203      * @param {Object} value the value to set as. or false on reset?
13204      */
13205     setFromData : function(o){
13206         
13207         if(this.multiple){
13208             this.addItem(o);
13209             return;
13210         }
13211             
13212         var dv = ''; // display value
13213         var vv = ''; // value value..
13214         this.lastData = o;
13215         if (this.displayField) {
13216             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13217         } else {
13218             // this is an error condition!!!
13219             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13220         }
13221         
13222         if(this.valueField){
13223             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13224         }
13225         
13226         var close = this.closeTriggerEl();
13227         
13228         if(close){
13229             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13230         }
13231         
13232         if(this.hiddenField){
13233             this.hiddenField.dom.value = vv;
13234             
13235             this.lastSelectionText = dv;
13236             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13237             this.value = vv;
13238             return;
13239         }
13240         // no hidden field.. - we store the value in 'value', but still display
13241         // display field!!!!
13242         this.lastSelectionText = dv;
13243         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13244         this.value = vv;
13245         
13246         
13247         
13248     },
13249     // private
13250     reset : function(){
13251         // overridden so that last data is reset..
13252         
13253         if(this.multiple){
13254             this.clearItem();
13255             return;
13256         }
13257         
13258         this.setValue(this.originalValue);
13259         //this.clearInvalid();
13260         this.lastData = false;
13261         if (this.view) {
13262             this.view.clearSelections();
13263         }
13264         
13265         this.validate();
13266     },
13267     // private
13268     findRecord : function(prop, value){
13269         var record;
13270         if(this.store.getCount() > 0){
13271             this.store.each(function(r){
13272                 if(r.data[prop] == value){
13273                     record = r;
13274                     return false;
13275                 }
13276                 return true;
13277             });
13278         }
13279         return record;
13280     },
13281     
13282     getName: function()
13283     {
13284         // returns hidden if it's set..
13285         if (!this.rendered) {return ''};
13286         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13287         
13288     },
13289     // private
13290     onViewMove : function(e, t){
13291         this.inKeyMode = false;
13292     },
13293
13294     // private
13295     onViewOver : function(e, t){
13296         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13297             return;
13298         }
13299         var item = this.view.findItemFromChild(t);
13300         
13301         if(item){
13302             var index = this.view.indexOf(item);
13303             this.select(index, false);
13304         }
13305     },
13306
13307     // private
13308     onViewClick : function(view, doFocus, el, e)
13309     {
13310         var index = this.view.getSelectedIndexes()[0];
13311         
13312         var r = this.store.getAt(index);
13313         
13314         if(this.tickable){
13315             
13316             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13317                 return;
13318             }
13319             
13320             var rm = false;
13321             var _this = this;
13322             
13323             Roo.each(this.tickItems, function(v,k){
13324                 
13325                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13326                     Roo.log(v);
13327                     _this.tickItems.splice(k, 1);
13328                     
13329                     if(typeof(e) == 'undefined' && view == false){
13330                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13331                     }
13332                     
13333                     rm = true;
13334                     return;
13335                 }
13336             });
13337             
13338             if(rm){
13339                 return;
13340             }
13341             
13342             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13343                 this.tickItems.push(r.data);
13344             }
13345             
13346             if(typeof(e) == 'undefined' && view == false){
13347                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13348             }
13349                     
13350             return;
13351         }
13352         
13353         if(r){
13354             this.onSelect(r, index);
13355         }
13356         if(doFocus !== false && !this.blockFocus){
13357             this.inputEl().focus();
13358         }
13359     },
13360
13361     // private
13362     restrictHeight : function(){
13363         //this.innerList.dom.style.height = '';
13364         //var inner = this.innerList.dom;
13365         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13366         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13367         //this.list.beginUpdate();
13368         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13369         this.list.alignTo(this.inputEl(), this.listAlign);
13370         this.list.alignTo(this.inputEl(), this.listAlign);
13371         //this.list.endUpdate();
13372     },
13373
13374     // private
13375     onEmptyResults : function(){
13376         
13377         if(this.tickable && this.editable){
13378             this.restrictHeight();
13379             return;
13380         }
13381         
13382         this.collapse();
13383     },
13384
13385     /**
13386      * Returns true if the dropdown list is expanded, else false.
13387      */
13388     isExpanded : function(){
13389         return this.list.isVisible();
13390     },
13391
13392     /**
13393      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13394      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13395      * @param {String} value The data value of the item to select
13396      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13397      * selected item if it is not currently in view (defaults to true)
13398      * @return {Boolean} True if the value matched an item in the list, else false
13399      */
13400     selectByValue : function(v, scrollIntoView){
13401         if(v !== undefined && v !== null){
13402             var r = this.findRecord(this.valueField || this.displayField, v);
13403             if(r){
13404                 this.select(this.store.indexOf(r), scrollIntoView);
13405                 return true;
13406             }
13407         }
13408         return false;
13409     },
13410
13411     /**
13412      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13413      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13414      * @param {Number} index The zero-based index of the list item to select
13415      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13416      * selected item if it is not currently in view (defaults to true)
13417      */
13418     select : function(index, scrollIntoView){
13419         this.selectedIndex = index;
13420         this.view.select(index);
13421         if(scrollIntoView !== false){
13422             var el = this.view.getNode(index);
13423             /*
13424              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13425              */
13426             if(el){
13427                 this.list.scrollChildIntoView(el, false);
13428             }
13429         }
13430     },
13431
13432     // private
13433     selectNext : function(){
13434         var ct = this.store.getCount();
13435         if(ct > 0){
13436             if(this.selectedIndex == -1){
13437                 this.select(0);
13438             }else if(this.selectedIndex < ct-1){
13439                 this.select(this.selectedIndex+1);
13440             }
13441         }
13442     },
13443
13444     // private
13445     selectPrev : function(){
13446         var ct = this.store.getCount();
13447         if(ct > 0){
13448             if(this.selectedIndex == -1){
13449                 this.select(0);
13450             }else if(this.selectedIndex != 0){
13451                 this.select(this.selectedIndex-1);
13452             }
13453         }
13454     },
13455
13456     // private
13457     onKeyUp : function(e){
13458         if(this.editable !== false && !e.isSpecialKey()){
13459             this.lastKey = e.getKey();
13460             this.dqTask.delay(this.queryDelay);
13461         }
13462     },
13463
13464     // private
13465     validateBlur : function(){
13466         return !this.list || !this.list.isVisible();   
13467     },
13468
13469     // private
13470     initQuery : function(){
13471         
13472         var v = this.getRawValue();
13473         
13474         if(this.tickable && this.editable){
13475             v = this.tickableInputEl().getValue();
13476         }
13477         
13478         this.doQuery(v);
13479     },
13480
13481     // private
13482     doForce : function(){
13483         if(this.inputEl().dom.value.length > 0){
13484             this.inputEl().dom.value =
13485                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13486              
13487         }
13488     },
13489
13490     /**
13491      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
13492      * query allowing the query action to be canceled if needed.
13493      * @param {String} query The SQL query to execute
13494      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13495      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
13496      * saved in the current store (defaults to false)
13497      */
13498     doQuery : function(q, forceAll){
13499         
13500         if(q === undefined || q === null){
13501             q = '';
13502         }
13503         var qe = {
13504             query: q,
13505             forceAll: forceAll,
13506             combo: this,
13507             cancel:false
13508         };
13509         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13510             return false;
13511         }
13512         q = qe.query;
13513         
13514         forceAll = qe.forceAll;
13515         if(forceAll === true || (q.length >= this.minChars)){
13516             
13517             this.hasQuery = true;
13518             
13519             if(this.lastQuery != q || this.alwaysQuery){
13520                 this.lastQuery = q;
13521                 if(this.mode == 'local'){
13522                     this.selectedIndex = -1;
13523                     if(forceAll){
13524                         this.store.clearFilter();
13525                     }else{
13526                         
13527                         if(this.specialFilter){
13528                             this.fireEvent('specialfilter', this);
13529                             this.onLoad();
13530                             return;
13531                         }
13532                         
13533                         this.store.filter(this.displayField, q);
13534                     }
13535                     
13536                     this.store.fireEvent("datachanged", this.store);
13537                     
13538                     this.onLoad();
13539                     
13540                     
13541                 }else{
13542                     
13543                     this.store.baseParams[this.queryParam] = q;
13544                     
13545                     var options = {params : this.getParams(q)};
13546                     
13547                     if(this.loadNext){
13548                         options.add = true;
13549                         options.params.start = this.page * this.pageSize;
13550                     }
13551                     
13552                     this.store.load(options);
13553                     
13554                     /*
13555                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
13556                      *  we should expand the list on onLoad
13557                      *  so command out it
13558                      */
13559 //                    this.expand();
13560                 }
13561             }else{
13562                 this.selectedIndex = -1;
13563                 this.onLoad();   
13564             }
13565         }
13566         
13567         this.loadNext = false;
13568     },
13569     
13570     // private
13571     getParams : function(q){
13572         var p = {};
13573         //p[this.queryParam] = q;
13574         
13575         if(this.pageSize){
13576             p.start = 0;
13577             p.limit = this.pageSize;
13578         }
13579         return p;
13580     },
13581
13582     /**
13583      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13584      */
13585     collapse : function(){
13586         if(!this.isExpanded()){
13587             return;
13588         }
13589         
13590         this.list.hide();
13591         
13592         if(this.tickable){
13593             this.hasFocus = false;
13594             this.okBtn.hide();
13595             this.cancelBtn.hide();
13596             this.trigger.show();
13597             
13598             if(this.editable){
13599                 this.tickableInputEl().dom.value = '';
13600                 this.tickableInputEl().blur();
13601             }
13602             
13603         }
13604         
13605         Roo.get(document).un('mousedown', this.collapseIf, this);
13606         Roo.get(document).un('mousewheel', this.collapseIf, this);
13607         if (!this.editable) {
13608             Roo.get(document).un('keydown', this.listKeyPress, this);
13609         }
13610         this.fireEvent('collapse', this);
13611         
13612         this.validate();
13613     },
13614
13615     // private
13616     collapseIf : function(e){
13617         var in_combo  = e.within(this.el);
13618         var in_list =  e.within(this.list);
13619         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13620         
13621         if (in_combo || in_list || is_list) {
13622             //e.stopPropagation();
13623             return;
13624         }
13625         
13626         if(this.tickable){
13627             this.onTickableFooterButtonClick(e, false, false);
13628         }
13629
13630         this.collapse();
13631         
13632     },
13633
13634     /**
13635      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13636      */
13637     expand : function(){
13638        
13639         if(this.isExpanded() || !this.hasFocus){
13640             return;
13641         }
13642         
13643         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13644         this.list.setWidth(lw);
13645         
13646         
13647          Roo.log('expand');
13648         
13649         this.list.show();
13650         
13651         this.restrictHeight();
13652         
13653         if(this.tickable){
13654             
13655             this.tickItems = Roo.apply([], this.item);
13656             
13657             this.okBtn.show();
13658             this.cancelBtn.show();
13659             this.trigger.hide();
13660             
13661             if(this.editable){
13662                 this.tickableInputEl().focus();
13663             }
13664             
13665         }
13666         
13667         Roo.get(document).on('mousedown', this.collapseIf, this);
13668         Roo.get(document).on('mousewheel', this.collapseIf, this);
13669         if (!this.editable) {
13670             Roo.get(document).on('keydown', this.listKeyPress, this);
13671         }
13672         
13673         this.fireEvent('expand', this);
13674     },
13675
13676     // private
13677     // Implements the default empty TriggerField.onTriggerClick function
13678     onTriggerClick : function(e)
13679     {
13680         Roo.log('trigger click');
13681         
13682         if(this.disabled || !this.triggerList){
13683             return;
13684         }
13685         
13686         this.page = 0;
13687         this.loadNext = false;
13688         
13689         if(this.isExpanded()){
13690             this.collapse();
13691             if (!this.blockFocus) {
13692                 this.inputEl().focus();
13693             }
13694             
13695         }else {
13696             this.hasFocus = true;
13697             if(this.triggerAction == 'all') {
13698                 this.doQuery(this.allQuery, true);
13699             } else {
13700                 this.doQuery(this.getRawValue());
13701             }
13702             if (!this.blockFocus) {
13703                 this.inputEl().focus();
13704             }
13705         }
13706     },
13707     
13708     onTickableTriggerClick : function(e)
13709     {
13710         if(this.disabled){
13711             return;
13712         }
13713         
13714         this.page = 0;
13715         this.loadNext = false;
13716         this.hasFocus = true;
13717         
13718         if(this.triggerAction == 'all') {
13719             this.doQuery(this.allQuery, true);
13720         } else {
13721             this.doQuery(this.getRawValue());
13722         }
13723     },
13724     
13725     onSearchFieldClick : function(e)
13726     {
13727         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13728             this.onTickableFooterButtonClick(e, false, false);
13729             return;
13730         }
13731         
13732         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13733             return;
13734         }
13735         
13736         this.page = 0;
13737         this.loadNext = false;
13738         this.hasFocus = true;
13739         
13740         if(this.triggerAction == 'all') {
13741             this.doQuery(this.allQuery, true);
13742         } else {
13743             this.doQuery(this.getRawValue());
13744         }
13745     },
13746     
13747     listKeyPress : function(e)
13748     {
13749         //Roo.log('listkeypress');
13750         // scroll to first matching element based on key pres..
13751         if (e.isSpecialKey()) {
13752             return false;
13753         }
13754         var k = String.fromCharCode(e.getKey()).toUpperCase();
13755         //Roo.log(k);
13756         var match  = false;
13757         var csel = this.view.getSelectedNodes();
13758         var cselitem = false;
13759         if (csel.length) {
13760             var ix = this.view.indexOf(csel[0]);
13761             cselitem  = this.store.getAt(ix);
13762             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13763                 cselitem = false;
13764             }
13765             
13766         }
13767         
13768         this.store.each(function(v) { 
13769             if (cselitem) {
13770                 // start at existing selection.
13771                 if (cselitem.id == v.id) {
13772                     cselitem = false;
13773                 }
13774                 return true;
13775             }
13776                 
13777             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13778                 match = this.store.indexOf(v);
13779                 return false;
13780             }
13781             return true;
13782         }, this);
13783         
13784         if (match === false) {
13785             return true; // no more action?
13786         }
13787         // scroll to?
13788         this.view.select(match);
13789         var sn = Roo.get(this.view.getSelectedNodes()[0]);
13790         sn.scrollIntoView(sn.dom.parentNode, false);
13791     },
13792     
13793     onViewScroll : function(e, t){
13794         
13795         if(this.view.el.getScroll().top == 0 ||this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
13796             return;
13797         }
13798         
13799         this.hasQuery = true;
13800         
13801         this.loading = this.list.select('.loading', true).first();
13802         
13803         if(this.loading === null){
13804             this.list.createChild({
13805                 tag: 'div',
13806                 cls: 'loading roo-select2-more-results roo-select2-active',
13807                 html: 'Loading more results...'
13808             });
13809             
13810             this.loading = this.list.select('.loading', true).first();
13811             
13812             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13813             
13814             this.loading.hide();
13815         }
13816         
13817         this.loading.show();
13818         
13819         var _combo = this;
13820         
13821         this.page++;
13822         this.loadNext = true;
13823         
13824         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13825         
13826         return;
13827     },
13828     
13829     addItem : function(o)
13830     {   
13831         var dv = ''; // display value
13832         
13833         if (this.displayField) {
13834             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13835         } else {
13836             // this is an error condition!!!
13837             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13838         }
13839         
13840         if(!dv.length){
13841             return;
13842         }
13843         
13844         var choice = this.choices.createChild({
13845             tag: 'li',
13846             cls: 'roo-select2-search-choice',
13847             cn: [
13848                 {
13849                     tag: 'div',
13850                     html: dv
13851                 },
13852                 {
13853                     tag: 'a',
13854                     href: '#',
13855                     cls: 'roo-select2-search-choice-close',
13856                     tabindex: '-1'
13857                 }
13858             ]
13859             
13860         }, this.searchField);
13861         
13862         var close = choice.select('a.roo-select2-search-choice-close', true).first();
13863         
13864         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13865         
13866         this.item.push(o);
13867         
13868         this.lastData = o;
13869         
13870         this.syncValue();
13871         
13872         this.inputEl().dom.value = '';
13873         
13874         this.validate();
13875     },
13876     
13877     onRemoveItem : function(e, _self, o)
13878     {
13879         e.preventDefault();
13880         
13881         this.lastItem = Roo.apply([], this.item);
13882         
13883         var index = this.item.indexOf(o.data) * 1;
13884         
13885         if( index < 0){
13886             Roo.log('not this item?!');
13887             return;
13888         }
13889         
13890         this.item.splice(index, 1);
13891         o.item.remove();
13892         
13893         this.syncValue();
13894         
13895         this.fireEvent('remove', this, e);
13896         
13897         this.validate();
13898         
13899     },
13900     
13901     syncValue : function()
13902     {
13903         if(!this.item.length){
13904             this.clearValue();
13905             return;
13906         }
13907             
13908         var value = [];
13909         var _this = this;
13910         Roo.each(this.item, function(i){
13911             if(_this.valueField){
13912                 value.push(i[_this.valueField]);
13913                 return;
13914             }
13915
13916             value.push(i);
13917         });
13918
13919         this.value = value.join(',');
13920
13921         if(this.hiddenField){
13922             this.hiddenField.dom.value = this.value;
13923         }
13924         
13925         this.store.fireEvent("datachanged", this.store);
13926         
13927         this.validate();
13928     },
13929     
13930     clearItem : function()
13931     {
13932         if(!this.multiple){
13933             return;
13934         }
13935         
13936         this.item = [];
13937         
13938         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
13939            c.remove();
13940         });
13941         
13942         this.syncValue();
13943         
13944         this.validate();
13945         
13946         if(this.tickable && !Roo.isTouch){
13947             this.view.refresh();
13948         }
13949     },
13950     
13951     inputEl: function ()
13952     {
13953         if(Roo.isIOS && this.useNativeIOS){
13954             return this.el.select('select.roo-ios-select', true).first();
13955         }
13956         
13957         if(Roo.isTouch && this.mobileTouchView){
13958             return this.el.select('input.form-control',true).first();
13959         }
13960         
13961         if(this.tickable){
13962             return this.searchField;
13963         }
13964         
13965         return this.el.select('input.form-control',true).first();
13966     },
13967     
13968     onTickableFooterButtonClick : function(e, btn, el)
13969     {
13970         e.preventDefault();
13971         
13972         this.lastItem = Roo.apply([], this.item);
13973         
13974         if(btn && btn.name == 'cancel'){
13975             this.tickItems = Roo.apply([], this.item);
13976             this.collapse();
13977             return;
13978         }
13979         
13980         this.clearItem();
13981         
13982         var _this = this;
13983         
13984         Roo.each(this.tickItems, function(o){
13985             _this.addItem(o);
13986         });
13987         
13988         this.collapse();
13989         
13990     },
13991     
13992     validate : function()
13993     {
13994         var v = this.getRawValue();
13995         
13996         if(this.multiple){
13997             v = this.getValue();
13998         }
13999         
14000         if(this.disabled || this.allowBlank || v.length){
14001             this.markValid();
14002             return true;
14003         }
14004         
14005         this.markInvalid();
14006         return false;
14007     },
14008     
14009     tickableInputEl : function()
14010     {
14011         if(!this.tickable || !this.editable){
14012             return this.inputEl();
14013         }
14014         
14015         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14016     },
14017     
14018     
14019     getAutoCreateTouchView : function()
14020     {
14021         var id = Roo.id();
14022         
14023         var cfg = {
14024             cls: 'form-group' //input-group
14025         };
14026         
14027         var input =  {
14028             tag: 'input',
14029             id : id,
14030             type : this.inputType,
14031             cls : 'form-control x-combo-noedit',
14032             autocomplete: 'new-password',
14033             placeholder : this.placeholder || '',
14034             readonly : true
14035         };
14036         
14037         if (this.name) {
14038             input.name = this.name;
14039         }
14040         
14041         if (this.size) {
14042             input.cls += ' input-' + this.size;
14043         }
14044         
14045         if (this.disabled) {
14046             input.disabled = true;
14047         }
14048         
14049         var inputblock = {
14050             cls : '',
14051             cn : [
14052                 input
14053             ]
14054         };
14055         
14056         if(this.before){
14057             inputblock.cls += ' input-group';
14058             
14059             inputblock.cn.unshift({
14060                 tag :'span',
14061                 cls : 'input-group-addon',
14062                 html : this.before
14063             });
14064         }
14065         
14066         if(this.removable && !this.multiple){
14067             inputblock.cls += ' roo-removable';
14068             
14069             inputblock.cn.push({
14070                 tag: 'button',
14071                 html : 'x',
14072                 cls : 'roo-combo-removable-btn close'
14073             });
14074         }
14075
14076         if(this.hasFeedback && !this.allowBlank){
14077             
14078             inputblock.cls += ' has-feedback';
14079             
14080             inputblock.cn.push({
14081                 tag: 'span',
14082                 cls: 'glyphicon form-control-feedback'
14083             });
14084             
14085         }
14086         
14087         if (this.after) {
14088             
14089             inputblock.cls += (this.before) ? '' : ' input-group';
14090             
14091             inputblock.cn.push({
14092                 tag :'span',
14093                 cls : 'input-group-addon',
14094                 html : this.after
14095             });
14096         }
14097
14098         var box = {
14099             tag: 'div',
14100             cn: [
14101                 {
14102                     tag: 'input',
14103                     type : 'hidden',
14104                     cls: 'form-hidden-field'
14105                 },
14106                 inputblock
14107             ]
14108             
14109         };
14110         
14111         if(this.multiple){
14112             box = {
14113                 tag: 'div',
14114                 cn: [
14115                     {
14116                         tag: 'input',
14117                         type : 'hidden',
14118                         cls: 'form-hidden-field'
14119                     },
14120                     {
14121                         tag: 'ul',
14122                         cls: 'roo-select2-choices',
14123                         cn:[
14124                             {
14125                                 tag: 'li',
14126                                 cls: 'roo-select2-search-field',
14127                                 cn: [
14128
14129                                     inputblock
14130                                 ]
14131                             }
14132                         ]
14133                     }
14134                 ]
14135             }
14136         };
14137         
14138         var combobox = {
14139             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14140             cn: [
14141                 box
14142             ]
14143         };
14144         
14145         if(!this.multiple && this.showToggleBtn){
14146             
14147             var caret = {
14148                         tag: 'span',
14149                         cls: 'caret'
14150             };
14151             
14152             if (this.caret != false) {
14153                 caret = {
14154                      tag: 'i',
14155                      cls: 'fa fa-' + this.caret
14156                 };
14157                 
14158             }
14159             
14160             combobox.cn.push({
14161                 tag :'span',
14162                 cls : 'input-group-addon btn dropdown-toggle',
14163                 cn : [
14164                     caret,
14165                     {
14166                         tag: 'span',
14167                         cls: 'combobox-clear',
14168                         cn  : [
14169                             {
14170                                 tag : 'i',
14171                                 cls: 'icon-remove'
14172                             }
14173                         ]
14174                     }
14175                 ]
14176
14177             })
14178         }
14179         
14180         if(this.multiple){
14181             combobox.cls += ' roo-select2-container-multi';
14182         }
14183         
14184         var align = this.labelAlign || this.parentLabelAlign();
14185         
14186         cfg.cn = combobox;
14187         
14188         if(this.fieldLabel.length && this.labelWidth){
14189             
14190             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
14191             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
14192             
14193             cfg.cn = [
14194                 {
14195                    tag : 'i',
14196                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14197                    tooltip : 'This field is required'
14198                 },
14199                 {
14200                     tag: 'label',
14201                     cls : 'control-label ' + lw,
14202                     html : this.fieldLabel
14203
14204                 },
14205                 {
14206                     cls : cw, 
14207                     cn: [
14208                         combobox
14209                     ]
14210                 }
14211             ];
14212             
14213             if(this.indicatorpos == 'right'){
14214                 cfg.cn = [
14215                     {
14216                         tag: 'label',
14217                         cls : 'control-label ' + lw,
14218                         html : this.fieldLabel
14219
14220                     },
14221                     {
14222                        tag : 'i',
14223                        cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14224                        tooltip : 'This field is required'
14225                     },
14226                     {
14227                         cls : cw, 
14228                         cn: [
14229                             combobox
14230                         ]
14231                     }
14232                 ];
14233             }
14234         }
14235         
14236         var settings = this;
14237         
14238         ['xs','sm','md','lg'].map(function(size){
14239             if (settings[size]) {
14240                 cfg.cls += ' col-' + size + '-' + settings[size];
14241             }
14242         });
14243         
14244         return cfg;
14245     },
14246     
14247     initTouchView : function()
14248     {
14249         this.renderTouchView();
14250         
14251         this.touchViewEl.on('scroll', function(){
14252             this.el.dom.scrollTop = 0;
14253         }, this);
14254         
14255         this.originalValue = this.getValue();
14256         
14257         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14258         
14259         this.inputEl().on("click", this.showTouchView, this);
14260         this.triggerEl.on("click", this.showTouchView, this);
14261         
14262         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14263         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14264         
14265         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14266         
14267         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14268         this.store.on('load', this.onTouchViewLoad, this);
14269         this.store.on('loadexception', this.onTouchViewLoadException, this);
14270         
14271         if(this.hiddenName){
14272             
14273             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14274             
14275             this.hiddenField.dom.value =
14276                 this.hiddenValue !== undefined ? this.hiddenValue :
14277                 this.value !== undefined ? this.value : '';
14278         
14279             this.el.dom.removeAttribute('name');
14280             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14281         }
14282         
14283         if(this.multiple){
14284             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14285             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14286         }
14287         
14288         if(this.removable && !this.multiple){
14289             var close = this.closeTriggerEl();
14290             if(close){
14291                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14292                 close.on('click', this.removeBtnClick, this, close);
14293             }
14294         }
14295         /*
14296          * fix the bug in Safari iOS8
14297          */
14298         this.inputEl().on("focus", function(e){
14299             document.activeElement.blur();
14300         }, this);
14301         
14302         return;
14303         
14304         
14305     },
14306     
14307     renderTouchView : function()
14308     {
14309         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14310         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14311         
14312         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14313         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14314         
14315         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14316         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14317         this.touchViewBodyEl.setStyle('overflow', 'auto');
14318         
14319         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14320         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14321         
14322         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14323         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14324         
14325     },
14326     
14327     showTouchView : function()
14328     {
14329         if(this.disabled){
14330             return;
14331         }
14332         
14333         this.touchViewHeaderEl.hide();
14334
14335         if(this.modalTitle.length){
14336             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14337             this.touchViewHeaderEl.show();
14338         }
14339
14340         this.touchViewEl.show();
14341
14342         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14343         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14344                 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14345
14346         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14347
14348         if(this.modalTitle.length){
14349             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14350         }
14351         
14352         this.touchViewBodyEl.setHeight(bodyHeight);
14353
14354         if(this.animate){
14355             var _this = this;
14356             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14357         }else{
14358             this.touchViewEl.addClass('in');
14359         }
14360
14361         this.doTouchViewQuery();
14362         
14363     },
14364     
14365     hideTouchView : function()
14366     {
14367         this.touchViewEl.removeClass('in');
14368
14369         if(this.animate){
14370             var _this = this;
14371             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14372         }else{
14373             this.touchViewEl.setStyle('display', 'none');
14374         }
14375         
14376     },
14377     
14378     setTouchViewValue : function()
14379     {
14380         if(this.multiple){
14381             this.clearItem();
14382         
14383             var _this = this;
14384
14385             Roo.each(this.tickItems, function(o){
14386                 this.addItem(o);
14387             }, this);
14388         }
14389         
14390         this.hideTouchView();
14391     },
14392     
14393     doTouchViewQuery : function()
14394     {
14395         var qe = {
14396             query: '',
14397             forceAll: true,
14398             combo: this,
14399             cancel:false
14400         };
14401         
14402         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14403             return false;
14404         }
14405         
14406         if(!this.alwaysQuery || this.mode == 'local'){
14407             this.onTouchViewLoad();
14408             return;
14409         }
14410         
14411         this.store.load();
14412     },
14413     
14414     onTouchViewBeforeLoad : function(combo,opts)
14415     {
14416         return;
14417     },
14418
14419     // private
14420     onTouchViewLoad : function()
14421     {
14422         if(this.store.getCount() < 1){
14423             this.onTouchViewEmptyResults();
14424             return;
14425         }
14426         
14427         this.clearTouchView();
14428         
14429         var rawValue = this.getRawValue();
14430         
14431         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14432         
14433         this.tickItems = [];
14434         
14435         this.store.data.each(function(d, rowIndex){
14436             var row = this.touchViewListGroup.createChild(template);
14437             
14438             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14439                 row.addClass(d.data.cls);
14440             }
14441             
14442             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14443                 var cfg = {
14444                     data : d.data,
14445                     html : d.data[this.displayField]
14446                 };
14447                 
14448                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14449                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14450                 }
14451             }
14452             row.removeClass('selected');
14453             if(!this.multiple && this.valueField &&
14454                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14455             {
14456                 // radio buttons..
14457                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14458                 row.addClass('selected');
14459             }
14460             
14461             if(this.multiple && this.valueField &&
14462                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14463             {
14464                 
14465                 // checkboxes...
14466                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14467                 this.tickItems.push(d.data);
14468             }
14469             
14470             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14471             
14472         }, this);
14473         
14474         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14475         
14476         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14477
14478         if(this.modalTitle.length){
14479             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14480         }
14481
14482         var listHeight = this.touchViewListGroup.getHeight();
14483         
14484         var _this = this;
14485         
14486         if(firstChecked && listHeight > bodyHeight){
14487             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14488         }
14489         
14490     },
14491     
14492     onTouchViewLoadException : function()
14493     {
14494         this.hideTouchView();
14495     },
14496     
14497     onTouchViewEmptyResults : function()
14498     {
14499         this.clearTouchView();
14500         
14501         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14502         
14503         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14504         
14505     },
14506     
14507     clearTouchView : function()
14508     {
14509         this.touchViewListGroup.dom.innerHTML = '';
14510     },
14511     
14512     onTouchViewClick : function(e, el, o)
14513     {
14514         e.preventDefault();
14515         
14516         var row = o.row;
14517         var rowIndex = o.rowIndex;
14518         
14519         var r = this.store.getAt(rowIndex);
14520         
14521         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14522             
14523             if(!this.multiple){
14524                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14525                     c.dom.removeAttribute('checked');
14526                 }, this);
14527
14528                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14529
14530                 this.setFromData(r.data);
14531
14532                 var close = this.closeTriggerEl();
14533
14534                 if(close){
14535                     close.show();
14536                 }
14537
14538                 this.hideTouchView();
14539
14540                 this.fireEvent('select', this, r, rowIndex);
14541
14542                 return;
14543             }
14544
14545             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14546                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14547                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14548                 return;
14549             }
14550
14551             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14552             this.addItem(r.data);
14553             this.tickItems.push(r.data);
14554         }
14555     },
14556     
14557     getAutoCreateNativeIOS : function()
14558     {
14559         var cfg = {
14560             cls: 'form-group' //input-group,
14561         };
14562         
14563         var combobox =  {
14564             tag: 'select',
14565             cls : 'roo-ios-select'
14566         };
14567         
14568         if (this.name) {
14569             combobox.name = this.name;
14570         }
14571         
14572         if (this.disabled) {
14573             combobox.disabled = true;
14574         }
14575         
14576         var settings = this;
14577         
14578         ['xs','sm','md','lg'].map(function(size){
14579             if (settings[size]) {
14580                 cfg.cls += ' col-' + size + '-' + settings[size];
14581             }
14582         });
14583         
14584         cfg.cn = combobox;
14585         
14586         return cfg;
14587         
14588     },
14589     
14590     initIOSView : function()
14591     {
14592         this.store.on('load', this.onIOSViewLoad, this);
14593         
14594         return;
14595     },
14596     
14597     onIOSViewLoad : function()
14598     {
14599         if(this.store.getCount() < 1){
14600             return;
14601         }
14602         
14603         this.clearIOSView();
14604         
14605         if(this.allowBlank) {
14606             
14607             var default_text = '-- SELECT --';
14608             
14609             var opt = this.inputEl().createChild({
14610                 tag: 'option',
14611                 value : 0,
14612                 html : default_text
14613             });
14614             
14615             var o = {};
14616             o[this.valueField] = 0;
14617             o[this.displayField] = default_text;
14618             
14619             this.ios_options.push({
14620                 data : o,
14621                 el : opt
14622             });
14623             
14624         }
14625         
14626         this.store.data.each(function(d, rowIndex){
14627             
14628             var html = '';
14629             
14630             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14631                 html = d.data[this.displayField];
14632             }
14633             
14634             var value = '';
14635             
14636             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
14637                 value = d.data[this.valueField];
14638             }
14639             
14640             var option = {
14641                 tag: 'option',
14642                 value : value,
14643                 html : html
14644             };
14645             
14646             if(this.value == d.data[this.valueField]){
14647                 option['selected'] = true;
14648             }
14649             
14650             var opt = this.inputEl().createChild(option);
14651             
14652             this.ios_options.push({
14653                 data : d.data,
14654                 el : opt
14655             });
14656             
14657         }, this);
14658         
14659         this.inputEl().on('change', function(){
14660            this.fireEvent('select', this);
14661         }, this);
14662         
14663     },
14664     
14665     clearIOSView: function()
14666     {
14667         this.inputEl().dom.innerHTML = '';
14668         
14669         this.ios_options = [];
14670     },
14671     
14672     setIOSValue: function(v)
14673     {
14674         this.value = v;
14675         
14676         if(!this.ios_options){
14677             return;
14678         }
14679         
14680         Roo.each(this.ios_options, function(opts){
14681            
14682            opts.el.dom.removeAttribute('selected');
14683            
14684            if(opts.data[this.valueField] != v){
14685                return;
14686            }
14687            
14688            opts.el.dom.setAttribute('selected', true);
14689            
14690         }, this);
14691     }
14692
14693     /** 
14694     * @cfg {Boolean} grow 
14695     * @hide 
14696     */
14697     /** 
14698     * @cfg {Number} growMin 
14699     * @hide 
14700     */
14701     /** 
14702     * @cfg {Number} growMax 
14703     * @hide 
14704     */
14705     /**
14706      * @hide
14707      * @method autoSize
14708      */
14709 });
14710
14711 Roo.apply(Roo.bootstrap.ComboBox,  {
14712     
14713     header : {
14714         tag: 'div',
14715         cls: 'modal-header',
14716         cn: [
14717             {
14718                 tag: 'h4',
14719                 cls: 'modal-title'
14720             }
14721         ]
14722     },
14723     
14724     body : {
14725         tag: 'div',
14726         cls: 'modal-body',
14727         cn: [
14728             {
14729                 tag: 'ul',
14730                 cls: 'list-group'
14731             }
14732         ]
14733     },
14734     
14735     listItemRadio : {
14736         tag: 'li',
14737         cls: 'list-group-item',
14738         cn: [
14739             {
14740                 tag: 'span',
14741                 cls: 'roo-combobox-list-group-item-value'
14742             },
14743             {
14744                 tag: 'div',
14745                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14746                 cn: [
14747                     {
14748                         tag: 'input',
14749                         type: 'radio'
14750                     },
14751                     {
14752                         tag: 'label'
14753                     }
14754                 ]
14755             }
14756         ]
14757     },
14758     
14759     listItemCheckbox : {
14760         tag: 'li',
14761         cls: 'list-group-item',
14762         cn: [
14763             {
14764                 tag: 'span',
14765                 cls: 'roo-combobox-list-group-item-value'
14766             },
14767             {
14768                 tag: 'div',
14769                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14770                 cn: [
14771                     {
14772                         tag: 'input',
14773                         type: 'checkbox'
14774                     },
14775                     {
14776                         tag: 'label'
14777                     }
14778                 ]
14779             }
14780         ]
14781     },
14782     
14783     emptyResult : {
14784         tag: 'div',
14785         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14786     },
14787     
14788     footer : {
14789         tag: 'div',
14790         cls: 'modal-footer',
14791         cn: [
14792             {
14793                 tag: 'div',
14794                 cls: 'row',
14795                 cn: [
14796                     {
14797                         tag: 'div',
14798                         cls: 'col-xs-6 text-left',
14799                         cn: {
14800                             tag: 'button',
14801                             cls: 'btn btn-danger roo-touch-view-cancel',
14802                             html: 'Cancel'
14803                         }
14804                     },
14805                     {
14806                         tag: 'div',
14807                         cls: 'col-xs-6 text-right',
14808                         cn: {
14809                             tag: 'button',
14810                             cls: 'btn btn-success roo-touch-view-ok',
14811                             html: 'OK'
14812                         }
14813                     }
14814                 ]
14815             }
14816         ]
14817         
14818     }
14819 });
14820
14821 Roo.apply(Roo.bootstrap.ComboBox,  {
14822     
14823     touchViewTemplate : {
14824         tag: 'div',
14825         cls: 'modal fade roo-combobox-touch-view',
14826         cn: [
14827             {
14828                 tag: 'div',
14829                 cls: 'modal-dialog',
14830                 style : 'position:fixed', // we have to fix position....
14831                 cn: [
14832                     {
14833                         tag: 'div',
14834                         cls: 'modal-content',
14835                         cn: [
14836                             Roo.bootstrap.ComboBox.header,
14837                             Roo.bootstrap.ComboBox.body,
14838                             Roo.bootstrap.ComboBox.footer
14839                         ]
14840                     }
14841                 ]
14842             }
14843         ]
14844     }
14845 });/*
14846  * Based on:
14847  * Ext JS Library 1.1.1
14848  * Copyright(c) 2006-2007, Ext JS, LLC.
14849  *
14850  * Originally Released Under LGPL - original licence link has changed is not relivant.
14851  *
14852  * Fork - LGPL
14853  * <script type="text/javascript">
14854  */
14855
14856 /**
14857  * @class Roo.View
14858  * @extends Roo.util.Observable
14859  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
14860  * This class also supports single and multi selection modes. <br>
14861  * Create a data model bound view:
14862  <pre><code>
14863  var store = new Roo.data.Store(...);
14864
14865  var view = new Roo.View({
14866     el : "my-element",
14867     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
14868  
14869     singleSelect: true,
14870     selectedClass: "ydataview-selected",
14871     store: store
14872  });
14873
14874  // listen for node click?
14875  view.on("click", function(vw, index, node, e){
14876  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14877  });
14878
14879  // load XML data
14880  dataModel.load("foobar.xml");
14881  </code></pre>
14882  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14883  * <br><br>
14884  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14885  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14886  * 
14887  * Note: old style constructor is still suported (container, template, config)
14888  * 
14889  * @constructor
14890  * Create a new View
14891  * @param {Object} config The config object
14892  * 
14893  */
14894 Roo.View = function(config, depreciated_tpl, depreciated_config){
14895     
14896     this.parent = false;
14897     
14898     if (typeof(depreciated_tpl) == 'undefined') {
14899         // new way.. - universal constructor.
14900         Roo.apply(this, config);
14901         this.el  = Roo.get(this.el);
14902     } else {
14903         // old format..
14904         this.el  = Roo.get(config);
14905         this.tpl = depreciated_tpl;
14906         Roo.apply(this, depreciated_config);
14907     }
14908     this.wrapEl  = this.el.wrap().wrap();
14909     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14910     
14911     
14912     if(typeof(this.tpl) == "string"){
14913         this.tpl = new Roo.Template(this.tpl);
14914     } else {
14915         // support xtype ctors..
14916         this.tpl = new Roo.factory(this.tpl, Roo);
14917     }
14918     
14919     
14920     this.tpl.compile();
14921     
14922     /** @private */
14923     this.addEvents({
14924         /**
14925          * @event beforeclick
14926          * Fires before a click is processed. Returns false to cancel the default action.
14927          * @param {Roo.View} this
14928          * @param {Number} index The index of the target node
14929          * @param {HTMLElement} node The target node
14930          * @param {Roo.EventObject} e The raw event object
14931          */
14932             "beforeclick" : true,
14933         /**
14934          * @event click
14935          * Fires when a template node is clicked.
14936          * @param {Roo.View} this
14937          * @param {Number} index The index of the target node
14938          * @param {HTMLElement} node The target node
14939          * @param {Roo.EventObject} e The raw event object
14940          */
14941             "click" : true,
14942         /**
14943          * @event dblclick
14944          * Fires when a template node is double clicked.
14945          * @param {Roo.View} this
14946          * @param {Number} index The index of the target node
14947          * @param {HTMLElement} node The target node
14948          * @param {Roo.EventObject} e The raw event object
14949          */
14950             "dblclick" : true,
14951         /**
14952          * @event contextmenu
14953          * Fires when a template node is right clicked.
14954          * @param {Roo.View} this
14955          * @param {Number} index The index of the target node
14956          * @param {HTMLElement} node The target node
14957          * @param {Roo.EventObject} e The raw event object
14958          */
14959             "contextmenu" : true,
14960         /**
14961          * @event selectionchange
14962          * Fires when the selected nodes change.
14963          * @param {Roo.View} this
14964          * @param {Array} selections Array of the selected nodes
14965          */
14966             "selectionchange" : true,
14967     
14968         /**
14969          * @event beforeselect
14970          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14971          * @param {Roo.View} this
14972          * @param {HTMLElement} node The node to be selected
14973          * @param {Array} selections Array of currently selected nodes
14974          */
14975             "beforeselect" : true,
14976         /**
14977          * @event preparedata
14978          * Fires on every row to render, to allow you to change the data.
14979          * @param {Roo.View} this
14980          * @param {Object} data to be rendered (change this)
14981          */
14982           "preparedata" : true
14983           
14984           
14985         });
14986
14987
14988
14989     this.el.on({
14990         "click": this.onClick,
14991         "dblclick": this.onDblClick,
14992         "contextmenu": this.onContextMenu,
14993         scope:this
14994     });
14995
14996     this.selections = [];
14997     this.nodes = [];
14998     this.cmp = new Roo.CompositeElementLite([]);
14999     if(this.store){
15000         this.store = Roo.factory(this.store, Roo.data);
15001         this.setStore(this.store, true);
15002     }
15003     
15004     if ( this.footer && this.footer.xtype) {
15005            
15006          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15007         
15008         this.footer.dataSource = this.store;
15009         this.footer.container = fctr;
15010         this.footer = Roo.factory(this.footer, Roo);
15011         fctr.insertFirst(this.el);
15012         
15013         // this is a bit insane - as the paging toolbar seems to detach the el..
15014 //        dom.parentNode.parentNode.parentNode
15015          // they get detached?
15016     }
15017     
15018     
15019     Roo.View.superclass.constructor.call(this);
15020     
15021     
15022 };
15023
15024 Roo.extend(Roo.View, Roo.util.Observable, {
15025     
15026      /**
15027      * @cfg {Roo.data.Store} store Data store to load data from.
15028      */
15029     store : false,
15030     
15031     /**
15032      * @cfg {String|Roo.Element} el The container element.
15033      */
15034     el : '',
15035     
15036     /**
15037      * @cfg {String|Roo.Template} tpl The template used by this View 
15038      */
15039     tpl : false,
15040     /**
15041      * @cfg {String} dataName the named area of the template to use as the data area
15042      *                          Works with domtemplates roo-name="name"
15043      */
15044     dataName: false,
15045     /**
15046      * @cfg {String} selectedClass The css class to add to selected nodes
15047      */
15048     selectedClass : "x-view-selected",
15049      /**
15050      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15051      */
15052     emptyText : "",
15053     
15054     /**
15055      * @cfg {String} text to display on mask (default Loading)
15056      */
15057     mask : false,
15058     /**
15059      * @cfg {Boolean} multiSelect Allow multiple selection
15060      */
15061     multiSelect : false,
15062     /**
15063      * @cfg {Boolean} singleSelect Allow single selection
15064      */
15065     singleSelect:  false,
15066     
15067     /**
15068      * @cfg {Boolean} toggleSelect - selecting 
15069      */
15070     toggleSelect : false,
15071     
15072     /**
15073      * @cfg {Boolean} tickable - selecting 
15074      */
15075     tickable : false,
15076     
15077     /**
15078      * Returns the element this view is bound to.
15079      * @return {Roo.Element}
15080      */
15081     getEl : function(){
15082         return this.wrapEl;
15083     },
15084     
15085     
15086
15087     /**
15088      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15089      */
15090     refresh : function(){
15091         //Roo.log('refresh');
15092         var t = this.tpl;
15093         
15094         // if we are using something like 'domtemplate', then
15095         // the what gets used is:
15096         // t.applySubtemplate(NAME, data, wrapping data..)
15097         // the outer template then get' applied with
15098         //     the store 'extra data'
15099         // and the body get's added to the
15100         //      roo-name="data" node?
15101         //      <span class='roo-tpl-{name}'></span> ?????
15102         
15103         
15104         
15105         this.clearSelections();
15106         this.el.update("");
15107         var html = [];
15108         var records = this.store.getRange();
15109         if(records.length < 1) {
15110             
15111             // is this valid??  = should it render a template??
15112             
15113             this.el.update(this.emptyText);
15114             return;
15115         }
15116         var el = this.el;
15117         if (this.dataName) {
15118             this.el.update(t.apply(this.store.meta)); //????
15119             el = this.el.child('.roo-tpl-' + this.dataName);
15120         }
15121         
15122         for(var i = 0, len = records.length; i < len; i++){
15123             var data = this.prepareData(records[i].data, i, records[i]);
15124             this.fireEvent("preparedata", this, data, i, records[i]);
15125             
15126             var d = Roo.apply({}, data);
15127             
15128             if(this.tickable){
15129                 Roo.apply(d, {'roo-id' : Roo.id()});
15130                 
15131                 var _this = this;
15132             
15133                 Roo.each(this.parent.item, function(item){
15134                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15135                         return;
15136                     }
15137                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15138                 });
15139             }
15140             
15141             html[html.length] = Roo.util.Format.trim(
15142                 this.dataName ?
15143                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15144                     t.apply(d)
15145             );
15146         }
15147         
15148         
15149         
15150         el.update(html.join(""));
15151         this.nodes = el.dom.childNodes;
15152         this.updateIndexes(0);
15153     },
15154     
15155
15156     /**
15157      * Function to override to reformat the data that is sent to
15158      * the template for each node.
15159      * DEPRICATED - use the preparedata event handler.
15160      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15161      * a JSON object for an UpdateManager bound view).
15162      */
15163     prepareData : function(data, index, record)
15164     {
15165         this.fireEvent("preparedata", this, data, index, record);
15166         return data;
15167     },
15168
15169     onUpdate : function(ds, record){
15170         // Roo.log('on update');   
15171         this.clearSelections();
15172         var index = this.store.indexOf(record);
15173         var n = this.nodes[index];
15174         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15175         n.parentNode.removeChild(n);
15176         this.updateIndexes(index, index);
15177     },
15178
15179     
15180     
15181 // --------- FIXME     
15182     onAdd : function(ds, records, index)
15183     {
15184         //Roo.log(['on Add', ds, records, index] );        
15185         this.clearSelections();
15186         if(this.nodes.length == 0){
15187             this.refresh();
15188             return;
15189         }
15190         var n = this.nodes[index];
15191         for(var i = 0, len = records.length; i < len; i++){
15192             var d = this.prepareData(records[i].data, i, records[i]);
15193             if(n){
15194                 this.tpl.insertBefore(n, d);
15195             }else{
15196                 
15197                 this.tpl.append(this.el, d);
15198             }
15199         }
15200         this.updateIndexes(index);
15201     },
15202
15203     onRemove : function(ds, record, index){
15204        // Roo.log('onRemove');
15205         this.clearSelections();
15206         var el = this.dataName  ?
15207             this.el.child('.roo-tpl-' + this.dataName) :
15208             this.el; 
15209         
15210         el.dom.removeChild(this.nodes[index]);
15211         this.updateIndexes(index);
15212     },
15213
15214     /**
15215      * Refresh an individual node.
15216      * @param {Number} index
15217      */
15218     refreshNode : function(index){
15219         this.onUpdate(this.store, this.store.getAt(index));
15220     },
15221
15222     updateIndexes : function(startIndex, endIndex){
15223         var ns = this.nodes;
15224         startIndex = startIndex || 0;
15225         endIndex = endIndex || ns.length - 1;
15226         for(var i = startIndex; i <= endIndex; i++){
15227             ns[i].nodeIndex = i;
15228         }
15229     },
15230
15231     /**
15232      * Changes the data store this view uses and refresh the view.
15233      * @param {Store} store
15234      */
15235     setStore : function(store, initial){
15236         if(!initial && this.store){
15237             this.store.un("datachanged", this.refresh);
15238             this.store.un("add", this.onAdd);
15239             this.store.un("remove", this.onRemove);
15240             this.store.un("update", this.onUpdate);
15241             this.store.un("clear", this.refresh);
15242             this.store.un("beforeload", this.onBeforeLoad);
15243             this.store.un("load", this.onLoad);
15244             this.store.un("loadexception", this.onLoad);
15245         }
15246         if(store){
15247           
15248             store.on("datachanged", this.refresh, this);
15249             store.on("add", this.onAdd, this);
15250             store.on("remove", this.onRemove, this);
15251             store.on("update", this.onUpdate, this);
15252             store.on("clear", this.refresh, this);
15253             store.on("beforeload", this.onBeforeLoad, this);
15254             store.on("load", this.onLoad, this);
15255             store.on("loadexception", this.onLoad, this);
15256         }
15257         
15258         if(store){
15259             this.refresh();
15260         }
15261     },
15262     /**
15263      * onbeforeLoad - masks the loading area.
15264      *
15265      */
15266     onBeforeLoad : function(store,opts)
15267     {
15268          //Roo.log('onBeforeLoad');   
15269         if (!opts.add) {
15270             this.el.update("");
15271         }
15272         this.el.mask(this.mask ? this.mask : "Loading" ); 
15273     },
15274     onLoad : function ()
15275     {
15276         this.el.unmask();
15277     },
15278     
15279
15280     /**
15281      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15282      * @param {HTMLElement} node
15283      * @return {HTMLElement} The template node
15284      */
15285     findItemFromChild : function(node){
15286         var el = this.dataName  ?
15287             this.el.child('.roo-tpl-' + this.dataName,true) :
15288             this.el.dom; 
15289         
15290         if(!node || node.parentNode == el){
15291                     return node;
15292             }
15293             var p = node.parentNode;
15294             while(p && p != el){
15295             if(p.parentNode == el){
15296                 return p;
15297             }
15298             p = p.parentNode;
15299         }
15300             return null;
15301     },
15302
15303     /** @ignore */
15304     onClick : function(e){
15305         var item = this.findItemFromChild(e.getTarget());
15306         if(item){
15307             var index = this.indexOf(item);
15308             if(this.onItemClick(item, index, e) !== false){
15309                 this.fireEvent("click", this, index, item, e);
15310             }
15311         }else{
15312             this.clearSelections();
15313         }
15314     },
15315
15316     /** @ignore */
15317     onContextMenu : function(e){
15318         var item = this.findItemFromChild(e.getTarget());
15319         if(item){
15320             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15321         }
15322     },
15323
15324     /** @ignore */
15325     onDblClick : function(e){
15326         var item = this.findItemFromChild(e.getTarget());
15327         if(item){
15328             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15329         }
15330     },
15331
15332     onItemClick : function(item, index, e)
15333     {
15334         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15335             return false;
15336         }
15337         if (this.toggleSelect) {
15338             var m = this.isSelected(item) ? 'unselect' : 'select';
15339             //Roo.log(m);
15340             var _t = this;
15341             _t[m](item, true, false);
15342             return true;
15343         }
15344         if(this.multiSelect || this.singleSelect){
15345             if(this.multiSelect && e.shiftKey && this.lastSelection){
15346                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15347             }else{
15348                 this.select(item, this.multiSelect && e.ctrlKey);
15349                 this.lastSelection = item;
15350             }
15351             
15352             if(!this.tickable){
15353                 e.preventDefault();
15354             }
15355             
15356         }
15357         return true;
15358     },
15359
15360     /**
15361      * Get the number of selected nodes.
15362      * @return {Number}
15363      */
15364     getSelectionCount : function(){
15365         return this.selections.length;
15366     },
15367
15368     /**
15369      * Get the currently selected nodes.
15370      * @return {Array} An array of HTMLElements
15371      */
15372     getSelectedNodes : function(){
15373         return this.selections;
15374     },
15375
15376     /**
15377      * Get the indexes of the selected nodes.
15378      * @return {Array}
15379      */
15380     getSelectedIndexes : function(){
15381         var indexes = [], s = this.selections;
15382         for(var i = 0, len = s.length; i < len; i++){
15383             indexes.push(s[i].nodeIndex);
15384         }
15385         return indexes;
15386     },
15387
15388     /**
15389      * Clear all selections
15390      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15391      */
15392     clearSelections : function(suppressEvent){
15393         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15394             this.cmp.elements = this.selections;
15395             this.cmp.removeClass(this.selectedClass);
15396             this.selections = [];
15397             if(!suppressEvent){
15398                 this.fireEvent("selectionchange", this, this.selections);
15399             }
15400         }
15401     },
15402
15403     /**
15404      * Returns true if the passed node is selected
15405      * @param {HTMLElement/Number} node The node or node index
15406      * @return {Boolean}
15407      */
15408     isSelected : function(node){
15409         var s = this.selections;
15410         if(s.length < 1){
15411             return false;
15412         }
15413         node = this.getNode(node);
15414         return s.indexOf(node) !== -1;
15415     },
15416
15417     /**
15418      * Selects nodes.
15419      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
15420      * @param {Boolean} keepExisting (optional) true to keep existing selections
15421      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15422      */
15423     select : function(nodeInfo, keepExisting, suppressEvent){
15424         if(nodeInfo instanceof Array){
15425             if(!keepExisting){
15426                 this.clearSelections(true);
15427             }
15428             for(var i = 0, len = nodeInfo.length; i < len; i++){
15429                 this.select(nodeInfo[i], true, true);
15430             }
15431             return;
15432         } 
15433         var node = this.getNode(nodeInfo);
15434         if(!node || this.isSelected(node)){
15435             return; // already selected.
15436         }
15437         if(!keepExisting){
15438             this.clearSelections(true);
15439         }
15440         
15441         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15442             Roo.fly(node).addClass(this.selectedClass);
15443             this.selections.push(node);
15444             if(!suppressEvent){
15445                 this.fireEvent("selectionchange", this, this.selections);
15446             }
15447         }
15448         
15449         
15450     },
15451       /**
15452      * Unselects nodes.
15453      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
15454      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15455      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15456      */
15457     unselect : function(nodeInfo, keepExisting, suppressEvent)
15458     {
15459         if(nodeInfo instanceof Array){
15460             Roo.each(this.selections, function(s) {
15461                 this.unselect(s, nodeInfo);
15462             }, this);
15463             return;
15464         }
15465         var node = this.getNode(nodeInfo);
15466         if(!node || !this.isSelected(node)){
15467             //Roo.log("not selected");
15468             return; // not selected.
15469         }
15470         // fireevent???
15471         var ns = [];
15472         Roo.each(this.selections, function(s) {
15473             if (s == node ) {
15474                 Roo.fly(node).removeClass(this.selectedClass);
15475
15476                 return;
15477             }
15478             ns.push(s);
15479         },this);
15480         
15481         this.selections= ns;
15482         this.fireEvent("selectionchange", this, this.selections);
15483     },
15484
15485     /**
15486      * Gets a template node.
15487      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15488      * @return {HTMLElement} The node or null if it wasn't found
15489      */
15490     getNode : function(nodeInfo){
15491         if(typeof nodeInfo == "string"){
15492             return document.getElementById(nodeInfo);
15493         }else if(typeof nodeInfo == "number"){
15494             return this.nodes[nodeInfo];
15495         }
15496         return nodeInfo;
15497     },
15498
15499     /**
15500      * Gets a range template nodes.
15501      * @param {Number} startIndex
15502      * @param {Number} endIndex
15503      * @return {Array} An array of nodes
15504      */
15505     getNodes : function(start, end){
15506         var ns = this.nodes;
15507         start = start || 0;
15508         end = typeof end == "undefined" ? ns.length - 1 : end;
15509         var nodes = [];
15510         if(start <= end){
15511             for(var i = start; i <= end; i++){
15512                 nodes.push(ns[i]);
15513             }
15514         } else{
15515             for(var i = start; i >= end; i--){
15516                 nodes.push(ns[i]);
15517             }
15518         }
15519         return nodes;
15520     },
15521
15522     /**
15523      * Finds the index of the passed node
15524      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15525      * @return {Number} The index of the node or -1
15526      */
15527     indexOf : function(node){
15528         node = this.getNode(node);
15529         if(typeof node.nodeIndex == "number"){
15530             return node.nodeIndex;
15531         }
15532         var ns = this.nodes;
15533         for(var i = 0, len = ns.length; i < len; i++){
15534             if(ns[i] == node){
15535                 return i;
15536             }
15537         }
15538         return -1;
15539     }
15540 });
15541 /*
15542  * - LGPL
15543  *
15544  * based on jquery fullcalendar
15545  * 
15546  */
15547
15548 Roo.bootstrap = Roo.bootstrap || {};
15549 /**
15550  * @class Roo.bootstrap.Calendar
15551  * @extends Roo.bootstrap.Component
15552  * Bootstrap Calendar class
15553  * @cfg {Boolean} loadMask (true|false) default false
15554  * @cfg {Object} header generate the user specific header of the calendar, default false
15555
15556  * @constructor
15557  * Create a new Container
15558  * @param {Object} config The config object
15559  */
15560
15561
15562
15563 Roo.bootstrap.Calendar = function(config){
15564     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15565      this.addEvents({
15566         /**
15567              * @event select
15568              * Fires when a date is selected
15569              * @param {DatePicker} this
15570              * @param {Date} date The selected date
15571              */
15572         'select': true,
15573         /**
15574              * @event monthchange
15575              * Fires when the displayed month changes 
15576              * @param {DatePicker} this
15577              * @param {Date} date The selected month
15578              */
15579         'monthchange': true,
15580         /**
15581              * @event evententer
15582              * Fires when mouse over an event
15583              * @param {Calendar} this
15584              * @param {event} Event
15585              */
15586         'evententer': true,
15587         /**
15588              * @event eventleave
15589              * Fires when the mouse leaves an
15590              * @param {Calendar} this
15591              * @param {event}
15592              */
15593         'eventleave': true,
15594         /**
15595              * @event eventclick
15596              * Fires when the mouse click an
15597              * @param {Calendar} this
15598              * @param {event}
15599              */
15600         'eventclick': true
15601         
15602     });
15603
15604 };
15605
15606 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
15607     
15608      /**
15609      * @cfg {Number} startDay
15610      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15611      */
15612     startDay : 0,
15613     
15614     loadMask : false,
15615     
15616     header : false,
15617       
15618     getAutoCreate : function(){
15619         
15620         
15621         var fc_button = function(name, corner, style, content ) {
15622             return Roo.apply({},{
15623                 tag : 'span',
15624                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
15625                          (corner.length ?
15626                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15627                             ''
15628                         ),
15629                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15630                 unselectable: 'on'
15631             });
15632         };
15633         
15634         var header = {};
15635         
15636         if(!this.header){
15637             header = {
15638                 tag : 'table',
15639                 cls : 'fc-header',
15640                 style : 'width:100%',
15641                 cn : [
15642                     {
15643                         tag: 'tr',
15644                         cn : [
15645                             {
15646                                 tag : 'td',
15647                                 cls : 'fc-header-left',
15648                                 cn : [
15649                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
15650                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
15651                                     { tag: 'span', cls: 'fc-header-space' },
15652                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
15653
15654
15655                                 ]
15656                             },
15657
15658                             {
15659                                 tag : 'td',
15660                                 cls : 'fc-header-center',
15661                                 cn : [
15662                                     {
15663                                         tag: 'span',
15664                                         cls: 'fc-header-title',
15665                                         cn : {
15666                                             tag: 'H2',
15667                                             html : 'month / year'
15668                                         }
15669                                     }
15670
15671                                 ]
15672                             },
15673                             {
15674                                 tag : 'td',
15675                                 cls : 'fc-header-right',
15676                                 cn : [
15677                               /*      fc_button('month', 'left', '', 'month' ),
15678                                     fc_button('week', '', '', 'week' ),
15679                                     fc_button('day', 'right', '', 'day' )
15680                                 */    
15681
15682                                 ]
15683                             }
15684
15685                         ]
15686                     }
15687                 ]
15688             };
15689         }
15690         
15691         header = this.header;
15692         
15693        
15694         var cal_heads = function() {
15695             var ret = [];
15696             // fixme - handle this.
15697             
15698             for (var i =0; i < Date.dayNames.length; i++) {
15699                 var d = Date.dayNames[i];
15700                 ret.push({
15701                     tag: 'th',
15702                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15703                     html : d.substring(0,3)
15704                 });
15705                 
15706             }
15707             ret[0].cls += ' fc-first';
15708             ret[6].cls += ' fc-last';
15709             return ret;
15710         };
15711         var cal_cell = function(n) {
15712             return  {
15713                 tag: 'td',
15714                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15715                 cn : [
15716                     {
15717                         cn : [
15718                             {
15719                                 cls: 'fc-day-number',
15720                                 html: 'D'
15721                             },
15722                             {
15723                                 cls: 'fc-day-content',
15724                              
15725                                 cn : [
15726                                      {
15727                                         style: 'position: relative;' // height: 17px;
15728                                     }
15729                                 ]
15730                             }
15731                             
15732                             
15733                         ]
15734                     }
15735                 ]
15736                 
15737             }
15738         };
15739         var cal_rows = function() {
15740             
15741             var ret = [];
15742             for (var r = 0; r < 6; r++) {
15743                 var row= {
15744                     tag : 'tr',
15745                     cls : 'fc-week',
15746                     cn : []
15747                 };
15748                 
15749                 for (var i =0; i < Date.dayNames.length; i++) {
15750                     var d = Date.dayNames[i];
15751                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15752
15753                 }
15754                 row.cn[0].cls+=' fc-first';
15755                 row.cn[0].cn[0].style = 'min-height:90px';
15756                 row.cn[6].cls+=' fc-last';
15757                 ret.push(row);
15758                 
15759             }
15760             ret[0].cls += ' fc-first';
15761             ret[4].cls += ' fc-prev-last';
15762             ret[5].cls += ' fc-last';
15763             return ret;
15764             
15765         };
15766         
15767         var cal_table = {
15768             tag: 'table',
15769             cls: 'fc-border-separate',
15770             style : 'width:100%',
15771             cellspacing  : 0,
15772             cn : [
15773                 { 
15774                     tag: 'thead',
15775                     cn : [
15776                         { 
15777                             tag: 'tr',
15778                             cls : 'fc-first fc-last',
15779                             cn : cal_heads()
15780                         }
15781                     ]
15782                 },
15783                 { 
15784                     tag: 'tbody',
15785                     cn : cal_rows()
15786                 }
15787                   
15788             ]
15789         };
15790          
15791          var cfg = {
15792             cls : 'fc fc-ltr',
15793             cn : [
15794                 header,
15795                 {
15796                     cls : 'fc-content',
15797                     style : "position: relative;",
15798                     cn : [
15799                         {
15800                             cls : 'fc-view fc-view-month fc-grid',
15801                             style : 'position: relative',
15802                             unselectable : 'on',
15803                             cn : [
15804                                 {
15805                                     cls : 'fc-event-container',
15806                                     style : 'position:absolute;z-index:8;top:0;left:0;'
15807                                 },
15808                                 cal_table
15809                             ]
15810                         }
15811                     ]
15812     
15813                 }
15814            ] 
15815             
15816         };
15817         
15818          
15819         
15820         return cfg;
15821     },
15822     
15823     
15824     initEvents : function()
15825     {
15826         if(!this.store){
15827             throw "can not find store for calendar";
15828         }
15829         
15830         var mark = {
15831             tag: "div",
15832             cls:"x-dlg-mask",
15833             style: "text-align:center",
15834             cn: [
15835                 {
15836                     tag: "div",
15837                     style: "background-color:white;width:50%;margin:250 auto",
15838                     cn: [
15839                         {
15840                             tag: "img",
15841                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
15842                         },
15843                         {
15844                             tag: "span",
15845                             html: "Loading"
15846                         }
15847                         
15848                     ]
15849                 }
15850             ]
15851         };
15852         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15853         
15854         var size = this.el.select('.fc-content', true).first().getSize();
15855         this.maskEl.setSize(size.width, size.height);
15856         this.maskEl.enableDisplayMode("block");
15857         if(!this.loadMask){
15858             this.maskEl.hide();
15859         }
15860         
15861         this.store = Roo.factory(this.store, Roo.data);
15862         this.store.on('load', this.onLoad, this);
15863         this.store.on('beforeload', this.onBeforeLoad, this);
15864         
15865         this.resize();
15866         
15867         this.cells = this.el.select('.fc-day',true);
15868         //Roo.log(this.cells);
15869         this.textNodes = this.el.query('.fc-day-number');
15870         this.cells.addClassOnOver('fc-state-hover');
15871         
15872         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15873         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15874         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15875         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15876         
15877         this.on('monthchange', this.onMonthChange, this);
15878         
15879         this.update(new Date().clearTime());
15880     },
15881     
15882     resize : function() {
15883         var sz  = this.el.getSize();
15884         
15885         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15886         this.el.select('.fc-day-content div',true).setHeight(34);
15887     },
15888     
15889     
15890     // private
15891     showPrevMonth : function(e){
15892         this.update(this.activeDate.add("mo", -1));
15893     },
15894     showToday : function(e){
15895         this.update(new Date().clearTime());
15896     },
15897     // private
15898     showNextMonth : function(e){
15899         this.update(this.activeDate.add("mo", 1));
15900     },
15901
15902     // private
15903     showPrevYear : function(){
15904         this.update(this.activeDate.add("y", -1));
15905     },
15906
15907     // private
15908     showNextYear : function(){
15909         this.update(this.activeDate.add("y", 1));
15910     },
15911
15912     
15913    // private
15914     update : function(date)
15915     {
15916         var vd = this.activeDate;
15917         this.activeDate = date;
15918 //        if(vd && this.el){
15919 //            var t = date.getTime();
15920 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15921 //                Roo.log('using add remove');
15922 //                
15923 //                this.fireEvent('monthchange', this, date);
15924 //                
15925 //                this.cells.removeClass("fc-state-highlight");
15926 //                this.cells.each(function(c){
15927 //                   if(c.dateValue == t){
15928 //                       c.addClass("fc-state-highlight");
15929 //                       setTimeout(function(){
15930 //                            try{c.dom.firstChild.focus();}catch(e){}
15931 //                       }, 50);
15932 //                       return false;
15933 //                   }
15934 //                   return true;
15935 //                });
15936 //                return;
15937 //            }
15938 //        }
15939         
15940         var days = date.getDaysInMonth();
15941         
15942         var firstOfMonth = date.getFirstDateOfMonth();
15943         var startingPos = firstOfMonth.getDay()-this.startDay;
15944         
15945         if(startingPos < this.startDay){
15946             startingPos += 7;
15947         }
15948         
15949         var pm = date.add(Date.MONTH, -1);
15950         var prevStart = pm.getDaysInMonth()-startingPos;
15951 //        
15952         this.cells = this.el.select('.fc-day',true);
15953         this.textNodes = this.el.query('.fc-day-number');
15954         this.cells.addClassOnOver('fc-state-hover');
15955         
15956         var cells = this.cells.elements;
15957         var textEls = this.textNodes;
15958         
15959         Roo.each(cells, function(cell){
15960             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15961         });
15962         
15963         days += startingPos;
15964
15965         // convert everything to numbers so it's fast
15966         var day = 86400000;
15967         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15968         //Roo.log(d);
15969         //Roo.log(pm);
15970         //Roo.log(prevStart);
15971         
15972         var today = new Date().clearTime().getTime();
15973         var sel = date.clearTime().getTime();
15974         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15975         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15976         var ddMatch = this.disabledDatesRE;
15977         var ddText = this.disabledDatesText;
15978         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15979         var ddaysText = this.disabledDaysText;
15980         var format = this.format;
15981         
15982         var setCellClass = function(cal, cell){
15983             cell.row = 0;
15984             cell.events = [];
15985             cell.more = [];
15986             //Roo.log('set Cell Class');
15987             cell.title = "";
15988             var t = d.getTime();
15989             
15990             //Roo.log(d);
15991             
15992             cell.dateValue = t;
15993             if(t == today){
15994                 cell.className += " fc-today";
15995                 cell.className += " fc-state-highlight";
15996                 cell.title = cal.todayText;
15997             }
15998             if(t == sel){
15999                 // disable highlight in other month..
16000                 //cell.className += " fc-state-highlight";
16001                 
16002             }
16003             // disabling
16004             if(t < min) {
16005                 cell.className = " fc-state-disabled";
16006                 cell.title = cal.minText;
16007                 return;
16008             }
16009             if(t > max) {
16010                 cell.className = " fc-state-disabled";
16011                 cell.title = cal.maxText;
16012                 return;
16013             }
16014             if(ddays){
16015                 if(ddays.indexOf(d.getDay()) != -1){
16016                     cell.title = ddaysText;
16017                     cell.className = " fc-state-disabled";
16018                 }
16019             }
16020             if(ddMatch && format){
16021                 var fvalue = d.dateFormat(format);
16022                 if(ddMatch.test(fvalue)){
16023                     cell.title = ddText.replace("%0", fvalue);
16024                     cell.className = " fc-state-disabled";
16025                 }
16026             }
16027             
16028             if (!cell.initialClassName) {
16029                 cell.initialClassName = cell.dom.className;
16030             }
16031             
16032             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16033         };
16034
16035         var i = 0;
16036         
16037         for(; i < startingPos; i++) {
16038             textEls[i].innerHTML = (++prevStart);
16039             d.setDate(d.getDate()+1);
16040             
16041             cells[i].className = "fc-past fc-other-month";
16042             setCellClass(this, cells[i]);
16043         }
16044         
16045         var intDay = 0;
16046         
16047         for(; i < days; i++){
16048             intDay = i - startingPos + 1;
16049             textEls[i].innerHTML = (intDay);
16050             d.setDate(d.getDate()+1);
16051             
16052             cells[i].className = ''; // "x-date-active";
16053             setCellClass(this, cells[i]);
16054         }
16055         var extraDays = 0;
16056         
16057         for(; i < 42; i++) {
16058             textEls[i].innerHTML = (++extraDays);
16059             d.setDate(d.getDate()+1);
16060             
16061             cells[i].className = "fc-future fc-other-month";
16062             setCellClass(this, cells[i]);
16063         }
16064         
16065         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16066         
16067         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16068         
16069         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16070         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16071         
16072         if(totalRows != 6){
16073             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16074             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16075         }
16076         
16077         this.fireEvent('monthchange', this, date);
16078         
16079         
16080         /*
16081         if(!this.internalRender){
16082             var main = this.el.dom.firstChild;
16083             var w = main.offsetWidth;
16084             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16085             Roo.fly(main).setWidth(w);
16086             this.internalRender = true;
16087             // opera does not respect the auto grow header center column
16088             // then, after it gets a width opera refuses to recalculate
16089             // without a second pass
16090             if(Roo.isOpera && !this.secondPass){
16091                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16092                 this.secondPass = true;
16093                 this.update.defer(10, this, [date]);
16094             }
16095         }
16096         */
16097         
16098     },
16099     
16100     findCell : function(dt) {
16101         dt = dt.clearTime().getTime();
16102         var ret = false;
16103         this.cells.each(function(c){
16104             //Roo.log("check " +c.dateValue + '?=' + dt);
16105             if(c.dateValue == dt){
16106                 ret = c;
16107                 return false;
16108             }
16109             return true;
16110         });
16111         
16112         return ret;
16113     },
16114     
16115     findCells : function(ev) {
16116         var s = ev.start.clone().clearTime().getTime();
16117        // Roo.log(s);
16118         var e= ev.end.clone().clearTime().getTime();
16119        // Roo.log(e);
16120         var ret = [];
16121         this.cells.each(function(c){
16122              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16123             
16124             if(c.dateValue > e){
16125                 return ;
16126             }
16127             if(c.dateValue < s){
16128                 return ;
16129             }
16130             ret.push(c);
16131         });
16132         
16133         return ret;    
16134     },
16135     
16136 //    findBestRow: function(cells)
16137 //    {
16138 //        var ret = 0;
16139 //        
16140 //        for (var i =0 ; i < cells.length;i++) {
16141 //            ret  = Math.max(cells[i].rows || 0,ret);
16142 //        }
16143 //        return ret;
16144 //        
16145 //    },
16146     
16147     
16148     addItem : function(ev)
16149     {
16150         // look for vertical location slot in
16151         var cells = this.findCells(ev);
16152         
16153 //        ev.row = this.findBestRow(cells);
16154         
16155         // work out the location.
16156         
16157         var crow = false;
16158         var rows = [];
16159         for(var i =0; i < cells.length; i++) {
16160             
16161             cells[i].row = cells[0].row;
16162             
16163             if(i == 0){
16164                 cells[i].row = cells[i].row + 1;
16165             }
16166             
16167             if (!crow) {
16168                 crow = {
16169                     start : cells[i],
16170                     end :  cells[i]
16171                 };
16172                 continue;
16173             }
16174             if (crow.start.getY() == cells[i].getY()) {
16175                 // on same row.
16176                 crow.end = cells[i];
16177                 continue;
16178             }
16179             // different row.
16180             rows.push(crow);
16181             crow = {
16182                 start: cells[i],
16183                 end : cells[i]
16184             };
16185             
16186         }
16187         
16188         rows.push(crow);
16189         ev.els = [];
16190         ev.rows = rows;
16191         ev.cells = cells;
16192         
16193         cells[0].events.push(ev);
16194         
16195         this.calevents.push(ev);
16196     },
16197     
16198     clearEvents: function() {
16199         
16200         if(!this.calevents){
16201             return;
16202         }
16203         
16204         Roo.each(this.cells.elements, function(c){
16205             c.row = 0;
16206             c.events = [];
16207             c.more = [];
16208         });
16209         
16210         Roo.each(this.calevents, function(e) {
16211             Roo.each(e.els, function(el) {
16212                 el.un('mouseenter' ,this.onEventEnter, this);
16213                 el.un('mouseleave' ,this.onEventLeave, this);
16214                 el.remove();
16215             },this);
16216         },this);
16217         
16218         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16219             e.remove();
16220         });
16221         
16222     },
16223     
16224     renderEvents: function()
16225     {   
16226         var _this = this;
16227         
16228         this.cells.each(function(c) {
16229             
16230             if(c.row < 5){
16231                 return;
16232             }
16233             
16234             var ev = c.events;
16235             
16236             var r = 4;
16237             if(c.row != c.events.length){
16238                 r = 4 - (4 - (c.row - c.events.length));
16239             }
16240             
16241             c.events = ev.slice(0, r);
16242             c.more = ev.slice(r);
16243             
16244             if(c.more.length && c.more.length == 1){
16245                 c.events.push(c.more.pop());
16246             }
16247             
16248             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16249             
16250         });
16251             
16252         this.cells.each(function(c) {
16253             
16254             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16255             
16256             
16257             for (var e = 0; e < c.events.length; e++){
16258                 var ev = c.events[e];
16259                 var rows = ev.rows;
16260                 
16261                 for(var i = 0; i < rows.length; i++) {
16262                 
16263                     // how many rows should it span..
16264
16265                     var  cfg = {
16266                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16267                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16268
16269                         unselectable : "on",
16270                         cn : [
16271                             {
16272                                 cls: 'fc-event-inner',
16273                                 cn : [
16274     //                                {
16275     //                                  tag:'span',
16276     //                                  cls: 'fc-event-time',
16277     //                                  html : cells.length > 1 ? '' : ev.time
16278     //                                },
16279                                     {
16280                                       tag:'span',
16281                                       cls: 'fc-event-title',
16282                                       html : String.format('{0}', ev.title)
16283                                     }
16284
16285
16286                                 ]
16287                             },
16288                             {
16289                                 cls: 'ui-resizable-handle ui-resizable-e',
16290                                 html : '&nbsp;&nbsp;&nbsp'
16291                             }
16292
16293                         ]
16294                     };
16295
16296                     if (i == 0) {
16297                         cfg.cls += ' fc-event-start';
16298                     }
16299                     if ((i+1) == rows.length) {
16300                         cfg.cls += ' fc-event-end';
16301                     }
16302
16303                     var ctr = _this.el.select('.fc-event-container',true).first();
16304                     var cg = ctr.createChild(cfg);
16305
16306                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16307                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16308
16309                     var r = (c.more.length) ? 1 : 0;
16310                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16311                     cg.setWidth(ebox.right - sbox.x -2);
16312
16313                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16314                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16315                     cg.on('click', _this.onEventClick, _this, ev);
16316
16317                     ev.els.push(cg);
16318                     
16319                 }
16320                 
16321             }
16322             
16323             
16324             if(c.more.length){
16325                 var  cfg = {
16326                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16327                     style : 'position: absolute',
16328                     unselectable : "on",
16329                     cn : [
16330                         {
16331                             cls: 'fc-event-inner',
16332                             cn : [
16333                                 {
16334                                   tag:'span',
16335                                   cls: 'fc-event-title',
16336                                   html : 'More'
16337                                 }
16338
16339
16340                             ]
16341                         },
16342                         {
16343                             cls: 'ui-resizable-handle ui-resizable-e',
16344                             html : '&nbsp;&nbsp;&nbsp'
16345                         }
16346
16347                     ]
16348                 };
16349
16350                 var ctr = _this.el.select('.fc-event-container',true).first();
16351                 var cg = ctr.createChild(cfg);
16352
16353                 var sbox = c.select('.fc-day-content',true).first().getBox();
16354                 var ebox = c.select('.fc-day-content',true).first().getBox();
16355                 //Roo.log(cg);
16356                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
16357                 cg.setWidth(ebox.right - sbox.x -2);
16358
16359                 cg.on('click', _this.onMoreEventClick, _this, c.more);
16360                 
16361             }
16362             
16363         });
16364         
16365         
16366         
16367     },
16368     
16369     onEventEnter: function (e, el,event,d) {
16370         this.fireEvent('evententer', this, el, event);
16371     },
16372     
16373     onEventLeave: function (e, el,event,d) {
16374         this.fireEvent('eventleave', this, el, event);
16375     },
16376     
16377     onEventClick: function (e, el,event,d) {
16378         this.fireEvent('eventclick', this, el, event);
16379     },
16380     
16381     onMonthChange: function () {
16382         this.store.load();
16383     },
16384     
16385     onMoreEventClick: function(e, el, more)
16386     {
16387         var _this = this;
16388         
16389         this.calpopover.placement = 'right';
16390         this.calpopover.setTitle('More');
16391         
16392         this.calpopover.setContent('');
16393         
16394         var ctr = this.calpopover.el.select('.popover-content', true).first();
16395         
16396         Roo.each(more, function(m){
16397             var cfg = {
16398                 cls : 'fc-event-hori fc-event-draggable',
16399                 html : m.title
16400             };
16401             var cg = ctr.createChild(cfg);
16402             
16403             cg.on('click', _this.onEventClick, _this, m);
16404         });
16405         
16406         this.calpopover.show(el);
16407         
16408         
16409     },
16410     
16411     onLoad: function () 
16412     {   
16413         this.calevents = [];
16414         var cal = this;
16415         
16416         if(this.store.getCount() > 0){
16417             this.store.data.each(function(d){
16418                cal.addItem({
16419                     id : d.data.id,
16420                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16421                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16422                     time : d.data.start_time,
16423                     title : d.data.title,
16424                     description : d.data.description,
16425                     venue : d.data.venue
16426                 });
16427             });
16428         }
16429         
16430         this.renderEvents();
16431         
16432         if(this.calevents.length && this.loadMask){
16433             this.maskEl.hide();
16434         }
16435     },
16436     
16437     onBeforeLoad: function()
16438     {
16439         this.clearEvents();
16440         if(this.loadMask){
16441             this.maskEl.show();
16442         }
16443     }
16444 });
16445
16446  
16447  /*
16448  * - LGPL
16449  *
16450  * element
16451  * 
16452  */
16453
16454 /**
16455  * @class Roo.bootstrap.Popover
16456  * @extends Roo.bootstrap.Component
16457  * Bootstrap Popover class
16458  * @cfg {String} html contents of the popover   (or false to use children..)
16459  * @cfg {String} title of popover (or false to hide)
16460  * @cfg {String} placement how it is placed
16461  * @cfg {String} trigger click || hover (or false to trigger manually)
16462  * @cfg {String} over what (parent or false to trigger manually.)
16463  * @cfg {Number} delay - delay before showing
16464  
16465  * @constructor
16466  * Create a new Popover
16467  * @param {Object} config The config object
16468  */
16469
16470 Roo.bootstrap.Popover = function(config){
16471     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16472     
16473     this.addEvents({
16474         // raw events
16475          /**
16476          * @event show
16477          * After the popover show
16478          * 
16479          * @param {Roo.bootstrap.Popover} this
16480          */
16481         "show" : true,
16482         /**
16483          * @event hide
16484          * After the popover hide
16485          * 
16486          * @param {Roo.bootstrap.Popover} this
16487          */
16488         "hide" : true
16489     });
16490 };
16491
16492 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
16493     
16494     title: 'Fill in a title',
16495     html: false,
16496     
16497     placement : 'right',
16498     trigger : 'hover', // hover
16499     
16500     delay : 0,
16501     
16502     over: 'parent',
16503     
16504     can_build_overlaid : false,
16505     
16506     getChildContainer : function()
16507     {
16508         return this.el.select('.popover-content',true).first();
16509     },
16510     
16511     getAutoCreate : function(){
16512          
16513         var cfg = {
16514            cls : 'popover roo-dynamic',
16515            style: 'display:block',
16516            cn : [
16517                 {
16518                     cls : 'arrow'
16519                 },
16520                 {
16521                     cls : 'popover-inner',
16522                     cn : [
16523                         {
16524                             tag: 'h3',
16525                             cls: 'popover-title',
16526                             html : this.title
16527                         },
16528                         {
16529                             cls : 'popover-content',
16530                             html : this.html
16531                         }
16532                     ]
16533                     
16534                 }
16535            ]
16536         };
16537         
16538         return cfg;
16539     },
16540     setTitle: function(str)
16541     {
16542         this.title = str;
16543         this.el.select('.popover-title',true).first().dom.innerHTML = str;
16544     },
16545     setContent: function(str)
16546     {
16547         this.html = str;
16548         this.el.select('.popover-content',true).first().dom.innerHTML = str;
16549     },
16550     // as it get's added to the bottom of the page.
16551     onRender : function(ct, position)
16552     {
16553         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16554         if(!this.el){
16555             var cfg = Roo.apply({},  this.getAutoCreate());
16556             cfg.id = Roo.id();
16557             
16558             if (this.cls) {
16559                 cfg.cls += ' ' + this.cls;
16560             }
16561             if (this.style) {
16562                 cfg.style = this.style;
16563             }
16564             //Roo.log("adding to ");
16565             this.el = Roo.get(document.body).createChild(cfg, position);
16566 //            Roo.log(this.el);
16567         }
16568         this.initEvents();
16569     },
16570     
16571     initEvents : function()
16572     {
16573         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16574         this.el.enableDisplayMode('block');
16575         this.el.hide();
16576         if (this.over === false) {
16577             return; 
16578         }
16579         if (this.triggers === false) {
16580             return;
16581         }
16582         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16583         var triggers = this.trigger ? this.trigger.split(' ') : [];
16584         Roo.each(triggers, function(trigger) {
16585         
16586             if (trigger == 'click') {
16587                 on_el.on('click', this.toggle, this);
16588             } else if (trigger != 'manual') {
16589                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
16590                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16591       
16592                 on_el.on(eventIn  ,this.enter, this);
16593                 on_el.on(eventOut, this.leave, this);
16594             }
16595         }, this);
16596         
16597     },
16598     
16599     
16600     // private
16601     timeout : null,
16602     hoverState : null,
16603     
16604     toggle : function () {
16605         this.hoverState == 'in' ? this.leave() : this.enter();
16606     },
16607     
16608     enter : function () {
16609         
16610         clearTimeout(this.timeout);
16611     
16612         this.hoverState = 'in';
16613     
16614         if (!this.delay || !this.delay.show) {
16615             this.show();
16616             return;
16617         }
16618         var _t = this;
16619         this.timeout = setTimeout(function () {
16620             if (_t.hoverState == 'in') {
16621                 _t.show();
16622             }
16623         }, this.delay.show)
16624     },
16625     
16626     leave : function() {
16627         clearTimeout(this.timeout);
16628     
16629         this.hoverState = 'out';
16630     
16631         if (!this.delay || !this.delay.hide) {
16632             this.hide();
16633             return;
16634         }
16635         var _t = this;
16636         this.timeout = setTimeout(function () {
16637             if (_t.hoverState == 'out') {
16638                 _t.hide();
16639             }
16640         }, this.delay.hide)
16641     },
16642     
16643     show : function (on_el)
16644     {
16645         if (!on_el) {
16646             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16647         }
16648         
16649         // set content.
16650         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16651         if (this.html !== false) {
16652             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16653         }
16654         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16655         if (!this.title.length) {
16656             this.el.select('.popover-title',true).hide();
16657         }
16658         
16659         var placement = typeof this.placement == 'function' ?
16660             this.placement.call(this, this.el, on_el) :
16661             this.placement;
16662             
16663         var autoToken = /\s?auto?\s?/i;
16664         var autoPlace = autoToken.test(placement);
16665         if (autoPlace) {
16666             placement = placement.replace(autoToken, '') || 'top';
16667         }
16668         
16669         //this.el.detach()
16670         //this.el.setXY([0,0]);
16671         this.el.show();
16672         this.el.dom.style.display='block';
16673         this.el.addClass(placement);
16674         
16675         //this.el.appendTo(on_el);
16676         
16677         var p = this.getPosition();
16678         var box = this.el.getBox();
16679         
16680         if (autoPlace) {
16681             // fixme..
16682         }
16683         var align = Roo.bootstrap.Popover.alignment[placement];
16684         this.el.alignTo(on_el, align[0],align[1]);
16685         //var arrow = this.el.select('.arrow',true).first();
16686         //arrow.set(align[2], 
16687         
16688         this.el.addClass('in');
16689         
16690         
16691         if (this.el.hasClass('fade')) {
16692             // fade it?
16693         }
16694         
16695         this.hoverState = 'in';
16696         
16697         this.fireEvent('show', this);
16698         
16699     },
16700     hide : function()
16701     {
16702         this.el.setXY([0,0]);
16703         this.el.removeClass('in');
16704         this.el.hide();
16705         this.hoverState = null;
16706         
16707         this.fireEvent('hide', this);
16708     }
16709     
16710 });
16711
16712 Roo.bootstrap.Popover.alignment = {
16713     'left' : ['r-l', [-10,0], 'right'],
16714     'right' : ['l-r', [10,0], 'left'],
16715     'bottom' : ['t-b', [0,10], 'top'],
16716     'top' : [ 'b-t', [0,-10], 'bottom']
16717 };
16718
16719  /*
16720  * - LGPL
16721  *
16722  * Progress
16723  * 
16724  */
16725
16726 /**
16727  * @class Roo.bootstrap.Progress
16728  * @extends Roo.bootstrap.Component
16729  * Bootstrap Progress class
16730  * @cfg {Boolean} striped striped of the progress bar
16731  * @cfg {Boolean} active animated of the progress bar
16732  * 
16733  * 
16734  * @constructor
16735  * Create a new Progress
16736  * @param {Object} config The config object
16737  */
16738
16739 Roo.bootstrap.Progress = function(config){
16740     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16741 };
16742
16743 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
16744     
16745     striped : false,
16746     active: false,
16747     
16748     getAutoCreate : function(){
16749         var cfg = {
16750             tag: 'div',
16751             cls: 'progress'
16752         };
16753         
16754         
16755         if(this.striped){
16756             cfg.cls += ' progress-striped';
16757         }
16758       
16759         if(this.active){
16760             cfg.cls += ' active';
16761         }
16762         
16763         
16764         return cfg;
16765     }
16766    
16767 });
16768
16769  
16770
16771  /*
16772  * - LGPL
16773  *
16774  * ProgressBar
16775  * 
16776  */
16777
16778 /**
16779  * @class Roo.bootstrap.ProgressBar
16780  * @extends Roo.bootstrap.Component
16781  * Bootstrap ProgressBar class
16782  * @cfg {Number} aria_valuenow aria-value now
16783  * @cfg {Number} aria_valuemin aria-value min
16784  * @cfg {Number} aria_valuemax aria-value max
16785  * @cfg {String} label label for the progress bar
16786  * @cfg {String} panel (success | info | warning | danger )
16787  * @cfg {String} role role of the progress bar
16788  * @cfg {String} sr_only text
16789  * 
16790  * 
16791  * @constructor
16792  * Create a new ProgressBar
16793  * @param {Object} config The config object
16794  */
16795
16796 Roo.bootstrap.ProgressBar = function(config){
16797     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16798 };
16799
16800 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
16801     
16802     aria_valuenow : 0,
16803     aria_valuemin : 0,
16804     aria_valuemax : 100,
16805     label : false,
16806     panel : false,
16807     role : false,
16808     sr_only: false,
16809     
16810     getAutoCreate : function()
16811     {
16812         
16813         var cfg = {
16814             tag: 'div',
16815             cls: 'progress-bar',
16816             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16817         };
16818         
16819         if(this.sr_only){
16820             cfg.cn = {
16821                 tag: 'span',
16822                 cls: 'sr-only',
16823                 html: this.sr_only
16824             }
16825         }
16826         
16827         if(this.role){
16828             cfg.role = this.role;
16829         }
16830         
16831         if(this.aria_valuenow){
16832             cfg['aria-valuenow'] = this.aria_valuenow;
16833         }
16834         
16835         if(this.aria_valuemin){
16836             cfg['aria-valuemin'] = this.aria_valuemin;
16837         }
16838         
16839         if(this.aria_valuemax){
16840             cfg['aria-valuemax'] = this.aria_valuemax;
16841         }
16842         
16843         if(this.label && !this.sr_only){
16844             cfg.html = this.label;
16845         }
16846         
16847         if(this.panel){
16848             cfg.cls += ' progress-bar-' + this.panel;
16849         }
16850         
16851         return cfg;
16852     },
16853     
16854     update : function(aria_valuenow)
16855     {
16856         this.aria_valuenow = aria_valuenow;
16857         
16858         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16859     }
16860    
16861 });
16862
16863  
16864
16865  /*
16866  * - LGPL
16867  *
16868  * column
16869  * 
16870  */
16871
16872 /**
16873  * @class Roo.bootstrap.TabGroup
16874  * @extends Roo.bootstrap.Column
16875  * Bootstrap Column class
16876  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16877  * @cfg {Boolean} carousel true to make the group behave like a carousel
16878  * @cfg {Boolean} bullets show bullets for the panels
16879  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16880  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16881  * @cfg {Number} timer auto slide timer .. default 0 millisecond
16882  * @cfg {Boolean} showarrow (true|false) show arrow default true
16883  * 
16884  * @constructor
16885  * Create a new TabGroup
16886  * @param {Object} config The config object
16887  */
16888
16889 Roo.bootstrap.TabGroup = function(config){
16890     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16891     if (!this.navId) {
16892         this.navId = Roo.id();
16893     }
16894     this.tabs = [];
16895     Roo.bootstrap.TabGroup.register(this);
16896     
16897 };
16898
16899 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
16900     
16901     carousel : false,
16902     transition : false,
16903     bullets : 0,
16904     timer : 0,
16905     autoslide : false,
16906     slideFn : false,
16907     slideOnTouch : false,
16908     showarrow : true,
16909     
16910     getAutoCreate : function()
16911     {
16912         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16913         
16914         cfg.cls += ' tab-content';
16915         
16916         if (this.carousel) {
16917             cfg.cls += ' carousel slide';
16918             
16919             cfg.cn = [{
16920                cls : 'carousel-inner',
16921                cn : []
16922             }];
16923         
16924             if(this.bullets  && !Roo.isTouch){
16925                 
16926                 var bullets = {
16927                     cls : 'carousel-bullets',
16928                     cn : []
16929                 };
16930                
16931                 if(this.bullets_cls){
16932                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16933                 }
16934                 
16935                 bullets.cn.push({
16936                     cls : 'clear'
16937                 });
16938                 
16939                 cfg.cn[0].cn.push(bullets);
16940             }
16941             
16942             if(this.showarrow){
16943                 cfg.cn[0].cn.push({
16944                     tag : 'div',
16945                     class : 'carousel-arrow',
16946                     cn : [
16947                         {
16948                             tag : 'div',
16949                             class : 'carousel-prev',
16950                             cn : [
16951                                 {
16952                                     tag : 'i',
16953                                     class : 'fa fa-chevron-left'
16954                                 }
16955                             ]
16956                         },
16957                         {
16958                             tag : 'div',
16959                             class : 'carousel-next',
16960                             cn : [
16961                                 {
16962                                     tag : 'i',
16963                                     class : 'fa fa-chevron-right'
16964                                 }
16965                             ]
16966                         }
16967                     ]
16968                 });
16969             }
16970             
16971         }
16972         
16973         return cfg;
16974     },
16975     
16976     initEvents:  function()
16977     {
16978         if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
16979             this.el.on("touchstart", this.onTouchStart, this);
16980         }
16981         
16982         if(this.autoslide){
16983             var _this = this;
16984             
16985             this.slideFn = window.setInterval(function() {
16986                 _this.showPanelNext();
16987             }, this.timer);
16988         }
16989         
16990         if(this.showarrow){
16991             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
16992             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
16993         }
16994         
16995         
16996     },
16997     
16998     onTouchStart : function(e, el, o)
16999     {
17000         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17001             return;
17002         }
17003         
17004         this.showPanelNext();
17005     },
17006     
17007     getChildContainer : function()
17008     {
17009         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17010     },
17011     
17012     /**
17013     * register a Navigation item
17014     * @param {Roo.bootstrap.NavItem} the navitem to add
17015     */
17016     register : function(item)
17017     {
17018         this.tabs.push( item);
17019         item.navId = this.navId; // not really needed..
17020         this.addBullet();
17021     
17022     },
17023     
17024     getActivePanel : function()
17025     {
17026         var r = false;
17027         Roo.each(this.tabs, function(t) {
17028             if (t.active) {
17029                 r = t;
17030                 return false;
17031             }
17032             return null;
17033         });
17034         return r;
17035         
17036     },
17037     getPanelByName : function(n)
17038     {
17039         var r = false;
17040         Roo.each(this.tabs, function(t) {
17041             if (t.tabId == n) {
17042                 r = t;
17043                 return false;
17044             }
17045             return null;
17046         });
17047         return r;
17048     },
17049     indexOfPanel : function(p)
17050     {
17051         var r = false;
17052         Roo.each(this.tabs, function(t,i) {
17053             if (t.tabId == p.tabId) {
17054                 r = i;
17055                 return false;
17056             }
17057             return null;
17058         });
17059         return r;
17060     },
17061     /**
17062      * show a specific panel
17063      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17064      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17065      */
17066     showPanel : function (pan)
17067     {
17068         if(this.transition || typeof(pan) == 'undefined'){
17069             Roo.log("waiting for the transitionend");
17070             return;
17071         }
17072         
17073         if (typeof(pan) == 'number') {
17074             pan = this.tabs[pan];
17075         }
17076         
17077         if (typeof(pan) == 'string') {
17078             pan = this.getPanelByName(pan);
17079         }
17080         
17081         var cur = this.getActivePanel();
17082         
17083         if(!pan || !cur){
17084             Roo.log('pan or acitve pan is undefined');
17085             return false;
17086         }
17087         
17088         if (pan.tabId == this.getActivePanel().tabId) {
17089             return true;
17090         }
17091         
17092         if (false === cur.fireEvent('beforedeactivate')) {
17093             return false;
17094         }
17095         
17096         if(this.bullets > 0 && !Roo.isTouch){
17097             this.setActiveBullet(this.indexOfPanel(pan));
17098         }
17099         
17100         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17101             
17102             this.transition = true;
17103             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17104             var lr = dir == 'next' ? 'left' : 'right';
17105             pan.el.addClass(dir); // or prev
17106             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17107             cur.el.addClass(lr); // or right
17108             pan.el.addClass(lr);
17109             
17110             var _this = this;
17111             cur.el.on('transitionend', function() {
17112                 Roo.log("trans end?");
17113                 
17114                 pan.el.removeClass([lr,dir]);
17115                 pan.setActive(true);
17116                 
17117                 cur.el.removeClass([lr]);
17118                 cur.setActive(false);
17119                 
17120                 _this.transition = false;
17121                 
17122             }, this, { single:  true } );
17123             
17124             return true;
17125         }
17126         
17127         cur.setActive(false);
17128         pan.setActive(true);
17129         
17130         return true;
17131         
17132     },
17133     showPanelNext : function()
17134     {
17135         var i = this.indexOfPanel(this.getActivePanel());
17136         
17137         if (i >= this.tabs.length - 1 && !this.autoslide) {
17138             return;
17139         }
17140         
17141         if (i >= this.tabs.length - 1 && this.autoslide) {
17142             i = -1;
17143         }
17144         
17145         this.showPanel(this.tabs[i+1]);
17146     },
17147     
17148     showPanelPrev : function()
17149     {
17150         var i = this.indexOfPanel(this.getActivePanel());
17151         
17152         if (i  < 1 && !this.autoslide) {
17153             return;
17154         }
17155         
17156         if (i < 1 && this.autoslide) {
17157             i = this.tabs.length;
17158         }
17159         
17160         this.showPanel(this.tabs[i-1]);
17161     },
17162     
17163     
17164     addBullet: function()
17165     {
17166         if(!this.bullets || Roo.isTouch){
17167             return;
17168         }
17169         var ctr = this.el.select('.carousel-bullets',true).first();
17170         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17171         var bullet = ctr.createChild({
17172             cls : 'bullet bullet-' + i
17173         },ctr.dom.lastChild);
17174         
17175         
17176         var _this = this;
17177         
17178         bullet.on('click', (function(e, el, o, ii, t){
17179
17180             e.preventDefault();
17181
17182             this.showPanel(ii);
17183
17184             if(this.autoslide && this.slideFn){
17185                 clearInterval(this.slideFn);
17186                 this.slideFn = window.setInterval(function() {
17187                     _this.showPanelNext();
17188                 }, this.timer);
17189             }
17190
17191         }).createDelegate(this, [i, bullet], true));
17192                 
17193         
17194     },
17195      
17196     setActiveBullet : function(i)
17197     {
17198         if(Roo.isTouch){
17199             return;
17200         }
17201         
17202         Roo.each(this.el.select('.bullet', true).elements, function(el){
17203             el.removeClass('selected');
17204         });
17205
17206         var bullet = this.el.select('.bullet-' + i, true).first();
17207         
17208         if(!bullet){
17209             return;
17210         }
17211         
17212         bullet.addClass('selected');
17213     }
17214     
17215     
17216   
17217 });
17218
17219  
17220
17221  
17222  
17223 Roo.apply(Roo.bootstrap.TabGroup, {
17224     
17225     groups: {},
17226      /**
17227     * register a Navigation Group
17228     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17229     */
17230     register : function(navgrp)
17231     {
17232         this.groups[navgrp.navId] = navgrp;
17233         
17234     },
17235     /**
17236     * fetch a Navigation Group based on the navigation ID
17237     * if one does not exist , it will get created.
17238     * @param {string} the navgroup to add
17239     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17240     */
17241     get: function(navId) {
17242         if (typeof(this.groups[navId]) == 'undefined') {
17243             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17244         }
17245         return this.groups[navId] ;
17246     }
17247     
17248     
17249     
17250 });
17251
17252  /*
17253  * - LGPL
17254  *
17255  * TabPanel
17256  * 
17257  */
17258
17259 /**
17260  * @class Roo.bootstrap.TabPanel
17261  * @extends Roo.bootstrap.Component
17262  * Bootstrap TabPanel class
17263  * @cfg {Boolean} active panel active
17264  * @cfg {String} html panel content
17265  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17266  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17267  * @cfg {String} href click to link..
17268  * 
17269  * 
17270  * @constructor
17271  * Create a new TabPanel
17272  * @param {Object} config The config object
17273  */
17274
17275 Roo.bootstrap.TabPanel = function(config){
17276     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17277     this.addEvents({
17278         /**
17279              * @event changed
17280              * Fires when the active status changes
17281              * @param {Roo.bootstrap.TabPanel} this
17282              * @param {Boolean} state the new state
17283             
17284          */
17285         'changed': true,
17286         /**
17287              * @event beforedeactivate
17288              * Fires before a tab is de-activated - can be used to do validation on a form.
17289              * @param {Roo.bootstrap.TabPanel} this
17290              * @return {Boolean} false if there is an error
17291             
17292          */
17293         'beforedeactivate': true
17294      });
17295     
17296     this.tabId = this.tabId || Roo.id();
17297   
17298 };
17299
17300 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17301     
17302     active: false,
17303     html: false,
17304     tabId: false,
17305     navId : false,
17306     href : '',
17307     
17308     getAutoCreate : function(){
17309         var cfg = {
17310             tag: 'div',
17311             // item is needed for carousel - not sure if it has any effect otherwise
17312             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17313             html: this.html || ''
17314         };
17315         
17316         if(this.active){
17317             cfg.cls += ' active';
17318         }
17319         
17320         if(this.tabId){
17321             cfg.tabId = this.tabId;
17322         }
17323         
17324         
17325         return cfg;
17326     },
17327     
17328     initEvents:  function()
17329     {
17330         var p = this.parent();
17331         this.navId = this.navId || p.navId;
17332         
17333         if (typeof(this.navId) != 'undefined') {
17334             // not really needed.. but just in case.. parent should be a NavGroup.
17335             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17336             
17337             tg.register(this);
17338             
17339             var i = tg.tabs.length - 1;
17340             
17341             if(this.active && tg.bullets > 0 && i < tg.bullets){
17342                 tg.setActiveBullet(i);
17343             }
17344         }
17345         
17346         if(this.href.length){
17347             this.el.on('click', this.onClick, this);
17348         }
17349         
17350     },
17351     
17352     onRender : function(ct, position)
17353     {
17354        // Roo.log("Call onRender: " + this.xtype);
17355         
17356         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17357         
17358         
17359         
17360         
17361         
17362     },
17363     
17364     setActive: function(state)
17365     {
17366         Roo.log("panel - set active " + this.tabId + "=" + state);
17367         
17368         this.active = state;
17369         if (!state) {
17370             this.el.removeClass('active');
17371             
17372         } else  if (!this.el.hasClass('active')) {
17373             this.el.addClass('active');
17374         }
17375         
17376         this.fireEvent('changed', this, state);
17377     },
17378     
17379     onClick: function(e)
17380     {
17381         e.preventDefault();
17382         
17383         window.location.href = this.href;
17384     }
17385     
17386     
17387 });
17388  
17389
17390  
17391
17392  /*
17393  * - LGPL
17394  *
17395  * DateField
17396  * 
17397  */
17398
17399 /**
17400  * @class Roo.bootstrap.DateField
17401  * @extends Roo.bootstrap.Input
17402  * Bootstrap DateField class
17403  * @cfg {Number} weekStart default 0
17404  * @cfg {String} viewMode default empty, (months|years)
17405  * @cfg {String} minViewMode default empty, (months|years)
17406  * @cfg {Number} startDate default -Infinity
17407  * @cfg {Number} endDate default Infinity
17408  * @cfg {Boolean} todayHighlight default false
17409  * @cfg {Boolean} todayBtn default false
17410  * @cfg {Boolean} calendarWeeks default false
17411  * @cfg {Object} daysOfWeekDisabled default empty
17412  * @cfg {Boolean} singleMode default false (true | false)
17413  * 
17414  * @cfg {Boolean} keyboardNavigation default true
17415  * @cfg {String} language default en
17416  * 
17417  * @constructor
17418  * Create a new DateField
17419  * @param {Object} config The config object
17420  */
17421
17422 Roo.bootstrap.DateField = function(config){
17423     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
17424      this.addEvents({
17425             /**
17426              * @event show
17427              * Fires when this field show.
17428              * @param {Roo.bootstrap.DateField} this
17429              * @param {Mixed} date The date value
17430              */
17431             show : true,
17432             /**
17433              * @event show
17434              * Fires when this field hide.
17435              * @param {Roo.bootstrap.DateField} this
17436              * @param {Mixed} date The date value
17437              */
17438             hide : true,
17439             /**
17440              * @event select
17441              * Fires when select a date.
17442              * @param {Roo.bootstrap.DateField} this
17443              * @param {Mixed} date The date value
17444              */
17445             select : true,
17446             /**
17447              * @event beforeselect
17448              * Fires when before select a date.
17449              * @param {Roo.bootstrap.DateField} this
17450              * @param {Mixed} date The date value
17451              */
17452             beforeselect : true
17453         });
17454 };
17455
17456 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
17457     
17458     /**
17459      * @cfg {String} format
17460      * The default date format string which can be overriden for localization support.  The format must be
17461      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17462      */
17463     format : "m/d/y",
17464     /**
17465      * @cfg {String} altFormats
17466      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17467      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17468      */
17469     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17470     
17471     weekStart : 0,
17472     
17473     viewMode : '',
17474     
17475     minViewMode : '',
17476     
17477     todayHighlight : false,
17478     
17479     todayBtn: false,
17480     
17481     language: 'en',
17482     
17483     keyboardNavigation: true,
17484     
17485     calendarWeeks: false,
17486     
17487     startDate: -Infinity,
17488     
17489     endDate: Infinity,
17490     
17491     daysOfWeekDisabled: [],
17492     
17493     _events: [],
17494     
17495     singleMode : false,
17496     
17497     UTCDate: function()
17498     {
17499         return new Date(Date.UTC.apply(Date, arguments));
17500     },
17501     
17502     UTCToday: function()
17503     {
17504         var today = new Date();
17505         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
17506     },
17507     
17508     getDate: function() {
17509             var d = this.getUTCDate();
17510             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
17511     },
17512     
17513     getUTCDate: function() {
17514             return this.date;
17515     },
17516     
17517     setDate: function(d) {
17518             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
17519     },
17520     
17521     setUTCDate: function(d) {
17522             this.date = d;
17523             this.setValue(this.formatDate(this.date));
17524     },
17525         
17526     onRender: function(ct, position)
17527     {
17528         
17529         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
17530         
17531         this.language = this.language || 'en';
17532         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
17533         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
17534         
17535         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17536         this.format = this.format || 'm/d/y';
17537         this.isInline = false;
17538         this.isInput = true;
17539         this.component = this.el.select('.add-on', true).first() || false;
17540         this.component = (this.component && this.component.length === 0) ? false : this.component;
17541         this.hasInput = this.component && this.inputEl().length;
17542         
17543         if (typeof(this.minViewMode === 'string')) {
17544             switch (this.minViewMode) {
17545                 case 'months':
17546                     this.minViewMode = 1;
17547                     break;
17548                 case 'years':
17549                     this.minViewMode = 2;
17550                     break;
17551                 default:
17552                     this.minViewMode = 0;
17553                     break;
17554             }
17555         }
17556         
17557         if (typeof(this.viewMode === 'string')) {
17558             switch (this.viewMode) {
17559                 case 'months':
17560                     this.viewMode = 1;
17561                     break;
17562                 case 'years':
17563                     this.viewMode = 2;
17564                     break;
17565                 default:
17566                     this.viewMode = 0;
17567                     break;
17568             }
17569         }
17570                 
17571         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
17572         
17573 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
17574         
17575         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17576         
17577         this.picker().on('mousedown', this.onMousedown, this);
17578         this.picker().on('click', this.onClick, this);
17579         
17580         this.picker().addClass('datepicker-dropdown');
17581         
17582         this.startViewMode = this.viewMode;
17583         
17584         if(this.singleMode){
17585             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17586                 v.setVisibilityMode(Roo.Element.DISPLAY);
17587                 v.hide();
17588             });
17589             
17590             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17591                 v.setStyle('width', '189px');
17592             });
17593         }
17594         
17595         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17596             if(!this.calendarWeeks){
17597                 v.remove();
17598                 return;
17599             }
17600             
17601             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17602             v.attr('colspan', function(i, val){
17603                 return parseInt(val) + 1;
17604             });
17605         });
17606                         
17607         
17608         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17609         
17610         this.setStartDate(this.startDate);
17611         this.setEndDate(this.endDate);
17612         
17613         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17614         
17615         this.fillDow();
17616         this.fillMonths();
17617         this.update();
17618         this.showMode();
17619         
17620         if(this.isInline) {
17621             this.show();
17622         }
17623     },
17624     
17625     picker : function()
17626     {
17627         return this.pickerEl;
17628 //        return this.el.select('.datepicker', true).first();
17629     },
17630     
17631     fillDow: function()
17632     {
17633         var dowCnt = this.weekStart;
17634         
17635         var dow = {
17636             tag: 'tr',
17637             cn: [
17638                 
17639             ]
17640         };
17641         
17642         if(this.calendarWeeks){
17643             dow.cn.push({
17644                 tag: 'th',
17645                 cls: 'cw',
17646                 html: '&nbsp;'
17647             })
17648         }
17649         
17650         while (dowCnt < this.weekStart + 7) {
17651             dow.cn.push({
17652                 tag: 'th',
17653                 cls: 'dow',
17654                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17655             });
17656         }
17657         
17658         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17659     },
17660     
17661     fillMonths: function()
17662     {    
17663         var i = 0;
17664         var months = this.picker().select('>.datepicker-months td', true).first();
17665         
17666         months.dom.innerHTML = '';
17667         
17668         while (i < 12) {
17669             var month = {
17670                 tag: 'span',
17671                 cls: 'month',
17672                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17673             };
17674             
17675             months.createChild(month);
17676         }
17677         
17678     },
17679     
17680     update: function()
17681     {
17682         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;
17683         
17684         if (this.date < this.startDate) {
17685             this.viewDate = new Date(this.startDate);
17686         } else if (this.date > this.endDate) {
17687             this.viewDate = new Date(this.endDate);
17688         } else {
17689             this.viewDate = new Date(this.date);
17690         }
17691         
17692         this.fill();
17693     },
17694     
17695     fill: function() 
17696     {
17697         var d = new Date(this.viewDate),
17698                 year = d.getUTCFullYear(),
17699                 month = d.getUTCMonth(),
17700                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17701                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17702                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17703                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17704                 currentDate = this.date && this.date.valueOf(),
17705                 today = this.UTCToday();
17706         
17707         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17708         
17709 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17710         
17711 //        this.picker.select('>tfoot th.today').
17712 //                                              .text(dates[this.language].today)
17713 //                                              .toggle(this.todayBtn !== false);
17714     
17715         this.updateNavArrows();
17716         this.fillMonths();
17717                                                 
17718         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17719         
17720         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17721          
17722         prevMonth.setUTCDate(day);
17723         
17724         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17725         
17726         var nextMonth = new Date(prevMonth);
17727         
17728         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17729         
17730         nextMonth = nextMonth.valueOf();
17731         
17732         var fillMonths = false;
17733         
17734         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17735         
17736         while(prevMonth.valueOf() < nextMonth) {
17737             var clsName = '';
17738             
17739             if (prevMonth.getUTCDay() === this.weekStart) {
17740                 if(fillMonths){
17741                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17742                 }
17743                     
17744                 fillMonths = {
17745                     tag: 'tr',
17746                     cn: []
17747                 };
17748                 
17749                 if(this.calendarWeeks){
17750                     // ISO 8601: First week contains first thursday.
17751                     // ISO also states week starts on Monday, but we can be more abstract here.
17752                     var
17753                     // Start of current week: based on weekstart/current date
17754                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17755                     // Thursday of this week
17756                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17757                     // First Thursday of year, year from thursday
17758                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17759                     // Calendar week: ms between thursdays, div ms per day, div 7 days
17760                     calWeek =  (th - yth) / 864e5 / 7 + 1;
17761                     
17762                     fillMonths.cn.push({
17763                         tag: 'td',
17764                         cls: 'cw',
17765                         html: calWeek
17766                     });
17767                 }
17768             }
17769             
17770             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17771                 clsName += ' old';
17772             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17773                 clsName += ' new';
17774             }
17775             if (this.todayHighlight &&
17776                 prevMonth.getUTCFullYear() == today.getFullYear() &&
17777                 prevMonth.getUTCMonth() == today.getMonth() &&
17778                 prevMonth.getUTCDate() == today.getDate()) {
17779                 clsName += ' today';
17780             }
17781             
17782             if (currentDate && prevMonth.valueOf() === currentDate) {
17783                 clsName += ' active';
17784             }
17785             
17786             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17787                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17788                     clsName += ' disabled';
17789             }
17790             
17791             fillMonths.cn.push({
17792                 tag: 'td',
17793                 cls: 'day ' + clsName,
17794                 html: prevMonth.getDate()
17795             });
17796             
17797             prevMonth.setDate(prevMonth.getDate()+1);
17798         }
17799           
17800         var currentYear = this.date && this.date.getUTCFullYear();
17801         var currentMonth = this.date && this.date.getUTCMonth();
17802         
17803         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17804         
17805         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17806             v.removeClass('active');
17807             
17808             if(currentYear === year && k === currentMonth){
17809                 v.addClass('active');
17810             }
17811             
17812             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17813                 v.addClass('disabled');
17814             }
17815             
17816         });
17817         
17818         
17819         year = parseInt(year/10, 10) * 10;
17820         
17821         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17822         
17823         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17824         
17825         year -= 1;
17826         for (var i = -1; i < 11; i++) {
17827             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17828                 tag: 'span',
17829                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17830                 html: year
17831             });
17832             
17833             year += 1;
17834         }
17835     },
17836     
17837     showMode: function(dir) 
17838     {
17839         if (dir) {
17840             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17841         }
17842         
17843         Roo.each(this.picker().select('>div',true).elements, function(v){
17844             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17845             v.hide();
17846         });
17847         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17848     },
17849     
17850     place: function()
17851     {
17852         if(this.isInline) {
17853             return;
17854         }
17855         
17856         this.picker().removeClass(['bottom', 'top']);
17857         
17858         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17859             /*
17860              * place to the top of element!
17861              *
17862              */
17863             
17864             this.picker().addClass('top');
17865             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17866             
17867             return;
17868         }
17869         
17870         this.picker().addClass('bottom');
17871         
17872         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17873     },
17874     
17875     parseDate : function(value)
17876     {
17877         if(!value || value instanceof Date){
17878             return value;
17879         }
17880         var v = Date.parseDate(value, this.format);
17881         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17882             v = Date.parseDate(value, 'Y-m-d');
17883         }
17884         if(!v && this.altFormats){
17885             if(!this.altFormatsArray){
17886                 this.altFormatsArray = this.altFormats.split("|");
17887             }
17888             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17889                 v = Date.parseDate(value, this.altFormatsArray[i]);
17890             }
17891         }
17892         return v;
17893     },
17894     
17895     formatDate : function(date, fmt)
17896     {   
17897         return (!date || !(date instanceof Date)) ?
17898         date : date.dateFormat(fmt || this.format);
17899     },
17900     
17901     onFocus : function()
17902     {
17903         Roo.bootstrap.DateField.superclass.onFocus.call(this);
17904         this.show();
17905     },
17906     
17907     onBlur : function()
17908     {
17909         Roo.bootstrap.DateField.superclass.onBlur.call(this);
17910         
17911         var d = this.inputEl().getValue();
17912         
17913         this.setValue(d);
17914                 
17915         this.hide();
17916     },
17917     
17918     show : function()
17919     {
17920         this.picker().show();
17921         this.update();
17922         this.place();
17923         
17924         this.fireEvent('show', this, this.date);
17925     },
17926     
17927     hide : function()
17928     {
17929         if(this.isInline) {
17930             return;
17931         }
17932         this.picker().hide();
17933         this.viewMode = this.startViewMode;
17934         this.showMode();
17935         
17936         this.fireEvent('hide', this, this.date);
17937         
17938     },
17939     
17940     onMousedown: function(e)
17941     {
17942         e.stopPropagation();
17943         e.preventDefault();
17944     },
17945     
17946     keyup: function(e)
17947     {
17948         Roo.bootstrap.DateField.superclass.keyup.call(this);
17949         this.update();
17950     },
17951
17952     setValue: function(v)
17953     {
17954         if(this.fireEvent('beforeselect', this, v) !== false){
17955             var d = new Date(this.parseDate(v) ).clearTime();
17956         
17957             if(isNaN(d.getTime())){
17958                 this.date = this.viewDate = '';
17959                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17960                 return;
17961             }
17962
17963             v = this.formatDate(d);
17964
17965             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17966
17967             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17968
17969             this.update();
17970
17971             this.fireEvent('select', this, this.date);
17972         }
17973     },
17974     
17975     getValue: function()
17976     {
17977         return this.formatDate(this.date);
17978     },
17979     
17980     fireKey: function(e)
17981     {
17982         if (!this.picker().isVisible()){
17983             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17984                 this.show();
17985             }
17986             return;
17987         }
17988         
17989         var dateChanged = false,
17990         dir, day, month,
17991         newDate, newViewDate;
17992         
17993         switch(e.keyCode){
17994             case 27: // escape
17995                 this.hide();
17996                 e.preventDefault();
17997                 break;
17998             case 37: // left
17999             case 39: // right
18000                 if (!this.keyboardNavigation) {
18001                     break;
18002                 }
18003                 dir = e.keyCode == 37 ? -1 : 1;
18004                 
18005                 if (e.ctrlKey){
18006                     newDate = this.moveYear(this.date, dir);
18007                     newViewDate = this.moveYear(this.viewDate, dir);
18008                 } else if (e.shiftKey){
18009                     newDate = this.moveMonth(this.date, dir);
18010                     newViewDate = this.moveMonth(this.viewDate, dir);
18011                 } else {
18012                     newDate = new Date(this.date);
18013                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18014                     newViewDate = new Date(this.viewDate);
18015                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18016                 }
18017                 if (this.dateWithinRange(newDate)){
18018                     this.date = newDate;
18019                     this.viewDate = newViewDate;
18020                     this.setValue(this.formatDate(this.date));
18021 //                    this.update();
18022                     e.preventDefault();
18023                     dateChanged = true;
18024                 }
18025                 break;
18026             case 38: // up
18027             case 40: // down
18028                 if (!this.keyboardNavigation) {
18029                     break;
18030                 }
18031                 dir = e.keyCode == 38 ? -1 : 1;
18032                 if (e.ctrlKey){
18033                     newDate = this.moveYear(this.date, dir);
18034                     newViewDate = this.moveYear(this.viewDate, dir);
18035                 } else if (e.shiftKey){
18036                     newDate = this.moveMonth(this.date, dir);
18037                     newViewDate = this.moveMonth(this.viewDate, dir);
18038                 } else {
18039                     newDate = new Date(this.date);
18040                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18041                     newViewDate = new Date(this.viewDate);
18042                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18043                 }
18044                 if (this.dateWithinRange(newDate)){
18045                     this.date = newDate;
18046                     this.viewDate = newViewDate;
18047                     this.setValue(this.formatDate(this.date));
18048 //                    this.update();
18049                     e.preventDefault();
18050                     dateChanged = true;
18051                 }
18052                 break;
18053             case 13: // enter
18054                 this.setValue(this.formatDate(this.date));
18055                 this.hide();
18056                 e.preventDefault();
18057                 break;
18058             case 9: // tab
18059                 this.setValue(this.formatDate(this.date));
18060                 this.hide();
18061                 break;
18062             case 16: // shift
18063             case 17: // ctrl
18064             case 18: // alt
18065                 break;
18066             default :
18067                 this.hide();
18068                 
18069         }
18070     },
18071     
18072     
18073     onClick: function(e) 
18074     {
18075         e.stopPropagation();
18076         e.preventDefault();
18077         
18078         var target = e.getTarget();
18079         
18080         if(target.nodeName.toLowerCase() === 'i'){
18081             target = Roo.get(target).dom.parentNode;
18082         }
18083         
18084         var nodeName = target.nodeName;
18085         var className = target.className;
18086         var html = target.innerHTML;
18087         //Roo.log(nodeName);
18088         
18089         switch(nodeName.toLowerCase()) {
18090             case 'th':
18091                 switch(className) {
18092                     case 'switch':
18093                         this.showMode(1);
18094                         break;
18095                     case 'prev':
18096                     case 'next':
18097                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18098                         switch(this.viewMode){
18099                                 case 0:
18100                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18101                                         break;
18102                                 case 1:
18103                                 case 2:
18104                                         this.viewDate = this.moveYear(this.viewDate, dir);
18105                                         break;
18106                         }
18107                         this.fill();
18108                         break;
18109                     case 'today':
18110                         var date = new Date();
18111                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18112 //                        this.fill()
18113                         this.setValue(this.formatDate(this.date));
18114                         
18115                         this.hide();
18116                         break;
18117                 }
18118                 break;
18119             case 'span':
18120                 if (className.indexOf('disabled') < 0) {
18121                     this.viewDate.setUTCDate(1);
18122                     if (className.indexOf('month') > -1) {
18123                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18124                     } else {
18125                         var year = parseInt(html, 10) || 0;
18126                         this.viewDate.setUTCFullYear(year);
18127                         
18128                     }
18129                     
18130                     if(this.singleMode){
18131                         this.setValue(this.formatDate(this.viewDate));
18132                         this.hide();
18133                         return;
18134                     }
18135                     
18136                     this.showMode(-1);
18137                     this.fill();
18138                 }
18139                 break;
18140                 
18141             case 'td':
18142                 //Roo.log(className);
18143                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18144                     var day = parseInt(html, 10) || 1;
18145                     var year = this.viewDate.getUTCFullYear(),
18146                         month = this.viewDate.getUTCMonth();
18147
18148                     if (className.indexOf('old') > -1) {
18149                         if(month === 0 ){
18150                             month = 11;
18151                             year -= 1;
18152                         }else{
18153                             month -= 1;
18154                         }
18155                     } else if (className.indexOf('new') > -1) {
18156                         if (month == 11) {
18157                             month = 0;
18158                             year += 1;
18159                         } else {
18160                             month += 1;
18161                         }
18162                     }
18163                     //Roo.log([year,month,day]);
18164                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18165                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18166 //                    this.fill();
18167                     //Roo.log(this.formatDate(this.date));
18168                     this.setValue(this.formatDate(this.date));
18169                     this.hide();
18170                 }
18171                 break;
18172         }
18173     },
18174     
18175     setStartDate: function(startDate)
18176     {
18177         this.startDate = startDate || -Infinity;
18178         if (this.startDate !== -Infinity) {
18179             this.startDate = this.parseDate(this.startDate);
18180         }
18181         this.update();
18182         this.updateNavArrows();
18183     },
18184
18185     setEndDate: function(endDate)
18186     {
18187         this.endDate = endDate || Infinity;
18188         if (this.endDate !== Infinity) {
18189             this.endDate = this.parseDate(this.endDate);
18190         }
18191         this.update();
18192         this.updateNavArrows();
18193     },
18194     
18195     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18196     {
18197         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18198         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18199             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18200         }
18201         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18202             return parseInt(d, 10);
18203         });
18204         this.update();
18205         this.updateNavArrows();
18206     },
18207     
18208     updateNavArrows: function() 
18209     {
18210         if(this.singleMode){
18211             return;
18212         }
18213         
18214         var d = new Date(this.viewDate),
18215         year = d.getUTCFullYear(),
18216         month = d.getUTCMonth();
18217         
18218         Roo.each(this.picker().select('.prev', true).elements, function(v){
18219             v.show();
18220             switch (this.viewMode) {
18221                 case 0:
18222
18223                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18224                         v.hide();
18225                     }
18226                     break;
18227                 case 1:
18228                 case 2:
18229                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18230                         v.hide();
18231                     }
18232                     break;
18233             }
18234         });
18235         
18236         Roo.each(this.picker().select('.next', true).elements, function(v){
18237             v.show();
18238             switch (this.viewMode) {
18239                 case 0:
18240
18241                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18242                         v.hide();
18243                     }
18244                     break;
18245                 case 1:
18246                 case 2:
18247                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18248                         v.hide();
18249                     }
18250                     break;
18251             }
18252         })
18253     },
18254     
18255     moveMonth: function(date, dir)
18256     {
18257         if (!dir) {
18258             return date;
18259         }
18260         var new_date = new Date(date.valueOf()),
18261         day = new_date.getUTCDate(),
18262         month = new_date.getUTCMonth(),
18263         mag = Math.abs(dir),
18264         new_month, test;
18265         dir = dir > 0 ? 1 : -1;
18266         if (mag == 1){
18267             test = dir == -1
18268             // If going back one month, make sure month is not current month
18269             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18270             ? function(){
18271                 return new_date.getUTCMonth() == month;
18272             }
18273             // If going forward one month, make sure month is as expected
18274             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18275             : function(){
18276                 return new_date.getUTCMonth() != new_month;
18277             };
18278             new_month = month + dir;
18279             new_date.setUTCMonth(new_month);
18280             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18281             if (new_month < 0 || new_month > 11) {
18282                 new_month = (new_month + 12) % 12;
18283             }
18284         } else {
18285             // For magnitudes >1, move one month at a time...
18286             for (var i=0; i<mag; i++) {
18287                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18288                 new_date = this.moveMonth(new_date, dir);
18289             }
18290             // ...then reset the day, keeping it in the new month
18291             new_month = new_date.getUTCMonth();
18292             new_date.setUTCDate(day);
18293             test = function(){
18294                 return new_month != new_date.getUTCMonth();
18295             };
18296         }
18297         // Common date-resetting loop -- if date is beyond end of month, make it
18298         // end of month
18299         while (test()){
18300             new_date.setUTCDate(--day);
18301             new_date.setUTCMonth(new_month);
18302         }
18303         return new_date;
18304     },
18305
18306     moveYear: function(date, dir)
18307     {
18308         return this.moveMonth(date, dir*12);
18309     },
18310
18311     dateWithinRange: function(date)
18312     {
18313         return date >= this.startDate && date <= this.endDate;
18314     },
18315
18316     
18317     remove: function() 
18318     {
18319         this.picker().remove();
18320     },
18321     
18322     validateValue : function(value)
18323     {
18324         if(value.length < 1)  {
18325             if(this.allowBlank){
18326                 return true;
18327             }
18328             return false;
18329         }
18330         
18331         if(value.length < this.minLength){
18332             return false;
18333         }
18334         if(value.length > this.maxLength){
18335             return false;
18336         }
18337         if(this.vtype){
18338             var vt = Roo.form.VTypes;
18339             if(!vt[this.vtype](value, this)){
18340                 return false;
18341             }
18342         }
18343         if(typeof this.validator == "function"){
18344             var msg = this.validator(value);
18345             if(msg !== true){
18346                 return false;
18347             }
18348         }
18349         
18350         if(this.regex && !this.regex.test(value)){
18351             return false;
18352         }
18353         
18354         if(typeof(this.parseDate(value)) == 'undefined'){
18355             return false;
18356         }
18357         
18358         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
18359             return false;
18360         }      
18361         
18362         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
18363             return false;
18364         } 
18365         
18366         
18367         return true;
18368     }
18369    
18370 });
18371
18372 Roo.apply(Roo.bootstrap.DateField,  {
18373     
18374     head : {
18375         tag: 'thead',
18376         cn: [
18377         {
18378             tag: 'tr',
18379             cn: [
18380             {
18381                 tag: 'th',
18382                 cls: 'prev',
18383                 html: '<i class="fa fa-arrow-left"/>'
18384             },
18385             {
18386                 tag: 'th',
18387                 cls: 'switch',
18388                 colspan: '5'
18389             },
18390             {
18391                 tag: 'th',
18392                 cls: 'next',
18393                 html: '<i class="fa fa-arrow-right"/>'
18394             }
18395
18396             ]
18397         }
18398         ]
18399     },
18400     
18401     content : {
18402         tag: 'tbody',
18403         cn: [
18404         {
18405             tag: 'tr',
18406             cn: [
18407             {
18408                 tag: 'td',
18409                 colspan: '7'
18410             }
18411             ]
18412         }
18413         ]
18414     },
18415     
18416     footer : {
18417         tag: 'tfoot',
18418         cn: [
18419         {
18420             tag: 'tr',
18421             cn: [
18422             {
18423                 tag: 'th',
18424                 colspan: '7',
18425                 cls: 'today'
18426             }
18427                     
18428             ]
18429         }
18430         ]
18431     },
18432     
18433     dates:{
18434         en: {
18435             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
18436             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
18437             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
18438             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18439             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
18440             today: "Today"
18441         }
18442     },
18443     
18444     modes: [
18445     {
18446         clsName: 'days',
18447         navFnc: 'Month',
18448         navStep: 1
18449     },
18450     {
18451         clsName: 'months',
18452         navFnc: 'FullYear',
18453         navStep: 1
18454     },
18455     {
18456         clsName: 'years',
18457         navFnc: 'FullYear',
18458         navStep: 10
18459     }]
18460 });
18461
18462 Roo.apply(Roo.bootstrap.DateField,  {
18463   
18464     template : {
18465         tag: 'div',
18466         cls: 'datepicker dropdown-menu roo-dynamic',
18467         cn: [
18468         {
18469             tag: 'div',
18470             cls: 'datepicker-days',
18471             cn: [
18472             {
18473                 tag: 'table',
18474                 cls: 'table-condensed',
18475                 cn:[
18476                 Roo.bootstrap.DateField.head,
18477                 {
18478                     tag: 'tbody'
18479                 },
18480                 Roo.bootstrap.DateField.footer
18481                 ]
18482             }
18483             ]
18484         },
18485         {
18486             tag: 'div',
18487             cls: 'datepicker-months',
18488             cn: [
18489             {
18490                 tag: 'table',
18491                 cls: 'table-condensed',
18492                 cn:[
18493                 Roo.bootstrap.DateField.head,
18494                 Roo.bootstrap.DateField.content,
18495                 Roo.bootstrap.DateField.footer
18496                 ]
18497             }
18498             ]
18499         },
18500         {
18501             tag: 'div',
18502             cls: 'datepicker-years',
18503             cn: [
18504             {
18505                 tag: 'table',
18506                 cls: 'table-condensed',
18507                 cn:[
18508                 Roo.bootstrap.DateField.head,
18509                 Roo.bootstrap.DateField.content,
18510                 Roo.bootstrap.DateField.footer
18511                 ]
18512             }
18513             ]
18514         }
18515         ]
18516     }
18517 });
18518
18519  
18520
18521  /*
18522  * - LGPL
18523  *
18524  * TimeField
18525  * 
18526  */
18527
18528 /**
18529  * @class Roo.bootstrap.TimeField
18530  * @extends Roo.bootstrap.Input
18531  * Bootstrap DateField class
18532  * 
18533  * 
18534  * @constructor
18535  * Create a new TimeField
18536  * @param {Object} config The config object
18537  */
18538
18539 Roo.bootstrap.TimeField = function(config){
18540     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
18541     this.addEvents({
18542             /**
18543              * @event show
18544              * Fires when this field show.
18545              * @param {Roo.bootstrap.DateField} thisthis
18546              * @param {Mixed} date The date value
18547              */
18548             show : true,
18549             /**
18550              * @event show
18551              * Fires when this field hide.
18552              * @param {Roo.bootstrap.DateField} this
18553              * @param {Mixed} date The date value
18554              */
18555             hide : true,
18556             /**
18557              * @event select
18558              * Fires when select a date.
18559              * @param {Roo.bootstrap.DateField} this
18560              * @param {Mixed} date The date value
18561              */
18562             select : true
18563         });
18564 };
18565
18566 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
18567     
18568     /**
18569      * @cfg {String} format
18570      * The default time format string which can be overriden for localization support.  The format must be
18571      * valid according to {@link Date#parseDate} (defaults to 'H:i').
18572      */
18573     format : "H:i",
18574        
18575     onRender: function(ct, position)
18576     {
18577         
18578         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
18579                 
18580         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
18581         
18582         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18583         
18584         this.pop = this.picker().select('>.datepicker-time',true).first();
18585         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18586         
18587         this.picker().on('mousedown', this.onMousedown, this);
18588         this.picker().on('click', this.onClick, this);
18589         
18590         this.picker().addClass('datepicker-dropdown');
18591     
18592         this.fillTime();
18593         this.update();
18594             
18595         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
18596         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
18597         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
18598         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
18599         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
18600         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
18601
18602     },
18603     
18604     fireKey: function(e){
18605         if (!this.picker().isVisible()){
18606             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18607                 this.show();
18608             }
18609             return;
18610         }
18611
18612         e.preventDefault();
18613         
18614         switch(e.keyCode){
18615             case 27: // escape
18616                 this.hide();
18617                 break;
18618             case 37: // left
18619             case 39: // right
18620                 this.onTogglePeriod();
18621                 break;
18622             case 38: // up
18623                 this.onIncrementMinutes();
18624                 break;
18625             case 40: // down
18626                 this.onDecrementMinutes();
18627                 break;
18628             case 13: // enter
18629             case 9: // tab
18630                 this.setTime();
18631                 break;
18632         }
18633     },
18634     
18635     onClick: function(e) {
18636         e.stopPropagation();
18637         e.preventDefault();
18638     },
18639     
18640     picker : function()
18641     {
18642         return this.el.select('.datepicker', true).first();
18643     },
18644     
18645     fillTime: function()
18646     {    
18647         var time = this.pop.select('tbody', true).first();
18648         
18649         time.dom.innerHTML = '';
18650         
18651         time.createChild({
18652             tag: 'tr',
18653             cn: [
18654                 {
18655                     tag: 'td',
18656                     cn: [
18657                         {
18658                             tag: 'a',
18659                             href: '#',
18660                             cls: 'btn',
18661                             cn: [
18662                                 {
18663                                     tag: 'span',
18664                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
18665                                 }
18666                             ]
18667                         } 
18668                     ]
18669                 },
18670                 {
18671                     tag: 'td',
18672                     cls: 'separator'
18673                 },
18674                 {
18675                     tag: 'td',
18676                     cn: [
18677                         {
18678                             tag: 'a',
18679                             href: '#',
18680                             cls: 'btn',
18681                             cn: [
18682                                 {
18683                                     tag: 'span',
18684                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
18685                                 }
18686                             ]
18687                         }
18688                     ]
18689                 },
18690                 {
18691                     tag: 'td',
18692                     cls: 'separator'
18693                 }
18694             ]
18695         });
18696         
18697         time.createChild({
18698             tag: 'tr',
18699             cn: [
18700                 {
18701                     tag: 'td',
18702                     cn: [
18703                         {
18704                             tag: 'span',
18705                             cls: 'timepicker-hour',
18706                             html: '00'
18707                         }  
18708                     ]
18709                 },
18710                 {
18711                     tag: 'td',
18712                     cls: 'separator',
18713                     html: ':'
18714                 },
18715                 {
18716                     tag: 'td',
18717                     cn: [
18718                         {
18719                             tag: 'span',
18720                             cls: 'timepicker-minute',
18721                             html: '00'
18722                         }  
18723                     ]
18724                 },
18725                 {
18726                     tag: 'td',
18727                     cls: 'separator'
18728                 },
18729                 {
18730                     tag: 'td',
18731                     cn: [
18732                         {
18733                             tag: 'button',
18734                             type: 'button',
18735                             cls: 'btn btn-primary period',
18736                             html: 'AM'
18737                             
18738                         }
18739                     ]
18740                 }
18741             ]
18742         });
18743         
18744         time.createChild({
18745             tag: 'tr',
18746             cn: [
18747                 {
18748                     tag: 'td',
18749                     cn: [
18750                         {
18751                             tag: 'a',
18752                             href: '#',
18753                             cls: 'btn',
18754                             cn: [
18755                                 {
18756                                     tag: 'span',
18757                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
18758                                 }
18759                             ]
18760                         }
18761                     ]
18762                 },
18763                 {
18764                     tag: 'td',
18765                     cls: 'separator'
18766                 },
18767                 {
18768                     tag: 'td',
18769                     cn: [
18770                         {
18771                             tag: 'a',
18772                             href: '#',
18773                             cls: 'btn',
18774                             cn: [
18775                                 {
18776                                     tag: 'span',
18777                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
18778                                 }
18779                             ]
18780                         }
18781                     ]
18782                 },
18783                 {
18784                     tag: 'td',
18785                     cls: 'separator'
18786                 }
18787             ]
18788         });
18789         
18790     },
18791     
18792     update: function()
18793     {
18794         
18795         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18796         
18797         this.fill();
18798     },
18799     
18800     fill: function() 
18801     {
18802         var hours = this.time.getHours();
18803         var minutes = this.time.getMinutes();
18804         var period = 'AM';
18805         
18806         if(hours > 11){
18807             period = 'PM';
18808         }
18809         
18810         if(hours == 0){
18811             hours = 12;
18812         }
18813         
18814         
18815         if(hours > 12){
18816             hours = hours - 12;
18817         }
18818         
18819         if(hours < 10){
18820             hours = '0' + hours;
18821         }
18822         
18823         if(minutes < 10){
18824             minutes = '0' + minutes;
18825         }
18826         
18827         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18828         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18829         this.pop.select('button', true).first().dom.innerHTML = period;
18830         
18831     },
18832     
18833     place: function()
18834     {   
18835         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18836         
18837         var cls = ['bottom'];
18838         
18839         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18840             cls.pop();
18841             cls.push('top');
18842         }
18843         
18844         cls.push('right');
18845         
18846         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18847             cls.pop();
18848             cls.push('left');
18849         }
18850         
18851         this.picker().addClass(cls.join('-'));
18852         
18853         var _this = this;
18854         
18855         Roo.each(cls, function(c){
18856             if(c == 'bottom'){
18857                 _this.picker().setTop(_this.inputEl().getHeight());
18858                 return;
18859             }
18860             if(c == 'top'){
18861                 _this.picker().setTop(0 - _this.picker().getHeight());
18862                 return;
18863             }
18864             
18865             if(c == 'left'){
18866                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18867                 return;
18868             }
18869             if(c == 'right'){
18870                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18871                 return;
18872             }
18873         });
18874         
18875     },
18876   
18877     onFocus : function()
18878     {
18879         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
18880         this.show();
18881     },
18882     
18883     onBlur : function()
18884     {
18885         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
18886         this.hide();
18887     },
18888     
18889     show : function()
18890     {
18891         this.picker().show();
18892         this.pop.show();
18893         this.update();
18894         this.place();
18895         
18896         this.fireEvent('show', this, this.date);
18897     },
18898     
18899     hide : function()
18900     {
18901         this.picker().hide();
18902         this.pop.hide();
18903         
18904         this.fireEvent('hide', this, this.date);
18905     },
18906     
18907     setTime : function()
18908     {
18909         this.hide();
18910         this.setValue(this.time.format(this.format));
18911         
18912         this.fireEvent('select', this, this.date);
18913         
18914         
18915     },
18916     
18917     onMousedown: function(e){
18918         e.stopPropagation();
18919         e.preventDefault();
18920     },
18921     
18922     onIncrementHours: function()
18923     {
18924         Roo.log('onIncrementHours');
18925         this.time = this.time.add(Date.HOUR, 1);
18926         this.update();
18927         
18928     },
18929     
18930     onDecrementHours: function()
18931     {
18932         Roo.log('onDecrementHours');
18933         this.time = this.time.add(Date.HOUR, -1);
18934         this.update();
18935     },
18936     
18937     onIncrementMinutes: function()
18938     {
18939         Roo.log('onIncrementMinutes');
18940         this.time = this.time.add(Date.MINUTE, 1);
18941         this.update();
18942     },
18943     
18944     onDecrementMinutes: function()
18945     {
18946         Roo.log('onDecrementMinutes');
18947         this.time = this.time.add(Date.MINUTE, -1);
18948         this.update();
18949     },
18950     
18951     onTogglePeriod: function()
18952     {
18953         Roo.log('onTogglePeriod');
18954         this.time = this.time.add(Date.HOUR, 12);
18955         this.update();
18956     }
18957     
18958    
18959 });
18960
18961 Roo.apply(Roo.bootstrap.TimeField,  {
18962     
18963     content : {
18964         tag: 'tbody',
18965         cn: [
18966             {
18967                 tag: 'tr',
18968                 cn: [
18969                 {
18970                     tag: 'td',
18971                     colspan: '7'
18972                 }
18973                 ]
18974             }
18975         ]
18976     },
18977     
18978     footer : {
18979         tag: 'tfoot',
18980         cn: [
18981             {
18982                 tag: 'tr',
18983                 cn: [
18984                 {
18985                     tag: 'th',
18986                     colspan: '7',
18987                     cls: '',
18988                     cn: [
18989                         {
18990                             tag: 'button',
18991                             cls: 'btn btn-info ok',
18992                             html: 'OK'
18993                         }
18994                     ]
18995                 }
18996
18997                 ]
18998             }
18999         ]
19000     }
19001 });
19002
19003 Roo.apply(Roo.bootstrap.TimeField,  {
19004   
19005     template : {
19006         tag: 'div',
19007         cls: 'datepicker dropdown-menu',
19008         cn: [
19009             {
19010                 tag: 'div',
19011                 cls: 'datepicker-time',
19012                 cn: [
19013                 {
19014                     tag: 'table',
19015                     cls: 'table-condensed',
19016                     cn:[
19017                     Roo.bootstrap.TimeField.content,
19018                     Roo.bootstrap.TimeField.footer
19019                     ]
19020                 }
19021                 ]
19022             }
19023         ]
19024     }
19025 });
19026
19027  
19028
19029  /*
19030  * - LGPL
19031  *
19032  * MonthField
19033  * 
19034  */
19035
19036 /**
19037  * @class Roo.bootstrap.MonthField
19038  * @extends Roo.bootstrap.Input
19039  * Bootstrap MonthField class
19040  * 
19041  * @cfg {String} language default en
19042  * 
19043  * @constructor
19044  * Create a new MonthField
19045  * @param {Object} config The config object
19046  */
19047
19048 Roo.bootstrap.MonthField = function(config){
19049     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19050     
19051     this.addEvents({
19052         /**
19053          * @event show
19054          * Fires when this field show.
19055          * @param {Roo.bootstrap.MonthField} this
19056          * @param {Mixed} date The date value
19057          */
19058         show : true,
19059         /**
19060          * @event show
19061          * Fires when this field hide.
19062          * @param {Roo.bootstrap.MonthField} this
19063          * @param {Mixed} date The date value
19064          */
19065         hide : true,
19066         /**
19067          * @event select
19068          * Fires when select a date.
19069          * @param {Roo.bootstrap.MonthField} this
19070          * @param {String} oldvalue The old value
19071          * @param {String} newvalue The new value
19072          */
19073         select : true
19074     });
19075 };
19076
19077 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19078     
19079     onRender: function(ct, position)
19080     {
19081         
19082         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19083         
19084         this.language = this.language || 'en';
19085         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19086         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19087         
19088         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19089         this.isInline = false;
19090         this.isInput = true;
19091         this.component = this.el.select('.add-on', true).first() || false;
19092         this.component = (this.component && this.component.length === 0) ? false : this.component;
19093         this.hasInput = this.component && this.inputEL().length;
19094         
19095         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19096         
19097         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19098         
19099         this.picker().on('mousedown', this.onMousedown, this);
19100         this.picker().on('click', this.onClick, this);
19101         
19102         this.picker().addClass('datepicker-dropdown');
19103         
19104         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19105             v.setStyle('width', '189px');
19106         });
19107         
19108         this.fillMonths();
19109         
19110         this.update();
19111         
19112         if(this.isInline) {
19113             this.show();
19114         }
19115         
19116     },
19117     
19118     setValue: function(v, suppressEvent)
19119     {   
19120         var o = this.getValue();
19121         
19122         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19123         
19124         this.update();
19125
19126         if(suppressEvent !== true){
19127             this.fireEvent('select', this, o, v);
19128         }
19129         
19130     },
19131     
19132     getValue: function()
19133     {
19134         return this.value;
19135     },
19136     
19137     onClick: function(e) 
19138     {
19139         e.stopPropagation();
19140         e.preventDefault();
19141         
19142         var target = e.getTarget();
19143         
19144         if(target.nodeName.toLowerCase() === 'i'){
19145             target = Roo.get(target).dom.parentNode;
19146         }
19147         
19148         var nodeName = target.nodeName;
19149         var className = target.className;
19150         var html = target.innerHTML;
19151         
19152         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19153             return;
19154         }
19155         
19156         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19157         
19158         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19159         
19160         this.hide();
19161                         
19162     },
19163     
19164     picker : function()
19165     {
19166         return this.pickerEl;
19167     },
19168     
19169     fillMonths: function()
19170     {    
19171         var i = 0;
19172         var months = this.picker().select('>.datepicker-months td', true).first();
19173         
19174         months.dom.innerHTML = '';
19175         
19176         while (i < 12) {
19177             var month = {
19178                 tag: 'span',
19179                 cls: 'month',
19180                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19181             };
19182             
19183             months.createChild(month);
19184         }
19185         
19186     },
19187     
19188     update: function()
19189     {
19190         var _this = this;
19191         
19192         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19193             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19194         }
19195         
19196         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19197             e.removeClass('active');
19198             
19199             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19200                 e.addClass('active');
19201             }
19202         })
19203     },
19204     
19205     place: function()
19206     {
19207         if(this.isInline) {
19208             return;
19209         }
19210         
19211         this.picker().removeClass(['bottom', 'top']);
19212         
19213         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19214             /*
19215              * place to the top of element!
19216              *
19217              */
19218             
19219             this.picker().addClass('top');
19220             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19221             
19222             return;
19223         }
19224         
19225         this.picker().addClass('bottom');
19226         
19227         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19228     },
19229     
19230     onFocus : function()
19231     {
19232         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19233         this.show();
19234     },
19235     
19236     onBlur : function()
19237     {
19238         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19239         
19240         var d = this.inputEl().getValue();
19241         
19242         this.setValue(d);
19243                 
19244         this.hide();
19245     },
19246     
19247     show : function()
19248     {
19249         this.picker().show();
19250         this.picker().select('>.datepicker-months', true).first().show();
19251         this.update();
19252         this.place();
19253         
19254         this.fireEvent('show', this, this.date);
19255     },
19256     
19257     hide : function()
19258     {
19259         if(this.isInline) {
19260             return;
19261         }
19262         this.picker().hide();
19263         this.fireEvent('hide', this, this.date);
19264         
19265     },
19266     
19267     onMousedown: function(e)
19268     {
19269         e.stopPropagation();
19270         e.preventDefault();
19271     },
19272     
19273     keyup: function(e)
19274     {
19275         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19276         this.update();
19277     },
19278
19279     fireKey: function(e)
19280     {
19281         if (!this.picker().isVisible()){
19282             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19283                 this.show();
19284             }
19285             return;
19286         }
19287         
19288         var dir;
19289         
19290         switch(e.keyCode){
19291             case 27: // escape
19292                 this.hide();
19293                 e.preventDefault();
19294                 break;
19295             case 37: // left
19296             case 39: // right
19297                 dir = e.keyCode == 37 ? -1 : 1;
19298                 
19299                 this.vIndex = this.vIndex + dir;
19300                 
19301                 if(this.vIndex < 0){
19302                     this.vIndex = 0;
19303                 }
19304                 
19305                 if(this.vIndex > 11){
19306                     this.vIndex = 11;
19307                 }
19308                 
19309                 if(isNaN(this.vIndex)){
19310                     this.vIndex = 0;
19311                 }
19312                 
19313                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19314                 
19315                 break;
19316             case 38: // up
19317             case 40: // down
19318                 
19319                 dir = e.keyCode == 38 ? -1 : 1;
19320                 
19321                 this.vIndex = this.vIndex + dir * 4;
19322                 
19323                 if(this.vIndex < 0){
19324                     this.vIndex = 0;
19325                 }
19326                 
19327                 if(this.vIndex > 11){
19328                     this.vIndex = 11;
19329                 }
19330                 
19331                 if(isNaN(this.vIndex)){
19332                     this.vIndex = 0;
19333                 }
19334                 
19335                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19336                 break;
19337                 
19338             case 13: // enter
19339                 
19340                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19341                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19342                 }
19343                 
19344                 this.hide();
19345                 e.preventDefault();
19346                 break;
19347             case 9: // tab
19348                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19349                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19350                 }
19351                 this.hide();
19352                 break;
19353             case 16: // shift
19354             case 17: // ctrl
19355             case 18: // alt
19356                 break;
19357             default :
19358                 this.hide();
19359                 
19360         }
19361     },
19362     
19363     remove: function() 
19364     {
19365         this.picker().remove();
19366     }
19367    
19368 });
19369
19370 Roo.apply(Roo.bootstrap.MonthField,  {
19371     
19372     content : {
19373         tag: 'tbody',
19374         cn: [
19375         {
19376             tag: 'tr',
19377             cn: [
19378             {
19379                 tag: 'td',
19380                 colspan: '7'
19381             }
19382             ]
19383         }
19384         ]
19385     },
19386     
19387     dates:{
19388         en: {
19389             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19390             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19391         }
19392     }
19393 });
19394
19395 Roo.apply(Roo.bootstrap.MonthField,  {
19396   
19397     template : {
19398         tag: 'div',
19399         cls: 'datepicker dropdown-menu roo-dynamic',
19400         cn: [
19401             {
19402                 tag: 'div',
19403                 cls: 'datepicker-months',
19404                 cn: [
19405                 {
19406                     tag: 'table',
19407                     cls: 'table-condensed',
19408                     cn:[
19409                         Roo.bootstrap.DateField.content
19410                     ]
19411                 }
19412                 ]
19413             }
19414         ]
19415     }
19416 });
19417
19418  
19419
19420  
19421  /*
19422  * - LGPL
19423  *
19424  * CheckBox
19425  * 
19426  */
19427
19428 /**
19429  * @class Roo.bootstrap.CheckBox
19430  * @extends Roo.bootstrap.Input
19431  * Bootstrap CheckBox class
19432  * 
19433  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
19434  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
19435  * @cfg {String} boxLabel The text that appears beside the checkbox
19436  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
19437  * @cfg {Boolean} checked initnal the element
19438  * @cfg {Boolean} inline inline the element (default false)
19439  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
19440  * 
19441  * @constructor
19442  * Create a new CheckBox
19443  * @param {Object} config The config object
19444  */
19445
19446 Roo.bootstrap.CheckBox = function(config){
19447     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
19448    
19449     this.addEvents({
19450         /**
19451         * @event check
19452         * Fires when the element is checked or unchecked.
19453         * @param {Roo.bootstrap.CheckBox} this This input
19454         * @param {Boolean} checked The new checked value
19455         */
19456        check : true
19457     });
19458     
19459 };
19460
19461 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
19462   
19463     inputType: 'checkbox',
19464     inputValue: 1,
19465     valueOff: 0,
19466     boxLabel: false,
19467     checked: false,
19468     weight : false,
19469     inline: false,
19470     
19471     getAutoCreate : function()
19472     {
19473         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19474         
19475         var id = Roo.id();
19476         
19477         var cfg = {};
19478         
19479         cfg.cls = 'form-group ' + this.inputType; //input-group
19480         
19481         if(this.inline){
19482             cfg.cls += ' ' + this.inputType + '-inline';
19483         }
19484         
19485         var input =  {
19486             tag: 'input',
19487             id : id,
19488             type : this.inputType,
19489             value : this.inputValue,
19490             cls : 'roo-' + this.inputType, //'form-box',
19491             placeholder : this.placeholder || ''
19492             
19493         };
19494         
19495         if(this.inputType != 'radio'){
19496             var hidden =  {
19497                 tag: 'input',
19498                 type : 'hidden',
19499                 cls : 'roo-hidden-value',
19500                 value : this.checked ? this.valueOff : this.inputValue
19501             };
19502         }
19503         
19504             
19505         if (this.weight) { // Validity check?
19506             cfg.cls += " " + this.inputType + "-" + this.weight;
19507         }
19508         
19509         if (this.disabled) {
19510             input.disabled=true;
19511         }
19512         
19513         if(this.checked){
19514             input.checked = this.checked;
19515             
19516         }
19517         
19518         
19519         if (this.name) {
19520             
19521             input.name = this.name;
19522             
19523             if(this.inputType != 'radio'){
19524                 hidden.name = this.name;
19525                 input.name = '_hidden_' + this.name;
19526             }
19527         }
19528         
19529         if (this.size) {
19530             input.cls += ' input-' + this.size;
19531         }
19532         
19533         var settings=this;
19534         
19535         ['xs','sm','md','lg'].map(function(size){
19536             if (settings[size]) {
19537                 cfg.cls += ' col-' + size + '-' + settings[size];
19538             }
19539         });
19540         
19541         var inputblock = input;
19542          
19543         if (this.before || this.after) {
19544             
19545             inputblock = {
19546                 cls : 'input-group',
19547                 cn :  [] 
19548             };
19549             
19550             if (this.before) {
19551                 inputblock.cn.push({
19552                     tag :'span',
19553                     cls : 'input-group-addon',
19554                     html : this.before
19555                 });
19556             }
19557             
19558             inputblock.cn.push(input);
19559             
19560             if(this.inputType != 'radio'){
19561                 inputblock.cn.push(hidden);
19562             }
19563             
19564             if (this.after) {
19565                 inputblock.cn.push({
19566                     tag :'span',
19567                     cls : 'input-group-addon',
19568                     html : this.after
19569                 });
19570             }
19571             
19572         }
19573         
19574         if (align ==='left' && this.fieldLabel.length) {
19575 //                Roo.log("left and has label");
19576                 cfg.cn = [
19577                     
19578                     {
19579                         tag: 'label',
19580                         'for' :  id,
19581                         cls : 'control-label col-md-' + this.labelWidth,
19582                         html : this.fieldLabel
19583                         
19584                     },
19585                     {
19586                         cls : "col-md-" + (12 - this.labelWidth), 
19587                         cn: [
19588                             inputblock
19589                         ]
19590                     }
19591                     
19592                 ];
19593         } else if ( this.fieldLabel.length) {
19594 //                Roo.log(" label");
19595                 cfg.cn = [
19596                    
19597                     {
19598                         tag: this.boxLabel ? 'span' : 'label',
19599                         'for': id,
19600                         cls: 'control-label box-input-label',
19601                         //cls : 'input-group-addon',
19602                         html : this.fieldLabel
19603                         
19604                     },
19605                     
19606                     inputblock
19607                     
19608                 ];
19609
19610         } else {
19611             
19612 //                Roo.log(" no label && no align");
19613                 cfg.cn = [  inputblock ] ;
19614                 
19615                 
19616         }
19617         
19618         if(this.boxLabel){
19619              var boxLabelCfg = {
19620                 tag: 'label',
19621                 //'for': id, // box label is handled by onclick - so no for...
19622                 cls: 'box-label',
19623                 html: this.boxLabel
19624             };
19625             
19626             if(this.tooltip){
19627                 boxLabelCfg.tooltip = this.tooltip;
19628             }
19629              
19630             cfg.cn.push(boxLabelCfg);
19631         }
19632         
19633         if(this.inputType != 'radio'){
19634             cfg.cn.push(hidden);
19635         }
19636         
19637         return cfg;
19638         
19639     },
19640     
19641     /**
19642      * return the real input element.
19643      */
19644     inputEl: function ()
19645     {
19646         return this.el.select('input.roo-' + this.inputType,true).first();
19647     },
19648     hiddenEl: function ()
19649     {
19650         return this.el.select('input.roo-hidden-value',true).first();
19651     },
19652     
19653     labelEl: function()
19654     {
19655         return this.el.select('label.control-label',true).first();
19656     },
19657     /* depricated... */
19658     
19659     label: function()
19660     {
19661         return this.labelEl();
19662     },
19663     
19664     boxLabelEl: function()
19665     {
19666         return this.el.select('label.box-label',true).first();
19667     },
19668     
19669     initEvents : function()
19670     {
19671 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19672         
19673         this.inputEl().on('click', this.onClick,  this);
19674         
19675         if (this.boxLabel) { 
19676             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
19677         }
19678         
19679         this.startValue = this.getValue();
19680         
19681         if(this.groupId){
19682             Roo.bootstrap.CheckBox.register(this);
19683         }
19684     },
19685     
19686     onClick : function()
19687     {   
19688         this.setChecked(!this.checked);
19689     },
19690     
19691     setChecked : function(state,suppressEvent)
19692     {
19693         this.startValue = this.getValue();
19694         
19695         if(this.inputType == 'radio'){
19696             
19697             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19698                 e.dom.checked = false;
19699             });
19700             
19701             this.inputEl().dom.checked = true;
19702             
19703             this.inputEl().dom.value = this.inputValue;
19704             
19705             if(suppressEvent !== true){
19706                 this.fireEvent('check', this, true);
19707             }
19708             
19709             this.validate();
19710             
19711             return;
19712         }
19713         
19714         this.checked = state;
19715         
19716         this.inputEl().dom.checked = state;
19717         
19718         
19719         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
19720         
19721         if(suppressEvent !== true){
19722             this.fireEvent('check', this, state);
19723         }
19724         
19725         this.validate();
19726     },
19727     
19728     getValue : function()
19729     {
19730         if(this.inputType == 'radio'){
19731             return this.getGroupValue();
19732         }
19733         
19734         return this.hiddenEl().dom.value;
19735         
19736     },
19737     
19738     getGroupValue : function()
19739     {
19740         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19741             return '';
19742         }
19743         
19744         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19745     },
19746     
19747     setValue : function(v,suppressEvent)
19748     {
19749         if(this.inputType == 'radio'){
19750             this.setGroupValue(v, suppressEvent);
19751             return;
19752         }
19753         
19754         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19755         
19756         this.validate();
19757     },
19758     
19759     setGroupValue : function(v, suppressEvent)
19760     {
19761         this.startValue = this.getValue();
19762         
19763         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19764             e.dom.checked = false;
19765             
19766             if(e.dom.value == v){
19767                 e.dom.checked = true;
19768             }
19769         });
19770         
19771         if(suppressEvent !== true){
19772             this.fireEvent('check', this, true);
19773         }
19774
19775         this.validate();
19776         
19777         return;
19778     },
19779     
19780     validate : function()
19781     {
19782         if(
19783                 this.disabled || 
19784                 (this.inputType == 'radio' && this.validateRadio()) ||
19785                 (this.inputType == 'checkbox' && this.validateCheckbox())
19786         ){
19787             this.markValid();
19788             return true;
19789         }
19790         
19791         this.markInvalid();
19792         return false;
19793     },
19794     
19795     validateRadio : function()
19796     {
19797         if(this.allowBlank){
19798             return true;
19799         }
19800         
19801         var valid = false;
19802         
19803         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19804             if(!e.dom.checked){
19805                 return;
19806             }
19807             
19808             valid = true;
19809             
19810             return false;
19811         });
19812         
19813         return valid;
19814     },
19815     
19816     validateCheckbox : function()
19817     {
19818         if(!this.groupId){
19819             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19820         }
19821         
19822         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19823         
19824         if(!group){
19825             return false;
19826         }
19827         
19828         var r = false;
19829         
19830         for(var i in group){
19831             if(r){
19832                 break;
19833             }
19834             
19835             r = (group[i].getValue() == group[i].inputValue) ? true : false;
19836         }
19837         
19838         return r;
19839     },
19840     
19841     /**
19842      * Mark this field as valid
19843      */
19844     markValid : function()
19845     {
19846         if(this.allowBlank){
19847             return;
19848         }
19849         
19850         var _this = this;
19851         
19852         this.fireEvent('valid', this);
19853         
19854         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19855         
19856         if(this.groupId){
19857             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19858         }
19859         
19860         if(label){
19861             label.markValid();
19862         }
19863         
19864         if(this.inputType == 'radio'){
19865             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19866                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19867                 e.findParent('.form-group', false, true).addClass(_this.validClass);
19868             });
19869             
19870             return;
19871         }
19872         
19873         if(!this.groupId){
19874             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19875             this.el.findParent('.form-group', false, true).addClass(this.validClass);
19876             return;
19877         }
19878         
19879         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19880             
19881         if(!group){
19882             return;
19883         }
19884         
19885         for(var i in group){
19886             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19887             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
19888         }
19889     },
19890     
19891      /**
19892      * Mark this field as invalid
19893      * @param {String} msg The validation message
19894      */
19895     markInvalid : function(msg)
19896     {
19897         if(this.allowBlank){
19898             return;
19899         }
19900         
19901         var _this = this;
19902         
19903         this.fireEvent('invalid', this, msg);
19904         
19905         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19906         
19907         if(this.groupId){
19908             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19909         }
19910         
19911         if(label){
19912             label.markInvalid();
19913         }
19914             
19915         if(this.inputType == 'radio'){
19916             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19917                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19918                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
19919             });
19920             
19921             return;
19922         }
19923         
19924         if(!this.groupId){
19925             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19926             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
19927             return;
19928         }
19929         
19930         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19931         
19932         if(!group){
19933             return;
19934         }
19935         
19936         for(var i in group){
19937             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19938             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
19939         }
19940         
19941     },
19942     
19943     disable : function()
19944     {
19945         if(this.inputType != 'radio'){
19946             Roo.bootstrap.CheckBox.superclass.disable.call(this);
19947             return;
19948         }
19949         
19950         var _this = this;
19951         
19952         if(this.rendered){
19953             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19954                 _this.getActionEl().addClass(this.disabledClass);
19955                 e.dom.disabled = true;
19956             });
19957         }
19958         
19959         this.disabled = true;
19960         this.fireEvent("disable", this);
19961         return this;
19962     },
19963
19964     enable : function()
19965     {
19966         if(this.inputType != 'radio'){
19967             Roo.bootstrap.CheckBox.superclass.enable.call(this);
19968             return;
19969         }
19970         
19971         var _this = this;
19972         
19973         if(this.rendered){
19974             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19975                 _this.getActionEl().removeClass(this.disabledClass);
19976                 e.dom.disabled = false;
19977             });
19978         }
19979         
19980         this.disabled = false;
19981         this.fireEvent("enable", this);
19982         return this;
19983     }
19984
19985 });
19986
19987 Roo.apply(Roo.bootstrap.CheckBox, {
19988     
19989     groups: {},
19990     
19991      /**
19992     * register a CheckBox Group
19993     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
19994     */
19995     register : function(checkbox)
19996     {
19997         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
19998             this.groups[checkbox.groupId] = {};
19999         }
20000         
20001         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20002             return;
20003         }
20004         
20005         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20006         
20007     },
20008     /**
20009     * fetch a CheckBox Group based on the group ID
20010     * @param {string} the group ID
20011     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20012     */
20013     get: function(groupId) {
20014         if (typeof(this.groups[groupId]) == 'undefined') {
20015             return false;
20016         }
20017         
20018         return this.groups[groupId] ;
20019     }
20020     
20021     
20022 });
20023 /*
20024  * - LGPL
20025  *
20026  * Radio
20027  *
20028  *
20029  * not inline
20030  *<div class="radio">
20031   <label>
20032     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
20033     Option one is this and that&mdash;be sure to include why it's great
20034   </label>
20035 </div>
20036  *
20037  *
20038  *inline
20039  *<span>
20040  *<label class="radio-inline">fieldLabel</label>
20041  *<label class="radio-inline">
20042   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
20043 </label>
20044 <span>
20045  *
20046  *
20047  */
20048
20049 /**
20050  * @class Roo.bootstrap.Radio
20051  * @extends Roo.bootstrap.CheckBox
20052  * Bootstrap Radio class
20053
20054  * @constructor
20055  * Create a new Radio
20056  * @param {Object} config The config object
20057  */
20058
20059 Roo.bootstrap.Radio = function(config){
20060     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20061
20062 };
20063
20064 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
20065
20066     inputType: 'radio',
20067     inputValue: '',
20068     valueOff: '',
20069
20070     getAutoCreate : function()
20071     {
20072         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20073         align = align || 'left'; // default...
20074
20075
20076
20077         var id = Roo.id();
20078
20079         var cfg = {
20080                 tag : this.inline ? 'span' : 'div',
20081                 cls : 'form-group',
20082                 cn : []
20083         };
20084
20085         var inline = this.inline ? ' radio-inline' : '';
20086
20087         var lbl = {
20088                 tag: 'label' ,
20089                 // does not need for, as we wrap the input with it..
20090                 'for' : id,
20091                 cls : 'control-label box-label' + inline,
20092                 cn : []
20093         };
20094         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
20095
20096         var fieldLabel = {
20097             tag: 'label' ,
20098             //cls : 'control-label' + inline,
20099             html : this.fieldLabel,
20100             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
20101         };
20102
20103         var input =  {
20104             tag: 'input',
20105             id : id,
20106             type : this.inputType,
20107             //value : (!this.checked) ? this.valueOff : this.inputValue,
20108             value : this.inputValue,
20109             cls : 'roo-radio',
20110             placeholder : this.placeholder || '' // ?? needed????
20111
20112         };
20113         if (this.weight) { // Validity check?
20114             input.cls += " radio-" + this.weight;
20115         }
20116         if (this.disabled) {
20117             input.disabled=true;
20118         }
20119
20120         if(this.checked){
20121             input.checked = this.checked;
20122         }
20123
20124         if (this.name) {
20125             input.name = this.name;
20126         }
20127
20128         if (this.size) {
20129             input.cls += ' input-' + this.size;
20130         }
20131
20132         //?? can span's inline have a width??
20133
20134         var settings=this;
20135         ['xs','sm','md','lg'].map(function(size){
20136             if (settings[size]) {
20137                 cfg.cls += ' col-' + size + '-' + settings[size];
20138             }
20139         });
20140
20141         var inputblock = input;
20142
20143         if (this.before || this.after) {
20144
20145             inputblock = {
20146                 cls : 'input-group',
20147                 tag : 'span',
20148                 cn :  []
20149             };
20150             if (this.before) {
20151                 inputblock.cn.push({
20152                     tag :'span',
20153                     cls : 'input-group-addon',
20154                     html : this.before
20155                 });
20156             }
20157             inputblock.cn.push(input);
20158             if (this.after) {
20159                 inputblock.cn.push({
20160                     tag :'span',
20161                     cls : 'input-group-addon',
20162                     html : this.after
20163                 });
20164             }
20165
20166         };
20167
20168
20169         if (this.fieldLabel && this.fieldLabel.length) {
20170             cfg.cn.push(fieldLabel);
20171         }
20172
20173         // normal bootstrap puts the input inside the label.
20174         // however with our styled version - it has to go after the input.
20175
20176         //lbl.cn.push(inputblock);
20177
20178         var lblwrap =  {
20179             tag: 'span',
20180             cls: 'radio' + inline,
20181             cn: [
20182                 inputblock,
20183                 lbl
20184             ]
20185         };
20186
20187         cfg.cn.push( lblwrap);
20188
20189         if(this.boxLabel){
20190             lbl.cn.push({
20191                 tag: 'span',
20192                 html: this.boxLabel
20193             })
20194         }
20195
20196
20197         return cfg;
20198
20199     },
20200
20201     initEvents : function()
20202     {
20203 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20204
20205         this.inputEl().on('click', this.onClick,  this);
20206         if (this.boxLabel) {
20207             //Roo.log('find label');
20208             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
20209         }
20210
20211     },
20212
20213     inputEl: function ()
20214     {
20215         return this.el.select('input.roo-radio',true).first();
20216     },
20217     onClick : function()
20218     {
20219         Roo.log("click");
20220         this.setChecked(true);
20221     },
20222
20223     setChecked : function(state,suppressEvent)
20224     {
20225         if(state){
20226             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
20227                 v.dom.checked = false;
20228             });
20229         }
20230         Roo.log(this.inputEl().dom);
20231         this.checked = state;
20232         this.inputEl().dom.checked = state;
20233
20234         if(suppressEvent !== true){
20235             this.fireEvent('check', this, state);
20236         }
20237
20238         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
20239
20240     },
20241
20242     getGroupValue : function()
20243     {
20244         var value = '';
20245         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
20246             if(v.dom.checked == true){
20247                 value = v.dom.value;
20248             }
20249         });
20250
20251         return value;
20252     },
20253
20254     /**
20255      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
20256      * @return {Mixed} value The field value
20257      */
20258     getValue : function(){
20259         return this.getGroupValue();
20260     }
20261
20262 });
20263 //<script type="text/javascript">
20264
20265 /*
20266  * Based  Ext JS Library 1.1.1
20267  * Copyright(c) 2006-2007, Ext JS, LLC.
20268  * LGPL
20269  *
20270  */
20271  
20272 /**
20273  * @class Roo.HtmlEditorCore
20274  * @extends Roo.Component
20275  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20276  *
20277  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20278  */
20279
20280 Roo.HtmlEditorCore = function(config){
20281     
20282     
20283     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20284     
20285     
20286     this.addEvents({
20287         /**
20288          * @event initialize
20289          * Fires when the editor is fully initialized (including the iframe)
20290          * @param {Roo.HtmlEditorCore} this
20291          */
20292         initialize: true,
20293         /**
20294          * @event activate
20295          * Fires when the editor is first receives the focus. Any insertion must wait
20296          * until after this event.
20297          * @param {Roo.HtmlEditorCore} this
20298          */
20299         activate: true,
20300          /**
20301          * @event beforesync
20302          * Fires before the textarea is updated with content from the editor iframe. Return false
20303          * to cancel the sync.
20304          * @param {Roo.HtmlEditorCore} this
20305          * @param {String} html
20306          */
20307         beforesync: true,
20308          /**
20309          * @event beforepush
20310          * Fires before the iframe editor is updated with content from the textarea. Return false
20311          * to cancel the push.
20312          * @param {Roo.HtmlEditorCore} this
20313          * @param {String} html
20314          */
20315         beforepush: true,
20316          /**
20317          * @event sync
20318          * Fires when the textarea is updated with content from the editor iframe.
20319          * @param {Roo.HtmlEditorCore} this
20320          * @param {String} html
20321          */
20322         sync: true,
20323          /**
20324          * @event push
20325          * Fires when the iframe editor is updated with content from the textarea.
20326          * @param {Roo.HtmlEditorCore} this
20327          * @param {String} html
20328          */
20329         push: true,
20330         
20331         /**
20332          * @event editorevent
20333          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20334          * @param {Roo.HtmlEditorCore} this
20335          */
20336         editorevent: true
20337         
20338     });
20339     
20340     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20341     
20342     // defaults : white / black...
20343     this.applyBlacklists();
20344     
20345     
20346     
20347 };
20348
20349
20350 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20351
20352
20353      /**
20354      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20355      */
20356     
20357     owner : false,
20358     
20359      /**
20360      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20361      *                        Roo.resizable.
20362      */
20363     resizable : false,
20364      /**
20365      * @cfg {Number} height (in pixels)
20366      */   
20367     height: 300,
20368    /**
20369      * @cfg {Number} width (in pixels)
20370      */   
20371     width: 500,
20372     
20373     /**
20374      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20375      * 
20376      */
20377     stylesheets: false,
20378     
20379     // id of frame..
20380     frameId: false,
20381     
20382     // private properties
20383     validationEvent : false,
20384     deferHeight: true,
20385     initialized : false,
20386     activated : false,
20387     sourceEditMode : false,
20388     onFocus : Roo.emptyFn,
20389     iframePad:3,
20390     hideMode:'offsets',
20391     
20392     clearUp: true,
20393     
20394     // blacklist + whitelisted elements..
20395     black: false,
20396     white: false,
20397      
20398     
20399
20400     /**
20401      * Protected method that will not generally be called directly. It
20402      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20403      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20404      */
20405     getDocMarkup : function(){
20406         // body styles..
20407         var st = '';
20408         
20409         // inherit styels from page...?? 
20410         if (this.stylesheets === false) {
20411             
20412             Roo.get(document.head).select('style').each(function(node) {
20413                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20414             });
20415             
20416             Roo.get(document.head).select('link').each(function(node) { 
20417                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20418             });
20419             
20420         } else if (!this.stylesheets.length) {
20421                 // simple..
20422                 st = '<style type="text/css">' +
20423                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20424                    '</style>';
20425         } else { 
20426             
20427         }
20428         
20429         st +=  '<style type="text/css">' +
20430             'IMG { cursor: pointer } ' +
20431         '</style>';
20432
20433         
20434         return '<html><head>' + st  +
20435             //<style type="text/css">' +
20436             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20437             //'</style>' +
20438             ' </head><body class="roo-htmleditor-body"></body></html>';
20439     },
20440
20441     // private
20442     onRender : function(ct, position)
20443     {
20444         var _t = this;
20445         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20446         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20447         
20448         
20449         this.el.dom.style.border = '0 none';
20450         this.el.dom.setAttribute('tabIndex', -1);
20451         this.el.addClass('x-hidden hide');
20452         
20453         
20454         
20455         if(Roo.isIE){ // fix IE 1px bogus margin
20456             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20457         }
20458        
20459         
20460         this.frameId = Roo.id();
20461         
20462          
20463         
20464         var iframe = this.owner.wrap.createChild({
20465             tag: 'iframe',
20466             cls: 'form-control', // bootstrap..
20467             id: this.frameId,
20468             name: this.frameId,
20469             frameBorder : 'no',
20470             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20471         }, this.el
20472         );
20473         
20474         
20475         this.iframe = iframe.dom;
20476
20477          this.assignDocWin();
20478         
20479         this.doc.designMode = 'on';
20480        
20481         this.doc.open();
20482         this.doc.write(this.getDocMarkup());
20483         this.doc.close();
20484
20485         
20486         var task = { // must defer to wait for browser to be ready
20487             run : function(){
20488                 //console.log("run task?" + this.doc.readyState);
20489                 this.assignDocWin();
20490                 if(this.doc.body || this.doc.readyState == 'complete'){
20491                     try {
20492                         this.doc.designMode="on";
20493                     } catch (e) {
20494                         return;
20495                     }
20496                     Roo.TaskMgr.stop(task);
20497                     this.initEditor.defer(10, this);
20498                 }
20499             },
20500             interval : 10,
20501             duration: 10000,
20502             scope: this
20503         };
20504         Roo.TaskMgr.start(task);
20505
20506     },
20507
20508     // private
20509     onResize : function(w, h)
20510     {
20511          Roo.log('resize: ' +w + ',' + h );
20512         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20513         if(!this.iframe){
20514             return;
20515         }
20516         if(typeof w == 'number'){
20517             
20518             this.iframe.style.width = w + 'px';
20519         }
20520         if(typeof h == 'number'){
20521             
20522             this.iframe.style.height = h + 'px';
20523             if(this.doc){
20524                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20525             }
20526         }
20527         
20528     },
20529
20530     /**
20531      * Toggles the editor between standard and source edit mode.
20532      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20533      */
20534     toggleSourceEdit : function(sourceEditMode){
20535         
20536         this.sourceEditMode = sourceEditMode === true;
20537         
20538         if(this.sourceEditMode){
20539  
20540             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20541             
20542         }else{
20543             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20544             //this.iframe.className = '';
20545             this.deferFocus();
20546         }
20547         //this.setSize(this.owner.wrap.getSize());
20548         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20549     },
20550
20551     
20552   
20553
20554     /**
20555      * Protected method that will not generally be called directly. If you need/want
20556      * custom HTML cleanup, this is the method you should override.
20557      * @param {String} html The HTML to be cleaned
20558      * return {String} The cleaned HTML
20559      */
20560     cleanHtml : function(html){
20561         html = String(html);
20562         if(html.length > 5){
20563             if(Roo.isSafari){ // strip safari nonsense
20564                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20565             }
20566         }
20567         if(html == '&nbsp;'){
20568             html = '';
20569         }
20570         return html;
20571     },
20572
20573     /**
20574      * HTML Editor -> Textarea
20575      * Protected method that will not generally be called directly. Syncs the contents
20576      * of the editor iframe with the textarea.
20577      */
20578     syncValue : function(){
20579         if(this.initialized){
20580             var bd = (this.doc.body || this.doc.documentElement);
20581             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20582             var html = bd.innerHTML;
20583             if(Roo.isSafari){
20584                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20585                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20586                 if(m && m[1]){
20587                     html = '<div style="'+m[0]+'">' + html + '</div>';
20588                 }
20589             }
20590             html = this.cleanHtml(html);
20591             // fix up the special chars.. normaly like back quotes in word...
20592             // however we do not want to do this with chinese..
20593             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20594                 var cc = b.charCodeAt();
20595                 if (
20596                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20597                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20598                     (cc >= 0xf900 && cc < 0xfb00 )
20599                 ) {
20600                         return b;
20601                 }
20602                 return "&#"+cc+";" 
20603             });
20604             if(this.owner.fireEvent('beforesync', this, html) !== false){
20605                 this.el.dom.value = html;
20606                 this.owner.fireEvent('sync', this, html);
20607             }
20608         }
20609     },
20610
20611     /**
20612      * Protected method that will not generally be called directly. Pushes the value of the textarea
20613      * into the iframe editor.
20614      */
20615     pushValue : function(){
20616         if(this.initialized){
20617             var v = this.el.dom.value.trim();
20618             
20619 //            if(v.length < 1){
20620 //                v = '&#160;';
20621 //            }
20622             
20623             if(this.owner.fireEvent('beforepush', this, v) !== false){
20624                 var d = (this.doc.body || this.doc.documentElement);
20625                 d.innerHTML = v;
20626                 this.cleanUpPaste();
20627                 this.el.dom.value = d.innerHTML;
20628                 this.owner.fireEvent('push', this, v);
20629             }
20630         }
20631     },
20632
20633     // private
20634     deferFocus : function(){
20635         this.focus.defer(10, this);
20636     },
20637
20638     // doc'ed in Field
20639     focus : function(){
20640         if(this.win && !this.sourceEditMode){
20641             this.win.focus();
20642         }else{
20643             this.el.focus();
20644         }
20645     },
20646     
20647     assignDocWin: function()
20648     {
20649         var iframe = this.iframe;
20650         
20651          if(Roo.isIE){
20652             this.doc = iframe.contentWindow.document;
20653             this.win = iframe.contentWindow;
20654         } else {
20655 //            if (!Roo.get(this.frameId)) {
20656 //                return;
20657 //            }
20658 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20659 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20660             
20661             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20662                 return;
20663             }
20664             
20665             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20666             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20667         }
20668     },
20669     
20670     // private
20671     initEditor : function(){
20672         //console.log("INIT EDITOR");
20673         this.assignDocWin();
20674         
20675         
20676         
20677         this.doc.designMode="on";
20678         this.doc.open();
20679         this.doc.write(this.getDocMarkup());
20680         this.doc.close();
20681         
20682         var dbody = (this.doc.body || this.doc.documentElement);
20683         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20684         // this copies styles from the containing element into thsi one..
20685         // not sure why we need all of this..
20686         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20687         
20688         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20689         //ss['background-attachment'] = 'fixed'; // w3c
20690         dbody.bgProperties = 'fixed'; // ie
20691         //Roo.DomHelper.applyStyles(dbody, ss);
20692         Roo.EventManager.on(this.doc, {
20693             //'mousedown': this.onEditorEvent,
20694             'mouseup': this.onEditorEvent,
20695             'dblclick': this.onEditorEvent,
20696             'click': this.onEditorEvent,
20697             'keyup': this.onEditorEvent,
20698             buffer:100,
20699             scope: this
20700         });
20701         if(Roo.isGecko){
20702             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20703         }
20704         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20705             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20706         }
20707         this.initialized = true;
20708
20709         this.owner.fireEvent('initialize', this);
20710         this.pushValue();
20711     },
20712
20713     // private
20714     onDestroy : function(){
20715         
20716         
20717         
20718         if(this.rendered){
20719             
20720             //for (var i =0; i < this.toolbars.length;i++) {
20721             //    // fixme - ask toolbars for heights?
20722             //    this.toolbars[i].onDestroy();
20723            // }
20724             
20725             //this.wrap.dom.innerHTML = '';
20726             //this.wrap.remove();
20727         }
20728     },
20729
20730     // private
20731     onFirstFocus : function(){
20732         
20733         this.assignDocWin();
20734         
20735         
20736         this.activated = true;
20737          
20738     
20739         if(Roo.isGecko){ // prevent silly gecko errors
20740             this.win.focus();
20741             var s = this.win.getSelection();
20742             if(!s.focusNode || s.focusNode.nodeType != 3){
20743                 var r = s.getRangeAt(0);
20744                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20745                 r.collapse(true);
20746                 this.deferFocus();
20747             }
20748             try{
20749                 this.execCmd('useCSS', true);
20750                 this.execCmd('styleWithCSS', false);
20751             }catch(e){}
20752         }
20753         this.owner.fireEvent('activate', this);
20754     },
20755
20756     // private
20757     adjustFont: function(btn){
20758         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20759         //if(Roo.isSafari){ // safari
20760         //    adjust *= 2;
20761        // }
20762         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20763         if(Roo.isSafari){ // safari
20764             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20765             v =  (v < 10) ? 10 : v;
20766             v =  (v > 48) ? 48 : v;
20767             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20768             
20769         }
20770         
20771         
20772         v = Math.max(1, v+adjust);
20773         
20774         this.execCmd('FontSize', v  );
20775     },
20776
20777     onEditorEvent : function(e)
20778     {
20779         this.owner.fireEvent('editorevent', this, e);
20780       //  this.updateToolbar();
20781         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20782     },
20783
20784     insertTag : function(tg)
20785     {
20786         // could be a bit smarter... -> wrap the current selected tRoo..
20787         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20788             
20789             range = this.createRange(this.getSelection());
20790             var wrappingNode = this.doc.createElement(tg.toLowerCase());
20791             wrappingNode.appendChild(range.extractContents());
20792             range.insertNode(wrappingNode);
20793
20794             return;
20795             
20796             
20797             
20798         }
20799         this.execCmd("formatblock",   tg);
20800         
20801     },
20802     
20803     insertText : function(txt)
20804     {
20805         
20806         
20807         var range = this.createRange();
20808         range.deleteContents();
20809                //alert(Sender.getAttribute('label'));
20810                
20811         range.insertNode(this.doc.createTextNode(txt));
20812     } ,
20813     
20814      
20815
20816     /**
20817      * Executes a Midas editor command on the editor document and performs necessary focus and
20818      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20819      * @param {String} cmd The Midas command
20820      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20821      */
20822     relayCmd : function(cmd, value){
20823         this.win.focus();
20824         this.execCmd(cmd, value);
20825         this.owner.fireEvent('editorevent', this);
20826         //this.updateToolbar();
20827         this.owner.deferFocus();
20828     },
20829
20830     /**
20831      * Executes a Midas editor command directly on the editor document.
20832      * For visual commands, you should use {@link #relayCmd} instead.
20833      * <b>This should only be called after the editor is initialized.</b>
20834      * @param {String} cmd The Midas command
20835      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20836      */
20837     execCmd : function(cmd, value){
20838         this.doc.execCommand(cmd, false, value === undefined ? null : value);
20839         this.syncValue();
20840     },
20841  
20842  
20843    
20844     /**
20845      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20846      * to insert tRoo.
20847      * @param {String} text | dom node.. 
20848      */
20849     insertAtCursor : function(text)
20850     {
20851         
20852         
20853         
20854         if(!this.activated){
20855             return;
20856         }
20857         /*
20858         if(Roo.isIE){
20859             this.win.focus();
20860             var r = this.doc.selection.createRange();
20861             if(r){
20862                 r.collapse(true);
20863                 r.pasteHTML(text);
20864                 this.syncValue();
20865                 this.deferFocus();
20866             
20867             }
20868             return;
20869         }
20870         */
20871         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20872             this.win.focus();
20873             
20874             
20875             // from jquery ui (MIT licenced)
20876             var range, node;
20877             var win = this.win;
20878             
20879             if (win.getSelection && win.getSelection().getRangeAt) {
20880                 range = win.getSelection().getRangeAt(0);
20881                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20882                 range.insertNode(node);
20883             } else if (win.document.selection && win.document.selection.createRange) {
20884                 // no firefox support
20885                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20886                 win.document.selection.createRange().pasteHTML(txt);
20887             } else {
20888                 // no firefox support
20889                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20890                 this.execCmd('InsertHTML', txt);
20891             } 
20892             
20893             this.syncValue();
20894             
20895             this.deferFocus();
20896         }
20897     },
20898  // private
20899     mozKeyPress : function(e){
20900         if(e.ctrlKey){
20901             var c = e.getCharCode(), cmd;
20902           
20903             if(c > 0){
20904                 c = String.fromCharCode(c).toLowerCase();
20905                 switch(c){
20906                     case 'b':
20907                         cmd = 'bold';
20908                         break;
20909                     case 'i':
20910                         cmd = 'italic';
20911                         break;
20912                     
20913                     case 'u':
20914                         cmd = 'underline';
20915                         break;
20916                     
20917                     case 'v':
20918                         this.cleanUpPaste.defer(100, this);
20919                         return;
20920                         
20921                 }
20922                 if(cmd){
20923                     this.win.focus();
20924                     this.execCmd(cmd);
20925                     this.deferFocus();
20926                     e.preventDefault();
20927                 }
20928                 
20929             }
20930         }
20931     },
20932
20933     // private
20934     fixKeys : function(){ // load time branching for fastest keydown performance
20935         if(Roo.isIE){
20936             return function(e){
20937                 var k = e.getKey(), r;
20938                 if(k == e.TAB){
20939                     e.stopEvent();
20940                     r = this.doc.selection.createRange();
20941                     if(r){
20942                         r.collapse(true);
20943                         r.pasteHTML('&#160;&#160;&#160;&#160;');
20944                         this.deferFocus();
20945                     }
20946                     return;
20947                 }
20948                 
20949                 if(k == e.ENTER){
20950                     r = this.doc.selection.createRange();
20951                     if(r){
20952                         var target = r.parentElement();
20953                         if(!target || target.tagName.toLowerCase() != 'li'){
20954                             e.stopEvent();
20955                             r.pasteHTML('<br />');
20956                             r.collapse(false);
20957                             r.select();
20958                         }
20959                     }
20960                 }
20961                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20962                     this.cleanUpPaste.defer(100, this);
20963                     return;
20964                 }
20965                 
20966                 
20967             };
20968         }else if(Roo.isOpera){
20969             return function(e){
20970                 var k = e.getKey();
20971                 if(k == e.TAB){
20972                     e.stopEvent();
20973                     this.win.focus();
20974                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
20975                     this.deferFocus();
20976                 }
20977                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20978                     this.cleanUpPaste.defer(100, this);
20979                     return;
20980                 }
20981                 
20982             };
20983         }else if(Roo.isSafari){
20984             return function(e){
20985                 var k = e.getKey();
20986                 
20987                 if(k == e.TAB){
20988                     e.stopEvent();
20989                     this.execCmd('InsertText','\t');
20990                     this.deferFocus();
20991                     return;
20992                 }
20993                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20994                     this.cleanUpPaste.defer(100, this);
20995                     return;
20996                 }
20997                 
20998              };
20999         }
21000     }(),
21001     
21002     getAllAncestors: function()
21003     {
21004         var p = this.getSelectedNode();
21005         var a = [];
21006         if (!p) {
21007             a.push(p); // push blank onto stack..
21008             p = this.getParentElement();
21009         }
21010         
21011         
21012         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21013             a.push(p);
21014             p = p.parentNode;
21015         }
21016         a.push(this.doc.body);
21017         return a;
21018     },
21019     lastSel : false,
21020     lastSelNode : false,
21021     
21022     
21023     getSelection : function() 
21024     {
21025         this.assignDocWin();
21026         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21027     },
21028     
21029     getSelectedNode: function() 
21030     {
21031         // this may only work on Gecko!!!
21032         
21033         // should we cache this!!!!
21034         
21035         
21036         
21037          
21038         var range = this.createRange(this.getSelection()).cloneRange();
21039         
21040         if (Roo.isIE) {
21041             var parent = range.parentElement();
21042             while (true) {
21043                 var testRange = range.duplicate();
21044                 testRange.moveToElementText(parent);
21045                 if (testRange.inRange(range)) {
21046                     break;
21047                 }
21048                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21049                     break;
21050                 }
21051                 parent = parent.parentElement;
21052             }
21053             return parent;
21054         }
21055         
21056         // is ancestor a text element.
21057         var ac =  range.commonAncestorContainer;
21058         if (ac.nodeType == 3) {
21059             ac = ac.parentNode;
21060         }
21061         
21062         var ar = ac.childNodes;
21063          
21064         var nodes = [];
21065         var other_nodes = [];
21066         var has_other_nodes = false;
21067         for (var i=0;i<ar.length;i++) {
21068             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21069                 continue;
21070             }
21071             // fullly contained node.
21072             
21073             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21074                 nodes.push(ar[i]);
21075                 continue;
21076             }
21077             
21078             // probably selected..
21079             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21080                 other_nodes.push(ar[i]);
21081                 continue;
21082             }
21083             // outer..
21084             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21085                 continue;
21086             }
21087             
21088             
21089             has_other_nodes = true;
21090         }
21091         if (!nodes.length && other_nodes.length) {
21092             nodes= other_nodes;
21093         }
21094         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21095             return false;
21096         }
21097         
21098         return nodes[0];
21099     },
21100     createRange: function(sel)
21101     {
21102         // this has strange effects when using with 
21103         // top toolbar - not sure if it's a great idea.
21104         //this.editor.contentWindow.focus();
21105         if (typeof sel != "undefined") {
21106             try {
21107                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21108             } catch(e) {
21109                 return this.doc.createRange();
21110             }
21111         } else {
21112             return this.doc.createRange();
21113         }
21114     },
21115     getParentElement: function()
21116     {
21117         
21118         this.assignDocWin();
21119         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21120         
21121         var range = this.createRange(sel);
21122          
21123         try {
21124             var p = range.commonAncestorContainer;
21125             while (p.nodeType == 3) { // text node
21126                 p = p.parentNode;
21127             }
21128             return p;
21129         } catch (e) {
21130             return null;
21131         }
21132     
21133     },
21134     /***
21135      *
21136      * Range intersection.. the hard stuff...
21137      *  '-1' = before
21138      *  '0' = hits..
21139      *  '1' = after.
21140      *         [ -- selected range --- ]
21141      *   [fail]                        [fail]
21142      *
21143      *    basically..
21144      *      if end is before start or  hits it. fail.
21145      *      if start is after end or hits it fail.
21146      *
21147      *   if either hits (but other is outside. - then it's not 
21148      *   
21149      *    
21150      **/
21151     
21152     
21153     // @see http://www.thismuchiknow.co.uk/?p=64.
21154     rangeIntersectsNode : function(range, node)
21155     {
21156         var nodeRange = node.ownerDocument.createRange();
21157         try {
21158             nodeRange.selectNode(node);
21159         } catch (e) {
21160             nodeRange.selectNodeContents(node);
21161         }
21162     
21163         var rangeStartRange = range.cloneRange();
21164         rangeStartRange.collapse(true);
21165     
21166         var rangeEndRange = range.cloneRange();
21167         rangeEndRange.collapse(false);
21168     
21169         var nodeStartRange = nodeRange.cloneRange();
21170         nodeStartRange.collapse(true);
21171     
21172         var nodeEndRange = nodeRange.cloneRange();
21173         nodeEndRange.collapse(false);
21174     
21175         return rangeStartRange.compareBoundaryPoints(
21176                  Range.START_TO_START, nodeEndRange) == -1 &&
21177                rangeEndRange.compareBoundaryPoints(
21178                  Range.START_TO_START, nodeStartRange) == 1;
21179         
21180          
21181     },
21182     rangeCompareNode : function(range, node)
21183     {
21184         var nodeRange = node.ownerDocument.createRange();
21185         try {
21186             nodeRange.selectNode(node);
21187         } catch (e) {
21188             nodeRange.selectNodeContents(node);
21189         }
21190         
21191         
21192         range.collapse(true);
21193     
21194         nodeRange.collapse(true);
21195      
21196         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21197         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21198          
21199         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21200         
21201         var nodeIsBefore   =  ss == 1;
21202         var nodeIsAfter    = ee == -1;
21203         
21204         if (nodeIsBefore && nodeIsAfter) {
21205             return 0; // outer
21206         }
21207         if (!nodeIsBefore && nodeIsAfter) {
21208             return 1; //right trailed.
21209         }
21210         
21211         if (nodeIsBefore && !nodeIsAfter) {
21212             return 2;  // left trailed.
21213         }
21214         // fully contined.
21215         return 3;
21216     },
21217
21218     // private? - in a new class?
21219     cleanUpPaste :  function()
21220     {
21221         // cleans up the whole document..
21222         Roo.log('cleanuppaste');
21223         
21224         this.cleanUpChildren(this.doc.body);
21225         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21226         if (clean != this.doc.body.innerHTML) {
21227             this.doc.body.innerHTML = clean;
21228         }
21229         
21230     },
21231     
21232     cleanWordChars : function(input) {// change the chars to hex code
21233         var he = Roo.HtmlEditorCore;
21234         
21235         var output = input;
21236         Roo.each(he.swapCodes, function(sw) { 
21237             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21238             
21239             output = output.replace(swapper, sw[1]);
21240         });
21241         
21242         return output;
21243     },
21244     
21245     
21246     cleanUpChildren : function (n)
21247     {
21248         if (!n.childNodes.length) {
21249             return;
21250         }
21251         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21252            this.cleanUpChild(n.childNodes[i]);
21253         }
21254     },
21255     
21256     
21257         
21258     
21259     cleanUpChild : function (node)
21260     {
21261         var ed = this;
21262         //console.log(node);
21263         if (node.nodeName == "#text") {
21264             // clean up silly Windows -- stuff?
21265             return; 
21266         }
21267         if (node.nodeName == "#comment") {
21268             node.parentNode.removeChild(node);
21269             // clean up silly Windows -- stuff?
21270             return; 
21271         }
21272         var lcname = node.tagName.toLowerCase();
21273         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21274         // whitelist of tags..
21275         
21276         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21277             // remove node.
21278             node.parentNode.removeChild(node);
21279             return;
21280             
21281         }
21282         
21283         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21284         
21285         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21286         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21287         
21288         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21289         //    remove_keep_children = true;
21290         //}
21291         
21292         if (remove_keep_children) {
21293             this.cleanUpChildren(node);
21294             // inserts everything just before this node...
21295             while (node.childNodes.length) {
21296                 var cn = node.childNodes[0];
21297                 node.removeChild(cn);
21298                 node.parentNode.insertBefore(cn, node);
21299             }
21300             node.parentNode.removeChild(node);
21301             return;
21302         }
21303         
21304         if (!node.attributes || !node.attributes.length) {
21305             this.cleanUpChildren(node);
21306             return;
21307         }
21308         
21309         function cleanAttr(n,v)
21310         {
21311             
21312             if (v.match(/^\./) || v.match(/^\//)) {
21313                 return;
21314             }
21315             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
21316                 return;
21317             }
21318             if (v.match(/^#/)) {
21319                 return;
21320             }
21321 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21322             node.removeAttribute(n);
21323             
21324         }
21325         
21326         var cwhite = this.cwhite;
21327         var cblack = this.cblack;
21328             
21329         function cleanStyle(n,v)
21330         {
21331             if (v.match(/expression/)) { //XSS?? should we even bother..
21332                 node.removeAttribute(n);
21333                 return;
21334             }
21335             
21336             var parts = v.split(/;/);
21337             var clean = [];
21338             
21339             Roo.each(parts, function(p) {
21340                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21341                 if (!p.length) {
21342                     return true;
21343                 }
21344                 var l = p.split(':').shift().replace(/\s+/g,'');
21345                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21346                 
21347                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21348 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21349                     //node.removeAttribute(n);
21350                     return true;
21351                 }
21352                 //Roo.log()
21353                 // only allow 'c whitelisted system attributes'
21354                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21355 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21356                     //node.removeAttribute(n);
21357                     return true;
21358                 }
21359                 
21360                 
21361                  
21362                 
21363                 clean.push(p);
21364                 return true;
21365             });
21366             if (clean.length) { 
21367                 node.setAttribute(n, clean.join(';'));
21368             } else {
21369                 node.removeAttribute(n);
21370             }
21371             
21372         }
21373         
21374         
21375         for (var i = node.attributes.length-1; i > -1 ; i--) {
21376             var a = node.attributes[i];
21377             //console.log(a);
21378             
21379             if (a.name.toLowerCase().substr(0,2)=='on')  {
21380                 node.removeAttribute(a.name);
21381                 continue;
21382             }
21383             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21384                 node.removeAttribute(a.name);
21385                 continue;
21386             }
21387             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21388                 cleanAttr(a.name,a.value); // fixme..
21389                 continue;
21390             }
21391             if (a.name == 'style') {
21392                 cleanStyle(a.name,a.value);
21393                 continue;
21394             }
21395             /// clean up MS crap..
21396             // tecnically this should be a list of valid class'es..
21397             
21398             
21399             if (a.name == 'class') {
21400                 if (a.value.match(/^Mso/)) {
21401                     node.className = '';
21402                 }
21403                 
21404                 if (a.value.match(/body/)) {
21405                     node.className = '';
21406                 }
21407                 continue;
21408             }
21409             
21410             // style cleanup!?
21411             // class cleanup?
21412             
21413         }
21414         
21415         
21416         this.cleanUpChildren(node);
21417         
21418         
21419     },
21420     
21421     /**
21422      * Clean up MS wordisms...
21423      */
21424     cleanWord : function(node)
21425     {
21426         
21427         
21428         if (!node) {
21429             this.cleanWord(this.doc.body);
21430             return;
21431         }
21432         if (node.nodeName == "#text") {
21433             // clean up silly Windows -- stuff?
21434             return; 
21435         }
21436         if (node.nodeName == "#comment") {
21437             node.parentNode.removeChild(node);
21438             // clean up silly Windows -- stuff?
21439             return; 
21440         }
21441         
21442         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21443             node.parentNode.removeChild(node);
21444             return;
21445         }
21446         
21447         // remove - but keep children..
21448         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21449             while (node.childNodes.length) {
21450                 var cn = node.childNodes[0];
21451                 node.removeChild(cn);
21452                 node.parentNode.insertBefore(cn, node);
21453             }
21454             node.parentNode.removeChild(node);
21455             this.iterateChildren(node, this.cleanWord);
21456             return;
21457         }
21458         // clean styles
21459         if (node.className.length) {
21460             
21461             var cn = node.className.split(/\W+/);
21462             var cna = [];
21463             Roo.each(cn, function(cls) {
21464                 if (cls.match(/Mso[a-zA-Z]+/)) {
21465                     return;
21466                 }
21467                 cna.push(cls);
21468             });
21469             node.className = cna.length ? cna.join(' ') : '';
21470             if (!cna.length) {
21471                 node.removeAttribute("class");
21472             }
21473         }
21474         
21475         if (node.hasAttribute("lang")) {
21476             node.removeAttribute("lang");
21477         }
21478         
21479         if (node.hasAttribute("style")) {
21480             
21481             var styles = node.getAttribute("style").split(";");
21482             var nstyle = [];
21483             Roo.each(styles, function(s) {
21484                 if (!s.match(/:/)) {
21485                     return;
21486                 }
21487                 var kv = s.split(":");
21488                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21489                     return;
21490                 }
21491                 // what ever is left... we allow.
21492                 nstyle.push(s);
21493             });
21494             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21495             if (!nstyle.length) {
21496                 node.removeAttribute('style');
21497             }
21498         }
21499         this.iterateChildren(node, this.cleanWord);
21500         
21501         
21502         
21503     },
21504     /**
21505      * iterateChildren of a Node, calling fn each time, using this as the scole..
21506      * @param {DomNode} node node to iterate children of.
21507      * @param {Function} fn method of this class to call on each item.
21508      */
21509     iterateChildren : function(node, fn)
21510     {
21511         if (!node.childNodes.length) {
21512                 return;
21513         }
21514         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21515            fn.call(this, node.childNodes[i])
21516         }
21517     },
21518     
21519     
21520     /**
21521      * cleanTableWidths.
21522      *
21523      * Quite often pasting from word etc.. results in tables with column and widths.
21524      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21525      *
21526      */
21527     cleanTableWidths : function(node)
21528     {
21529          
21530          
21531         if (!node) {
21532             this.cleanTableWidths(this.doc.body);
21533             return;
21534         }
21535         
21536         // ignore list...
21537         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21538             return; 
21539         }
21540         Roo.log(node.tagName);
21541         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21542             this.iterateChildren(node, this.cleanTableWidths);
21543             return;
21544         }
21545         if (node.hasAttribute('width')) {
21546             node.removeAttribute('width');
21547         }
21548         
21549          
21550         if (node.hasAttribute("style")) {
21551             // pretty basic...
21552             
21553             var styles = node.getAttribute("style").split(";");
21554             var nstyle = [];
21555             Roo.each(styles, function(s) {
21556                 if (!s.match(/:/)) {
21557                     return;
21558                 }
21559                 var kv = s.split(":");
21560                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21561                     return;
21562                 }
21563                 // what ever is left... we allow.
21564                 nstyle.push(s);
21565             });
21566             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21567             if (!nstyle.length) {
21568                 node.removeAttribute('style');
21569             }
21570         }
21571         
21572         this.iterateChildren(node, this.cleanTableWidths);
21573         
21574         
21575     },
21576     
21577     
21578     
21579     
21580     domToHTML : function(currentElement, depth, nopadtext) {
21581         
21582         depth = depth || 0;
21583         nopadtext = nopadtext || false;
21584     
21585         if (!currentElement) {
21586             return this.domToHTML(this.doc.body);
21587         }
21588         
21589         //Roo.log(currentElement);
21590         var j;
21591         var allText = false;
21592         var nodeName = currentElement.nodeName;
21593         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21594         
21595         if  (nodeName == '#text') {
21596             
21597             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21598         }
21599         
21600         
21601         var ret = '';
21602         if (nodeName != 'BODY') {
21603              
21604             var i = 0;
21605             // Prints the node tagName, such as <A>, <IMG>, etc
21606             if (tagName) {
21607                 var attr = [];
21608                 for(i = 0; i < currentElement.attributes.length;i++) {
21609                     // quoting?
21610                     var aname = currentElement.attributes.item(i).name;
21611                     if (!currentElement.attributes.item(i).value.length) {
21612                         continue;
21613                     }
21614                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21615                 }
21616                 
21617                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21618             } 
21619             else {
21620                 
21621                 // eack
21622             }
21623         } else {
21624             tagName = false;
21625         }
21626         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21627             return ret;
21628         }
21629         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21630             nopadtext = true;
21631         }
21632         
21633         
21634         // Traverse the tree
21635         i = 0;
21636         var currentElementChild = currentElement.childNodes.item(i);
21637         var allText = true;
21638         var innerHTML  = '';
21639         lastnode = '';
21640         while (currentElementChild) {
21641             // Formatting code (indent the tree so it looks nice on the screen)
21642             var nopad = nopadtext;
21643             if (lastnode == 'SPAN') {
21644                 nopad  = true;
21645             }
21646             // text
21647             if  (currentElementChild.nodeName == '#text') {
21648                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21649                 toadd = nopadtext ? toadd : toadd.trim();
21650                 if (!nopad && toadd.length > 80) {
21651                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21652                 }
21653                 innerHTML  += toadd;
21654                 
21655                 i++;
21656                 currentElementChild = currentElement.childNodes.item(i);
21657                 lastNode = '';
21658                 continue;
21659             }
21660             allText = false;
21661             
21662             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21663                 
21664             // Recursively traverse the tree structure of the child node
21665             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21666             lastnode = currentElementChild.nodeName;
21667             i++;
21668             currentElementChild=currentElement.childNodes.item(i);
21669         }
21670         
21671         ret += innerHTML;
21672         
21673         if (!allText) {
21674                 // The remaining code is mostly for formatting the tree
21675             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21676         }
21677         
21678         
21679         if (tagName) {
21680             ret+= "</"+tagName+">";
21681         }
21682         return ret;
21683         
21684     },
21685         
21686     applyBlacklists : function()
21687     {
21688         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
21689         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
21690         
21691         this.white = [];
21692         this.black = [];
21693         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21694             if (b.indexOf(tag) > -1) {
21695                 return;
21696             }
21697             this.white.push(tag);
21698             
21699         }, this);
21700         
21701         Roo.each(w, function(tag) {
21702             if (b.indexOf(tag) > -1) {
21703                 return;
21704             }
21705             if (this.white.indexOf(tag) > -1) {
21706                 return;
21707             }
21708             this.white.push(tag);
21709             
21710         }, this);
21711         
21712         
21713         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21714             if (w.indexOf(tag) > -1) {
21715                 return;
21716             }
21717             this.black.push(tag);
21718             
21719         }, this);
21720         
21721         Roo.each(b, function(tag) {
21722             if (w.indexOf(tag) > -1) {
21723                 return;
21724             }
21725             if (this.black.indexOf(tag) > -1) {
21726                 return;
21727             }
21728             this.black.push(tag);
21729             
21730         }, this);
21731         
21732         
21733         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
21734         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
21735         
21736         this.cwhite = [];
21737         this.cblack = [];
21738         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21739             if (b.indexOf(tag) > -1) {
21740                 return;
21741             }
21742             this.cwhite.push(tag);
21743             
21744         }, this);
21745         
21746         Roo.each(w, function(tag) {
21747             if (b.indexOf(tag) > -1) {
21748                 return;
21749             }
21750             if (this.cwhite.indexOf(tag) > -1) {
21751                 return;
21752             }
21753             this.cwhite.push(tag);
21754             
21755         }, this);
21756         
21757         
21758         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21759             if (w.indexOf(tag) > -1) {
21760                 return;
21761             }
21762             this.cblack.push(tag);
21763             
21764         }, this);
21765         
21766         Roo.each(b, function(tag) {
21767             if (w.indexOf(tag) > -1) {
21768                 return;
21769             }
21770             if (this.cblack.indexOf(tag) > -1) {
21771                 return;
21772             }
21773             this.cblack.push(tag);
21774             
21775         }, this);
21776     },
21777     
21778     setStylesheets : function(stylesheets)
21779     {
21780         if(typeof(stylesheets) == 'string'){
21781             Roo.get(this.iframe.contentDocument.head).createChild({
21782                 tag : 'link',
21783                 rel : 'stylesheet',
21784                 type : 'text/css',
21785                 href : stylesheets
21786             });
21787             
21788             return;
21789         }
21790         var _this = this;
21791      
21792         Roo.each(stylesheets, function(s) {
21793             if(!s.length){
21794                 return;
21795             }
21796             
21797             Roo.get(_this.iframe.contentDocument.head).createChild({
21798                 tag : 'link',
21799                 rel : 'stylesheet',
21800                 type : 'text/css',
21801                 href : s
21802             });
21803         });
21804
21805         
21806     },
21807     
21808     removeStylesheets : function()
21809     {
21810         var _this = this;
21811         
21812         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21813             s.remove();
21814         });
21815     }
21816     
21817     // hide stuff that is not compatible
21818     /**
21819      * @event blur
21820      * @hide
21821      */
21822     /**
21823      * @event change
21824      * @hide
21825      */
21826     /**
21827      * @event focus
21828      * @hide
21829      */
21830     /**
21831      * @event specialkey
21832      * @hide
21833      */
21834     /**
21835      * @cfg {String} fieldClass @hide
21836      */
21837     /**
21838      * @cfg {String} focusClass @hide
21839      */
21840     /**
21841      * @cfg {String} autoCreate @hide
21842      */
21843     /**
21844      * @cfg {String} inputType @hide
21845      */
21846     /**
21847      * @cfg {String} invalidClass @hide
21848      */
21849     /**
21850      * @cfg {String} invalidText @hide
21851      */
21852     /**
21853      * @cfg {String} msgFx @hide
21854      */
21855     /**
21856      * @cfg {String} validateOnBlur @hide
21857      */
21858 });
21859
21860 Roo.HtmlEditorCore.white = [
21861         'area', 'br', 'img', 'input', 'hr', 'wbr',
21862         
21863        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
21864        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
21865        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
21866        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
21867        'table',   'ul',         'xmp', 
21868        
21869        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
21870       'thead',   'tr', 
21871      
21872       'dir', 'menu', 'ol', 'ul', 'dl',
21873        
21874       'embed',  'object'
21875 ];
21876
21877
21878 Roo.HtmlEditorCore.black = [
21879     //    'embed',  'object', // enable - backend responsiblity to clean thiese
21880         'applet', // 
21881         'base',   'basefont', 'bgsound', 'blink',  'body', 
21882         'frame',  'frameset', 'head',    'html',   'ilayer', 
21883         'iframe', 'layer',  'link',     'meta',    'object',   
21884         'script', 'style' ,'title',  'xml' // clean later..
21885 ];
21886 Roo.HtmlEditorCore.clean = [
21887     'script', 'style', 'title', 'xml'
21888 ];
21889 Roo.HtmlEditorCore.remove = [
21890     'font'
21891 ];
21892 // attributes..
21893
21894 Roo.HtmlEditorCore.ablack = [
21895     'on'
21896 ];
21897     
21898 Roo.HtmlEditorCore.aclean = [ 
21899     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
21900 ];
21901
21902 // protocols..
21903 Roo.HtmlEditorCore.pwhite= [
21904         'http',  'https',  'mailto'
21905 ];
21906
21907 // white listed style attributes.
21908 Roo.HtmlEditorCore.cwhite= [
21909       //  'text-align', /// default is to allow most things..
21910       
21911          
21912 //        'font-size'//??
21913 ];
21914
21915 // black listed style attributes.
21916 Roo.HtmlEditorCore.cblack= [
21917       //  'font-size' -- this can be set by the project 
21918 ];
21919
21920
21921 Roo.HtmlEditorCore.swapCodes   =[ 
21922     [    8211, "--" ], 
21923     [    8212, "--" ], 
21924     [    8216,  "'" ],  
21925     [    8217, "'" ],  
21926     [    8220, '"' ],  
21927     [    8221, '"' ],  
21928     [    8226, "*" ],  
21929     [    8230, "..." ]
21930 ]; 
21931
21932     /*
21933  * - LGPL
21934  *
21935  * HtmlEditor
21936  * 
21937  */
21938
21939 /**
21940  * @class Roo.bootstrap.HtmlEditor
21941  * @extends Roo.bootstrap.TextArea
21942  * Bootstrap HtmlEditor class
21943
21944  * @constructor
21945  * Create a new HtmlEditor
21946  * @param {Object} config The config object
21947  */
21948
21949 Roo.bootstrap.HtmlEditor = function(config){
21950     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
21951     if (!this.toolbars) {
21952         this.toolbars = [];
21953     }
21954     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
21955     this.addEvents({
21956             /**
21957              * @event initialize
21958              * Fires when the editor is fully initialized (including the iframe)
21959              * @param {HtmlEditor} this
21960              */
21961             initialize: true,
21962             /**
21963              * @event activate
21964              * Fires when the editor is first receives the focus. Any insertion must wait
21965              * until after this event.
21966              * @param {HtmlEditor} this
21967              */
21968             activate: true,
21969              /**
21970              * @event beforesync
21971              * Fires before the textarea is updated with content from the editor iframe. Return false
21972              * to cancel the sync.
21973              * @param {HtmlEditor} this
21974              * @param {String} html
21975              */
21976             beforesync: true,
21977              /**
21978              * @event beforepush
21979              * Fires before the iframe editor is updated with content from the textarea. Return false
21980              * to cancel the push.
21981              * @param {HtmlEditor} this
21982              * @param {String} html
21983              */
21984             beforepush: true,
21985              /**
21986              * @event sync
21987              * Fires when the textarea is updated with content from the editor iframe.
21988              * @param {HtmlEditor} this
21989              * @param {String} html
21990              */
21991             sync: true,
21992              /**
21993              * @event push
21994              * Fires when the iframe editor is updated with content from the textarea.
21995              * @param {HtmlEditor} this
21996              * @param {String} html
21997              */
21998             push: true,
21999              /**
22000              * @event editmodechange
22001              * Fires when the editor switches edit modes
22002              * @param {HtmlEditor} this
22003              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22004              */
22005             editmodechange: true,
22006             /**
22007              * @event editorevent
22008              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22009              * @param {HtmlEditor} this
22010              */
22011             editorevent: true,
22012             /**
22013              * @event firstfocus
22014              * Fires when on first focus - needed by toolbars..
22015              * @param {HtmlEditor} this
22016              */
22017             firstfocus: true,
22018             /**
22019              * @event autosave
22020              * Auto save the htmlEditor value as a file into Events
22021              * @param {HtmlEditor} this
22022              */
22023             autosave: true,
22024             /**
22025              * @event savedpreview
22026              * preview the saved version of htmlEditor
22027              * @param {HtmlEditor} this
22028              */
22029             savedpreview: true
22030         });
22031 };
22032
22033
22034 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
22035     
22036     
22037       /**
22038      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22039      */
22040     toolbars : false,
22041    
22042      /**
22043      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22044      *                        Roo.resizable.
22045      */
22046     resizable : false,
22047      /**
22048      * @cfg {Number} height (in pixels)
22049      */   
22050     height: 300,
22051    /**
22052      * @cfg {Number} width (in pixels)
22053      */   
22054     width: false,
22055     
22056     /**
22057      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22058      * 
22059      */
22060     stylesheets: false,
22061     
22062     // id of frame..
22063     frameId: false,
22064     
22065     // private properties
22066     validationEvent : false,
22067     deferHeight: true,
22068     initialized : false,
22069     activated : false,
22070     
22071     onFocus : Roo.emptyFn,
22072     iframePad:3,
22073     hideMode:'offsets',
22074     
22075     
22076     tbContainer : false,
22077     
22078     toolbarContainer :function() {
22079         return this.wrap.select('.x-html-editor-tb',true).first();
22080     },
22081
22082     /**
22083      * Protected method that will not generally be called directly. It
22084      * is called when the editor creates its toolbar. Override this method if you need to
22085      * add custom toolbar buttons.
22086      * @param {HtmlEditor} editor
22087      */
22088     createToolbar : function(){
22089         
22090         Roo.log("create toolbars");
22091         
22092         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
22093         this.toolbars[0].render(this.toolbarContainer());
22094         
22095         return;
22096         
22097 //        if (!editor.toolbars || !editor.toolbars.length) {
22098 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
22099 //        }
22100 //        
22101 //        for (var i =0 ; i < editor.toolbars.length;i++) {
22102 //            editor.toolbars[i] = Roo.factory(
22103 //                    typeof(editor.toolbars[i]) == 'string' ?
22104 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
22105 //                Roo.bootstrap.HtmlEditor);
22106 //            editor.toolbars[i].init(editor);
22107 //        }
22108     },
22109
22110      
22111     // private
22112     onRender : function(ct, position)
22113     {
22114        // Roo.log("Call onRender: " + this.xtype);
22115         var _t = this;
22116         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
22117       
22118         this.wrap = this.inputEl().wrap({
22119             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22120         });
22121         
22122         this.editorcore.onRender(ct, position);
22123          
22124         if (this.resizable) {
22125             this.resizeEl = new Roo.Resizable(this.wrap, {
22126                 pinned : true,
22127                 wrap: true,
22128                 dynamic : true,
22129                 minHeight : this.height,
22130                 height: this.height,
22131                 handles : this.resizable,
22132                 width: this.width,
22133                 listeners : {
22134                     resize : function(r, w, h) {
22135                         _t.onResize(w,h); // -something
22136                     }
22137                 }
22138             });
22139             
22140         }
22141         this.createToolbar(this);
22142        
22143         
22144         if(!this.width && this.resizable){
22145             this.setSize(this.wrap.getSize());
22146         }
22147         if (this.resizeEl) {
22148             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22149             // should trigger onReize..
22150         }
22151         
22152     },
22153
22154     // private
22155     onResize : function(w, h)
22156     {
22157         Roo.log('resize: ' +w + ',' + h );
22158         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
22159         var ew = false;
22160         var eh = false;
22161         
22162         if(this.inputEl() ){
22163             if(typeof w == 'number'){
22164                 var aw = w - this.wrap.getFrameWidth('lr');
22165                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
22166                 ew = aw;
22167             }
22168             if(typeof h == 'number'){
22169                  var tbh = -11;  // fixme it needs to tool bar size!
22170                 for (var i =0; i < this.toolbars.length;i++) {
22171                     // fixme - ask toolbars for heights?
22172                     tbh += this.toolbars[i].el.getHeight();
22173                     //if (this.toolbars[i].footer) {
22174                     //    tbh += this.toolbars[i].footer.el.getHeight();
22175                     //}
22176                 }
22177               
22178                 
22179                 
22180                 
22181                 
22182                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22183                 ah -= 5; // knock a few pixes off for look..
22184                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
22185                 var eh = ah;
22186             }
22187         }
22188         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22189         this.editorcore.onResize(ew,eh);
22190         
22191     },
22192
22193     /**
22194      * Toggles the editor between standard and source edit mode.
22195      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22196      */
22197     toggleSourceEdit : function(sourceEditMode)
22198     {
22199         this.editorcore.toggleSourceEdit(sourceEditMode);
22200         
22201         if(this.editorcore.sourceEditMode){
22202             Roo.log('editor - showing textarea');
22203             
22204 //            Roo.log('in');
22205 //            Roo.log(this.syncValue());
22206             this.syncValue();
22207             this.inputEl().removeClass(['hide', 'x-hidden']);
22208             this.inputEl().dom.removeAttribute('tabIndex');
22209             this.inputEl().focus();
22210         }else{
22211             Roo.log('editor - hiding textarea');
22212 //            Roo.log('out')
22213 //            Roo.log(this.pushValue()); 
22214             this.pushValue();
22215             
22216             this.inputEl().addClass(['hide', 'x-hidden']);
22217             this.inputEl().dom.setAttribute('tabIndex', -1);
22218             //this.deferFocus();
22219         }
22220          
22221         if(this.resizable){
22222             this.setSize(this.wrap.getSize());
22223         }
22224         
22225         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22226     },
22227  
22228     // private (for BoxComponent)
22229     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22230
22231     // private (for BoxComponent)
22232     getResizeEl : function(){
22233         return this.wrap;
22234     },
22235
22236     // private (for BoxComponent)
22237     getPositionEl : function(){
22238         return this.wrap;
22239     },
22240
22241     // private
22242     initEvents : function(){
22243         this.originalValue = this.getValue();
22244     },
22245
22246 //    /**
22247 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22248 //     * @method
22249 //     */
22250 //    markInvalid : Roo.emptyFn,
22251 //    /**
22252 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22253 //     * @method
22254 //     */
22255 //    clearInvalid : Roo.emptyFn,
22256
22257     setValue : function(v){
22258         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
22259         this.editorcore.pushValue();
22260     },
22261
22262      
22263     // private
22264     deferFocus : function(){
22265         this.focus.defer(10, this);
22266     },
22267
22268     // doc'ed in Field
22269     focus : function(){
22270         this.editorcore.focus();
22271         
22272     },
22273       
22274
22275     // private
22276     onDestroy : function(){
22277         
22278         
22279         
22280         if(this.rendered){
22281             
22282             for (var i =0; i < this.toolbars.length;i++) {
22283                 // fixme - ask toolbars for heights?
22284                 this.toolbars[i].onDestroy();
22285             }
22286             
22287             this.wrap.dom.innerHTML = '';
22288             this.wrap.remove();
22289         }
22290     },
22291
22292     // private
22293     onFirstFocus : function(){
22294         //Roo.log("onFirstFocus");
22295         this.editorcore.onFirstFocus();
22296          for (var i =0; i < this.toolbars.length;i++) {
22297             this.toolbars[i].onFirstFocus();
22298         }
22299         
22300     },
22301     
22302     // private
22303     syncValue : function()
22304     {   
22305         this.editorcore.syncValue();
22306     },
22307     
22308     pushValue : function()
22309     {   
22310         this.editorcore.pushValue();
22311     }
22312      
22313     
22314     // hide stuff that is not compatible
22315     /**
22316      * @event blur
22317      * @hide
22318      */
22319     /**
22320      * @event change
22321      * @hide
22322      */
22323     /**
22324      * @event focus
22325      * @hide
22326      */
22327     /**
22328      * @event specialkey
22329      * @hide
22330      */
22331     /**
22332      * @cfg {String} fieldClass @hide
22333      */
22334     /**
22335      * @cfg {String} focusClass @hide
22336      */
22337     /**
22338      * @cfg {String} autoCreate @hide
22339      */
22340     /**
22341      * @cfg {String} inputType @hide
22342      */
22343     /**
22344      * @cfg {String} invalidClass @hide
22345      */
22346     /**
22347      * @cfg {String} invalidText @hide
22348      */
22349     /**
22350      * @cfg {String} msgFx @hide
22351      */
22352     /**
22353      * @cfg {String} validateOnBlur @hide
22354      */
22355 });
22356  
22357     
22358    
22359    
22360    
22361       
22362 Roo.namespace('Roo.bootstrap.htmleditor');
22363 /**
22364  * @class Roo.bootstrap.HtmlEditorToolbar1
22365  * Basic Toolbar
22366  * 
22367  * Usage:
22368  *
22369  new Roo.bootstrap.HtmlEditor({
22370     ....
22371     toolbars : [
22372         new Roo.bootstrap.HtmlEditorToolbar1({
22373             disable : { fonts: 1 , format: 1, ..., ... , ...],
22374             btns : [ .... ]
22375         })
22376     }
22377      
22378  * 
22379  * @cfg {Object} disable List of elements to disable..
22380  * @cfg {Array} btns List of additional buttons.
22381  * 
22382  * 
22383  * NEEDS Extra CSS? 
22384  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22385  */
22386  
22387 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
22388 {
22389     
22390     Roo.apply(this, config);
22391     
22392     // default disabled, based on 'good practice'..
22393     this.disable = this.disable || {};
22394     Roo.applyIf(this.disable, {
22395         fontSize : true,
22396         colors : true,
22397         specialElements : true
22398     });
22399     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
22400     
22401     this.editor = config.editor;
22402     this.editorcore = config.editor.editorcore;
22403     
22404     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
22405     
22406     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22407     // dont call parent... till later.
22408 }
22409 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
22410      
22411     bar : true,
22412     
22413     editor : false,
22414     editorcore : false,
22415     
22416     
22417     formats : [
22418         "p" ,  
22419         "h1","h2","h3","h4","h5","h6", 
22420         "pre", "code", 
22421         "abbr", "acronym", "address", "cite", "samp", "var",
22422         'div','span'
22423     ],
22424     
22425     onRender : function(ct, position)
22426     {
22427        // Roo.log("Call onRender: " + this.xtype);
22428         
22429        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
22430        Roo.log(this.el);
22431        this.el.dom.style.marginBottom = '0';
22432        var _this = this;
22433        var editorcore = this.editorcore;
22434        var editor= this.editor;
22435        
22436        var children = [];
22437        var btn = function(id,cmd , toggle, handler){
22438        
22439             var  event = toggle ? 'toggle' : 'click';
22440        
22441             var a = {
22442                 size : 'sm',
22443                 xtype: 'Button',
22444                 xns: Roo.bootstrap,
22445                 glyphicon : id,
22446                 cmd : id || cmd,
22447                 enableToggle:toggle !== false,
22448                 //html : 'submit'
22449                 pressed : toggle ? false : null,
22450                 listeners : {}
22451             };
22452             a.listeners[toggle ? 'toggle' : 'click'] = function() {
22453                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
22454             };
22455             children.push(a);
22456             return a;
22457        }
22458         
22459         var style = {
22460                 xtype: 'Button',
22461                 size : 'sm',
22462                 xns: Roo.bootstrap,
22463                 glyphicon : 'font',
22464                 //html : 'submit'
22465                 menu : {
22466                     xtype: 'Menu',
22467                     xns: Roo.bootstrap,
22468                     items:  []
22469                 }
22470         };
22471         Roo.each(this.formats, function(f) {
22472             style.menu.items.push({
22473                 xtype :'MenuItem',
22474                 xns: Roo.bootstrap,
22475                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
22476                 tagname : f,
22477                 listeners : {
22478                     click : function()
22479                     {
22480                         editorcore.insertTag(this.tagname);
22481                         editor.focus();
22482                     }
22483                 }
22484                 
22485             });
22486         });
22487          children.push(style);   
22488             
22489             
22490         btn('bold',false,true);
22491         btn('italic',false,true);
22492         btn('align-left', 'justifyleft',true);
22493         btn('align-center', 'justifycenter',true);
22494         btn('align-right' , 'justifyright',true);
22495         btn('link', false, false, function(btn) {
22496             //Roo.log("create link?");
22497             var url = prompt(this.createLinkText, this.defaultLinkValue);
22498             if(url && url != 'http:/'+'/'){
22499                 this.editorcore.relayCmd('createlink', url);
22500             }
22501         }),
22502         btn('list','insertunorderedlist',true);
22503         btn('pencil', false,true, function(btn){
22504                 Roo.log(this);
22505                 
22506                 this.toggleSourceEdit(btn.pressed);
22507         });
22508         /*
22509         var cog = {
22510                 xtype: 'Button',
22511                 size : 'sm',
22512                 xns: Roo.bootstrap,
22513                 glyphicon : 'cog',
22514                 //html : 'submit'
22515                 menu : {
22516                     xtype: 'Menu',
22517                     xns: Roo.bootstrap,
22518                     items:  []
22519                 }
22520         };
22521         
22522         cog.menu.items.push({
22523             xtype :'MenuItem',
22524             xns: Roo.bootstrap,
22525             html : Clean styles,
22526             tagname : f,
22527             listeners : {
22528                 click : function()
22529                 {
22530                     editorcore.insertTag(this.tagname);
22531                     editor.focus();
22532                 }
22533             }
22534             
22535         });
22536        */
22537         
22538          
22539        this.xtype = 'NavSimplebar';
22540         
22541         for(var i=0;i< children.length;i++) {
22542             
22543             this.buttons.add(this.addxtypeChild(children[i]));
22544             
22545         }
22546         
22547         editor.on('editorevent', this.updateToolbar, this);
22548     },
22549     onBtnClick : function(id)
22550     {
22551        this.editorcore.relayCmd(id);
22552        this.editorcore.focus();
22553     },
22554     
22555     /**
22556      * Protected method that will not generally be called directly. It triggers
22557      * a toolbar update by reading the markup state of the current selection in the editor.
22558      */
22559     updateToolbar: function(){
22560
22561         if(!this.editorcore.activated){
22562             this.editor.onFirstFocus(); // is this neeed?
22563             return;
22564         }
22565
22566         var btns = this.buttons; 
22567         var doc = this.editorcore.doc;
22568         btns.get('bold').setActive(doc.queryCommandState('bold'));
22569         btns.get('italic').setActive(doc.queryCommandState('italic'));
22570         //btns.get('underline').setActive(doc.queryCommandState('underline'));
22571         
22572         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
22573         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
22574         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
22575         
22576         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
22577         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
22578          /*
22579         
22580         var ans = this.editorcore.getAllAncestors();
22581         if (this.formatCombo) {
22582             
22583             
22584             var store = this.formatCombo.store;
22585             this.formatCombo.setValue("");
22586             for (var i =0; i < ans.length;i++) {
22587                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
22588                     // select it..
22589                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
22590                     break;
22591                 }
22592             }
22593         }
22594         
22595         
22596         
22597         // hides menus... - so this cant be on a menu...
22598         Roo.bootstrap.MenuMgr.hideAll();
22599         */
22600         Roo.bootstrap.MenuMgr.hideAll();
22601         //this.editorsyncValue();
22602     },
22603     onFirstFocus: function() {
22604         this.buttons.each(function(item){
22605            item.enable();
22606         });
22607     },
22608     toggleSourceEdit : function(sourceEditMode){
22609         
22610           
22611         if(sourceEditMode){
22612             Roo.log("disabling buttons");
22613            this.buttons.each( function(item){
22614                 if(item.cmd != 'pencil'){
22615                     item.disable();
22616                 }
22617             });
22618           
22619         }else{
22620             Roo.log("enabling buttons");
22621             if(this.editorcore.initialized){
22622                 this.buttons.each( function(item){
22623                     item.enable();
22624                 });
22625             }
22626             
22627         }
22628         Roo.log("calling toggole on editor");
22629         // tell the editor that it's been pressed..
22630         this.editor.toggleSourceEdit(sourceEditMode);
22631        
22632     }
22633 });
22634
22635
22636
22637
22638
22639 /**
22640  * @class Roo.bootstrap.Table.AbstractSelectionModel
22641  * @extends Roo.util.Observable
22642  * Abstract base class for grid SelectionModels.  It provides the interface that should be
22643  * implemented by descendant classes.  This class should not be directly instantiated.
22644  * @constructor
22645  */
22646 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22647     this.locked = false;
22648     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22649 };
22650
22651
22652 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
22653     /** @ignore Called by the grid automatically. Do not call directly. */
22654     init : function(grid){
22655         this.grid = grid;
22656         this.initEvents();
22657     },
22658
22659     /**
22660      * Locks the selections.
22661      */
22662     lock : function(){
22663         this.locked = true;
22664     },
22665
22666     /**
22667      * Unlocks the selections.
22668      */
22669     unlock : function(){
22670         this.locked = false;
22671     },
22672
22673     /**
22674      * Returns true if the selections are locked.
22675      * @return {Boolean}
22676      */
22677     isLocked : function(){
22678         return this.locked;
22679     }
22680 });
22681 /**
22682  * @extends Roo.bootstrap.Table.AbstractSelectionModel
22683  * @class Roo.bootstrap.Table.RowSelectionModel
22684  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22685  * It supports multiple selections and keyboard selection/navigation. 
22686  * @constructor
22687  * @param {Object} config
22688  */
22689
22690 Roo.bootstrap.Table.RowSelectionModel = function(config){
22691     Roo.apply(this, config);
22692     this.selections = new Roo.util.MixedCollection(false, function(o){
22693         return o.id;
22694     });
22695
22696     this.last = false;
22697     this.lastActive = false;
22698
22699     this.addEvents({
22700         /**
22701              * @event selectionchange
22702              * Fires when the selection changes
22703              * @param {SelectionModel} this
22704              */
22705             "selectionchange" : true,
22706         /**
22707              * @event afterselectionchange
22708              * Fires after the selection changes (eg. by key press or clicking)
22709              * @param {SelectionModel} this
22710              */
22711             "afterselectionchange" : true,
22712         /**
22713              * @event beforerowselect
22714              * Fires when a row is selected being selected, return false to cancel.
22715              * @param {SelectionModel} this
22716              * @param {Number} rowIndex The selected index
22717              * @param {Boolean} keepExisting False if other selections will be cleared
22718              */
22719             "beforerowselect" : true,
22720         /**
22721              * @event rowselect
22722              * Fires when a row is selected.
22723              * @param {SelectionModel} this
22724              * @param {Number} rowIndex The selected index
22725              * @param {Roo.data.Record} r The record
22726              */
22727             "rowselect" : true,
22728         /**
22729              * @event rowdeselect
22730              * Fires when a row is deselected.
22731              * @param {SelectionModel} this
22732              * @param {Number} rowIndex The selected index
22733              */
22734         "rowdeselect" : true
22735     });
22736     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22737     this.locked = false;
22738  };
22739
22740 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
22741     /**
22742      * @cfg {Boolean} singleSelect
22743      * True to allow selection of only one row at a time (defaults to false)
22744      */
22745     singleSelect : false,
22746
22747     // private
22748     initEvents : function()
22749     {
22750
22751         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22752         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
22753         //}else{ // allow click to work like normal
22754          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
22755         //}
22756         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
22757         this.grid.on("rowclick", this.handleMouseDown, this);
22758         
22759         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22760             "up" : function(e){
22761                 if(!e.shiftKey){
22762                     this.selectPrevious(e.shiftKey);
22763                 }else if(this.last !== false && this.lastActive !== false){
22764                     var last = this.last;
22765                     this.selectRange(this.last,  this.lastActive-1);
22766                     this.grid.getView().focusRow(this.lastActive);
22767                     if(last !== false){
22768                         this.last = last;
22769                     }
22770                 }else{
22771                     this.selectFirstRow();
22772                 }
22773                 this.fireEvent("afterselectionchange", this);
22774             },
22775             "down" : function(e){
22776                 if(!e.shiftKey){
22777                     this.selectNext(e.shiftKey);
22778                 }else if(this.last !== false && this.lastActive !== false){
22779                     var last = this.last;
22780                     this.selectRange(this.last,  this.lastActive+1);
22781                     this.grid.getView().focusRow(this.lastActive);
22782                     if(last !== false){
22783                         this.last = last;
22784                     }
22785                 }else{
22786                     this.selectFirstRow();
22787                 }
22788                 this.fireEvent("afterselectionchange", this);
22789             },
22790             scope: this
22791         });
22792         this.grid.store.on('load', function(){
22793             this.selections.clear();
22794         },this);
22795         /*
22796         var view = this.grid.view;
22797         view.on("refresh", this.onRefresh, this);
22798         view.on("rowupdated", this.onRowUpdated, this);
22799         view.on("rowremoved", this.onRemove, this);
22800         */
22801     },
22802
22803     // private
22804     onRefresh : function()
22805     {
22806         var ds = this.grid.store, i, v = this.grid.view;
22807         var s = this.selections;
22808         s.each(function(r){
22809             if((i = ds.indexOfId(r.id)) != -1){
22810                 v.onRowSelect(i);
22811             }else{
22812                 s.remove(r);
22813             }
22814         });
22815     },
22816
22817     // private
22818     onRemove : function(v, index, r){
22819         this.selections.remove(r);
22820     },
22821
22822     // private
22823     onRowUpdated : function(v, index, r){
22824         if(this.isSelected(r)){
22825             v.onRowSelect(index);
22826         }
22827     },
22828
22829     /**
22830      * Select records.
22831      * @param {Array} records The records to select
22832      * @param {Boolean} keepExisting (optional) True to keep existing selections
22833      */
22834     selectRecords : function(records, keepExisting)
22835     {
22836         if(!keepExisting){
22837             this.clearSelections();
22838         }
22839             var ds = this.grid.store;
22840         for(var i = 0, len = records.length; i < len; i++){
22841             this.selectRow(ds.indexOf(records[i]), true);
22842         }
22843     },
22844
22845     /**
22846      * Gets the number of selected rows.
22847      * @return {Number}
22848      */
22849     getCount : function(){
22850         return this.selections.length;
22851     },
22852
22853     /**
22854      * Selects the first row in the grid.
22855      */
22856     selectFirstRow : function(){
22857         this.selectRow(0);
22858     },
22859
22860     /**
22861      * Select the last row.
22862      * @param {Boolean} keepExisting (optional) True to keep existing selections
22863      */
22864     selectLastRow : function(keepExisting){
22865         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22866         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
22867     },
22868
22869     /**
22870      * Selects the row immediately following the last selected row.
22871      * @param {Boolean} keepExisting (optional) True to keep existing selections
22872      */
22873     selectNext : function(keepExisting)
22874     {
22875             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
22876             this.selectRow(this.last+1, keepExisting);
22877             this.grid.getView().focusRow(this.last);
22878         }
22879     },
22880
22881     /**
22882      * Selects the row that precedes the last selected row.
22883      * @param {Boolean} keepExisting (optional) True to keep existing selections
22884      */
22885     selectPrevious : function(keepExisting){
22886         if(this.last){
22887             this.selectRow(this.last-1, keepExisting);
22888             this.grid.getView().focusRow(this.last);
22889         }
22890     },
22891
22892     /**
22893      * Returns the selected records
22894      * @return {Array} Array of selected records
22895      */
22896     getSelections : function(){
22897         return [].concat(this.selections.items);
22898     },
22899
22900     /**
22901      * Returns the first selected record.
22902      * @return {Record}
22903      */
22904     getSelected : function(){
22905         return this.selections.itemAt(0);
22906     },
22907
22908
22909     /**
22910      * Clears all selections.
22911      */
22912     clearSelections : function(fast)
22913     {
22914         if(this.locked) {
22915             return;
22916         }
22917         if(fast !== true){
22918                 var ds = this.grid.store;
22919             var s = this.selections;
22920             s.each(function(r){
22921                 this.deselectRow(ds.indexOfId(r.id));
22922             }, this);
22923             s.clear();
22924         }else{
22925             this.selections.clear();
22926         }
22927         this.last = false;
22928     },
22929
22930
22931     /**
22932      * Selects all rows.
22933      */
22934     selectAll : function(){
22935         if(this.locked) {
22936             return;
22937         }
22938         this.selections.clear();
22939         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
22940             this.selectRow(i, true);
22941         }
22942     },
22943
22944     /**
22945      * Returns True if there is a selection.
22946      * @return {Boolean}
22947      */
22948     hasSelection : function(){
22949         return this.selections.length > 0;
22950     },
22951
22952     /**
22953      * Returns True if the specified row is selected.
22954      * @param {Number/Record} record The record or index of the record to check
22955      * @return {Boolean}
22956      */
22957     isSelected : function(index){
22958             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
22959         return (r && this.selections.key(r.id) ? true : false);
22960     },
22961
22962     /**
22963      * Returns True if the specified record id is selected.
22964      * @param {String} id The id of record to check
22965      * @return {Boolean}
22966      */
22967     isIdSelected : function(id){
22968         return (this.selections.key(id) ? true : false);
22969     },
22970
22971
22972     // private
22973     handleMouseDBClick : function(e, t){
22974         
22975     },
22976     // private
22977     handleMouseDown : function(e, t)
22978     {
22979             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
22980         if(this.isLocked() || rowIndex < 0 ){
22981             return;
22982         };
22983         if(e.shiftKey && this.last !== false){
22984             var last = this.last;
22985             this.selectRange(last, rowIndex, e.ctrlKey);
22986             this.last = last; // reset the last
22987             t.focus();
22988     
22989         }else{
22990             var isSelected = this.isSelected(rowIndex);
22991             //Roo.log("select row:" + rowIndex);
22992             if(isSelected){
22993                 this.deselectRow(rowIndex);
22994             } else {
22995                         this.selectRow(rowIndex, true);
22996             }
22997     
22998             /*
22999                 if(e.button !== 0 && isSelected){
23000                 alert('rowIndex 2: ' + rowIndex);
23001                     view.focusRow(rowIndex);
23002                 }else if(e.ctrlKey && isSelected){
23003                     this.deselectRow(rowIndex);
23004                 }else if(!isSelected){
23005                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23006                     view.focusRow(rowIndex);
23007                 }
23008             */
23009         }
23010         this.fireEvent("afterselectionchange", this);
23011     },
23012     // private
23013     handleDragableRowClick :  function(grid, rowIndex, e) 
23014     {
23015         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23016             this.selectRow(rowIndex, false);
23017             grid.view.focusRow(rowIndex);
23018              this.fireEvent("afterselectionchange", this);
23019         }
23020     },
23021     
23022     /**
23023      * Selects multiple rows.
23024      * @param {Array} rows Array of the indexes of the row to select
23025      * @param {Boolean} keepExisting (optional) True to keep existing selections
23026      */
23027     selectRows : function(rows, keepExisting){
23028         if(!keepExisting){
23029             this.clearSelections();
23030         }
23031         for(var i = 0, len = rows.length; i < len; i++){
23032             this.selectRow(rows[i], true);
23033         }
23034     },
23035
23036     /**
23037      * Selects a range of rows. All rows in between startRow and endRow are also selected.
23038      * @param {Number} startRow The index of the first row in the range
23039      * @param {Number} endRow The index of the last row in the range
23040      * @param {Boolean} keepExisting (optional) True to retain existing selections
23041      */
23042     selectRange : function(startRow, endRow, keepExisting){
23043         if(this.locked) {
23044             return;
23045         }
23046         if(!keepExisting){
23047             this.clearSelections();
23048         }
23049         if(startRow <= endRow){
23050             for(var i = startRow; i <= endRow; i++){
23051                 this.selectRow(i, true);
23052             }
23053         }else{
23054             for(var i = startRow; i >= endRow; i--){
23055                 this.selectRow(i, true);
23056             }
23057         }
23058     },
23059
23060     /**
23061      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
23062      * @param {Number} startRow The index of the first row in the range
23063      * @param {Number} endRow The index of the last row in the range
23064      */
23065     deselectRange : function(startRow, endRow, preventViewNotify){
23066         if(this.locked) {
23067             return;
23068         }
23069         for(var i = startRow; i <= endRow; i++){
23070             this.deselectRow(i, preventViewNotify);
23071         }
23072     },
23073
23074     /**
23075      * Selects a row.
23076      * @param {Number} row The index of the row to select
23077      * @param {Boolean} keepExisting (optional) True to keep existing selections
23078      */
23079     selectRow : function(index, keepExisting, preventViewNotify)
23080     {
23081             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
23082             return;
23083         }
23084         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
23085             if(!keepExisting || this.singleSelect){
23086                 this.clearSelections();
23087             }
23088             
23089             var r = this.grid.store.getAt(index);
23090             //console.log('selectRow - record id :' + r.id);
23091             
23092             this.selections.add(r);
23093             this.last = this.lastActive = index;
23094             if(!preventViewNotify){
23095                 var proxy = new Roo.Element(
23096                                 this.grid.getRowDom(index)
23097                 );
23098                 proxy.addClass('bg-info info');
23099             }
23100             this.fireEvent("rowselect", this, index, r);
23101             this.fireEvent("selectionchange", this);
23102         }
23103     },
23104
23105     /**
23106      * Deselects a row.
23107      * @param {Number} row The index of the row to deselect
23108      */
23109     deselectRow : function(index, preventViewNotify)
23110     {
23111         if(this.locked) {
23112             return;
23113         }
23114         if(this.last == index){
23115             this.last = false;
23116         }
23117         if(this.lastActive == index){
23118             this.lastActive = false;
23119         }
23120         
23121         var r = this.grid.store.getAt(index);
23122         if (!r) {
23123             return;
23124         }
23125         
23126         this.selections.remove(r);
23127         //.console.log('deselectRow - record id :' + r.id);
23128         if(!preventViewNotify){
23129         
23130             var proxy = new Roo.Element(
23131                 this.grid.getRowDom(index)
23132             );
23133             proxy.removeClass('bg-info info');
23134         }
23135         this.fireEvent("rowdeselect", this, index);
23136         this.fireEvent("selectionchange", this);
23137     },
23138
23139     // private
23140     restoreLast : function(){
23141         if(this._last){
23142             this.last = this._last;
23143         }
23144     },
23145
23146     // private
23147     acceptsNav : function(row, col, cm){
23148         return !cm.isHidden(col) && cm.isCellEditable(col, row);
23149     },
23150
23151     // private
23152     onEditorKey : function(field, e){
23153         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
23154         if(k == e.TAB){
23155             e.stopEvent();
23156             ed.completeEdit();
23157             if(e.shiftKey){
23158                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
23159             }else{
23160                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
23161             }
23162         }else if(k == e.ENTER && !e.ctrlKey){
23163             e.stopEvent();
23164             ed.completeEdit();
23165             if(e.shiftKey){
23166                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
23167             }else{
23168                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
23169             }
23170         }else if(k == e.ESC){
23171             ed.cancelEdit();
23172         }
23173         if(newCell){
23174             g.startEditing(newCell[0], newCell[1]);
23175         }
23176     }
23177 });
23178 /*
23179  * Based on:
23180  * Ext JS Library 1.1.1
23181  * Copyright(c) 2006-2007, Ext JS, LLC.
23182  *
23183  * Originally Released Under LGPL - original licence link has changed is not relivant.
23184  *
23185  * Fork - LGPL
23186  * <script type="text/javascript">
23187  */
23188  
23189 /**
23190  * @class Roo.bootstrap.PagingToolbar
23191  * @extends Roo.bootstrap.NavSimplebar
23192  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
23193  * @constructor
23194  * Create a new PagingToolbar
23195  * @param {Object} config The config object
23196  * @param {Roo.data.Store} store
23197  */
23198 Roo.bootstrap.PagingToolbar = function(config)
23199 {
23200     // old args format still supported... - xtype is prefered..
23201         // created from xtype...
23202     
23203     this.ds = config.dataSource;
23204     
23205     if (config.store && !this.ds) {
23206         this.store= Roo.factory(config.store, Roo.data);
23207         this.ds = this.store;
23208         this.ds.xmodule = this.xmodule || false;
23209     }
23210     
23211     this.toolbarItems = [];
23212     if (config.items) {
23213         this.toolbarItems = config.items;
23214     }
23215     
23216     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
23217     
23218     this.cursor = 0;
23219     
23220     if (this.ds) { 
23221         this.bind(this.ds);
23222     }
23223     
23224     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
23225     
23226 };
23227
23228 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
23229     /**
23230      * @cfg {Roo.data.Store} dataSource
23231      * The underlying data store providing the paged data
23232      */
23233     /**
23234      * @cfg {String/HTMLElement/Element} container
23235      * container The id or element that will contain the toolbar
23236      */
23237     /**
23238      * @cfg {Boolean} displayInfo
23239      * True to display the displayMsg (defaults to false)
23240      */
23241     /**
23242      * @cfg {Number} pageSize
23243      * The number of records to display per page (defaults to 20)
23244      */
23245     pageSize: 20,
23246     /**
23247      * @cfg {String} displayMsg
23248      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
23249      */
23250     displayMsg : 'Displaying {0} - {1} of {2}',
23251     /**
23252      * @cfg {String} emptyMsg
23253      * The message to display when no records are found (defaults to "No data to display")
23254      */
23255     emptyMsg : 'No data to display',
23256     /**
23257      * Customizable piece of the default paging text (defaults to "Page")
23258      * @type String
23259      */
23260     beforePageText : "Page",
23261     /**
23262      * Customizable piece of the default paging text (defaults to "of %0")
23263      * @type String
23264      */
23265     afterPageText : "of {0}",
23266     /**
23267      * Customizable piece of the default paging text (defaults to "First Page")
23268      * @type String
23269      */
23270     firstText : "First Page",
23271     /**
23272      * Customizable piece of the default paging text (defaults to "Previous Page")
23273      * @type String
23274      */
23275     prevText : "Previous Page",
23276     /**
23277      * Customizable piece of the default paging text (defaults to "Next Page")
23278      * @type String
23279      */
23280     nextText : "Next Page",
23281     /**
23282      * Customizable piece of the default paging text (defaults to "Last Page")
23283      * @type String
23284      */
23285     lastText : "Last Page",
23286     /**
23287      * Customizable piece of the default paging text (defaults to "Refresh")
23288      * @type String
23289      */
23290     refreshText : "Refresh",
23291
23292     buttons : false,
23293     // private
23294     onRender : function(ct, position) 
23295     {
23296         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
23297         this.navgroup.parentId = this.id;
23298         this.navgroup.onRender(this.el, null);
23299         // add the buttons to the navgroup
23300         
23301         if(this.displayInfo){
23302             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
23303             this.displayEl = this.el.select('.x-paging-info', true).first();
23304 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
23305 //            this.displayEl = navel.el.select('span',true).first();
23306         }
23307         
23308         var _this = this;
23309         
23310         if(this.buttons){
23311             Roo.each(_this.buttons, function(e){ // this might need to use render????
23312                Roo.factory(e).onRender(_this.el, null);
23313             });
23314         }
23315             
23316         Roo.each(_this.toolbarItems, function(e) {
23317             _this.navgroup.addItem(e);
23318         });
23319         
23320         
23321         this.first = this.navgroup.addItem({
23322             tooltip: this.firstText,
23323             cls: "prev",
23324             icon : 'fa fa-backward',
23325             disabled: true,
23326             preventDefault: true,
23327             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
23328         });
23329         
23330         this.prev =  this.navgroup.addItem({
23331             tooltip: this.prevText,
23332             cls: "prev",
23333             icon : 'fa fa-step-backward',
23334             disabled: true,
23335             preventDefault: true,
23336             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
23337         });
23338     //this.addSeparator();
23339         
23340         
23341         var field = this.navgroup.addItem( {
23342             tagtype : 'span',
23343             cls : 'x-paging-position',
23344             
23345             html : this.beforePageText  +
23346                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
23347                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
23348          } ); //?? escaped?
23349         
23350         this.field = field.el.select('input', true).first();
23351         this.field.on("keydown", this.onPagingKeydown, this);
23352         this.field.on("focus", function(){this.dom.select();});
23353     
23354     
23355         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
23356         //this.field.setHeight(18);
23357         //this.addSeparator();
23358         this.next = this.navgroup.addItem({
23359             tooltip: this.nextText,
23360             cls: "next",
23361             html : ' <i class="fa fa-step-forward">',
23362             disabled: true,
23363             preventDefault: true,
23364             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
23365         });
23366         this.last = this.navgroup.addItem({
23367             tooltip: this.lastText,
23368             icon : 'fa fa-forward',
23369             cls: "next",
23370             disabled: true,
23371             preventDefault: true,
23372             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
23373         });
23374     //this.addSeparator();
23375         this.loading = this.navgroup.addItem({
23376             tooltip: this.refreshText,
23377             icon: 'fa fa-refresh',
23378             preventDefault: true,
23379             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
23380         });
23381         
23382     },
23383
23384     // private
23385     updateInfo : function(){
23386         if(this.displayEl){
23387             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
23388             var msg = count == 0 ?
23389                 this.emptyMsg :
23390                 String.format(
23391                     this.displayMsg,
23392                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
23393                 );
23394             this.displayEl.update(msg);
23395         }
23396     },
23397
23398     // private
23399     onLoad : function(ds, r, o){
23400        this.cursor = o.params ? o.params.start : 0;
23401        var d = this.getPageData(),
23402             ap = d.activePage,
23403             ps = d.pages;
23404         
23405        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
23406        this.field.dom.value = ap;
23407        this.first.setDisabled(ap == 1);
23408        this.prev.setDisabled(ap == 1);
23409        this.next.setDisabled(ap == ps);
23410        this.last.setDisabled(ap == ps);
23411        this.loading.enable();
23412        this.updateInfo();
23413     },
23414
23415     // private
23416     getPageData : function(){
23417         var total = this.ds.getTotalCount();
23418         return {
23419             total : total,
23420             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
23421             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
23422         };
23423     },
23424
23425     // private
23426     onLoadError : function(){
23427         this.loading.enable();
23428     },
23429
23430     // private
23431     onPagingKeydown : function(e){
23432         var k = e.getKey();
23433         var d = this.getPageData();
23434         if(k == e.RETURN){
23435             var v = this.field.dom.value, pageNum;
23436             if(!v || isNaN(pageNum = parseInt(v, 10))){
23437                 this.field.dom.value = d.activePage;
23438                 return;
23439             }
23440             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
23441             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23442             e.stopEvent();
23443         }
23444         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))
23445         {
23446           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
23447           this.field.dom.value = pageNum;
23448           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
23449           e.stopEvent();
23450         }
23451         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
23452         {
23453           var v = this.field.dom.value, pageNum; 
23454           var increment = (e.shiftKey) ? 10 : 1;
23455           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
23456                 increment *= -1;
23457           }
23458           if(!v || isNaN(pageNum = parseInt(v, 10))) {
23459             this.field.dom.value = d.activePage;
23460             return;
23461           }
23462           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
23463           {
23464             this.field.dom.value = parseInt(v, 10) + increment;
23465             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
23466             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23467           }
23468           e.stopEvent();
23469         }
23470     },
23471
23472     // private
23473     beforeLoad : function(){
23474         if(this.loading){
23475             this.loading.disable();
23476         }
23477     },
23478
23479     // private
23480     onClick : function(which){
23481         
23482         var ds = this.ds;
23483         if (!ds) {
23484             return;
23485         }
23486         
23487         switch(which){
23488             case "first":
23489                 ds.load({params:{start: 0, limit: this.pageSize}});
23490             break;
23491             case "prev":
23492                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
23493             break;
23494             case "next":
23495                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
23496             break;
23497             case "last":
23498                 var total = ds.getTotalCount();
23499                 var extra = total % this.pageSize;
23500                 var lastStart = extra ? (total - extra) : total-this.pageSize;
23501                 ds.load({params:{start: lastStart, limit: this.pageSize}});
23502             break;
23503             case "refresh":
23504                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
23505             break;
23506         }
23507     },
23508
23509     /**
23510      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
23511      * @param {Roo.data.Store} store The data store to unbind
23512      */
23513     unbind : function(ds){
23514         ds.un("beforeload", this.beforeLoad, this);
23515         ds.un("load", this.onLoad, this);
23516         ds.un("loadexception", this.onLoadError, this);
23517         ds.un("remove", this.updateInfo, this);
23518         ds.un("add", this.updateInfo, this);
23519         this.ds = undefined;
23520     },
23521
23522     /**
23523      * Binds the paging toolbar to the specified {@link Roo.data.Store}
23524      * @param {Roo.data.Store} store The data store to bind
23525      */
23526     bind : function(ds){
23527         ds.on("beforeload", this.beforeLoad, this);
23528         ds.on("load", this.onLoad, this);
23529         ds.on("loadexception", this.onLoadError, this);
23530         ds.on("remove", this.updateInfo, this);
23531         ds.on("add", this.updateInfo, this);
23532         this.ds = ds;
23533     }
23534 });/*
23535  * - LGPL
23536  *
23537  * element
23538  * 
23539  */
23540
23541 /**
23542  * @class Roo.bootstrap.MessageBar
23543  * @extends Roo.bootstrap.Component
23544  * Bootstrap MessageBar class
23545  * @cfg {String} html contents of the MessageBar
23546  * @cfg {String} weight (info | success | warning | danger) default info
23547  * @cfg {String} beforeClass insert the bar before the given class
23548  * @cfg {Boolean} closable (true | false) default false
23549  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
23550  * 
23551  * @constructor
23552  * Create a new Element
23553  * @param {Object} config The config object
23554  */
23555
23556 Roo.bootstrap.MessageBar = function(config){
23557     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
23558 };
23559
23560 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
23561     
23562     html: '',
23563     weight: 'info',
23564     closable: false,
23565     fixed: false,
23566     beforeClass: 'bootstrap-sticky-wrap',
23567     
23568     getAutoCreate : function(){
23569         
23570         var cfg = {
23571             tag: 'div',
23572             cls: 'alert alert-dismissable alert-' + this.weight,
23573             cn: [
23574                 {
23575                     tag: 'span',
23576                     cls: 'message',
23577                     html: this.html || ''
23578                 }
23579             ]
23580         };
23581         
23582         if(this.fixed){
23583             cfg.cls += ' alert-messages-fixed';
23584         }
23585         
23586         if(this.closable){
23587             cfg.cn.push({
23588                 tag: 'button',
23589                 cls: 'close',
23590                 html: 'x'
23591             });
23592         }
23593         
23594         return cfg;
23595     },
23596     
23597     onRender : function(ct, position)
23598     {
23599         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
23600         
23601         if(!this.el){
23602             var cfg = Roo.apply({},  this.getAutoCreate());
23603             cfg.id = Roo.id();
23604             
23605             if (this.cls) {
23606                 cfg.cls += ' ' + this.cls;
23607             }
23608             if (this.style) {
23609                 cfg.style = this.style;
23610             }
23611             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
23612             
23613             this.el.setVisibilityMode(Roo.Element.DISPLAY);
23614         }
23615         
23616         this.el.select('>button.close').on('click', this.hide, this);
23617         
23618     },
23619     
23620     show : function()
23621     {
23622         if (!this.rendered) {
23623             this.render();
23624         }
23625         
23626         this.el.show();
23627         
23628         this.fireEvent('show', this);
23629         
23630     },
23631     
23632     hide : function()
23633     {
23634         if (!this.rendered) {
23635             this.render();
23636         }
23637         
23638         this.el.hide();
23639         
23640         this.fireEvent('hide', this);
23641     },
23642     
23643     update : function()
23644     {
23645 //        var e = this.el.dom.firstChild;
23646 //        
23647 //        if(this.closable){
23648 //            e = e.nextSibling;
23649 //        }
23650 //        
23651 //        e.data = this.html || '';
23652
23653         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
23654     }
23655    
23656 });
23657
23658  
23659
23660      /*
23661  * - LGPL
23662  *
23663  * Graph
23664  * 
23665  */
23666
23667
23668 /**
23669  * @class Roo.bootstrap.Graph
23670  * @extends Roo.bootstrap.Component
23671  * Bootstrap Graph class
23672 > Prameters
23673  -sm {number} sm 4
23674  -md {number} md 5
23675  @cfg {String} graphtype  bar | vbar | pie
23676  @cfg {number} g_x coodinator | centre x (pie)
23677  @cfg {number} g_y coodinator | centre y (pie)
23678  @cfg {number} g_r radius (pie)
23679  @cfg {number} g_height height of the chart (respected by all elements in the set)
23680  @cfg {number} g_width width of the chart (respected by all elements in the set)
23681  @cfg {Object} title The title of the chart
23682     
23683  -{Array}  values
23684  -opts (object) options for the chart 
23685      o {
23686      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
23687      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
23688      o vgutter (number)
23689      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.
23690      o stacked (boolean) whether or not to tread values as in a stacked bar chart
23691      o to
23692      o stretch (boolean)
23693      o }
23694  -opts (object) options for the pie
23695      o{
23696      o cut
23697      o startAngle (number)
23698      o endAngle (number)
23699      } 
23700  *
23701  * @constructor
23702  * Create a new Input
23703  * @param {Object} config The config object
23704  */
23705
23706 Roo.bootstrap.Graph = function(config){
23707     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
23708     
23709     this.addEvents({
23710         // img events
23711         /**
23712          * @event click
23713          * The img click event for the img.
23714          * @param {Roo.EventObject} e
23715          */
23716         "click" : true
23717     });
23718 };
23719
23720 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
23721     
23722     sm: 4,
23723     md: 5,
23724     graphtype: 'bar',
23725     g_height: 250,
23726     g_width: 400,
23727     g_x: 50,
23728     g_y: 50,
23729     g_r: 30,
23730     opts:{
23731         //g_colors: this.colors,
23732         g_type: 'soft',
23733         g_gutter: '20%'
23734
23735     },
23736     title : false,
23737
23738     getAutoCreate : function(){
23739         
23740         var cfg = {
23741             tag: 'div',
23742             html : null
23743         };
23744         
23745         
23746         return  cfg;
23747     },
23748
23749     onRender : function(ct,position){
23750         
23751         
23752         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23753         
23754         if (typeof(Raphael) == 'undefined') {
23755             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23756             return;
23757         }
23758         
23759         this.raphael = Raphael(this.el.dom);
23760         
23761                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23762                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23763                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23764                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23765                 /*
23766                 r.text(160, 10, "Single Series Chart").attr(txtattr);
23767                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23768                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23769                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23770                 
23771                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23772                 r.barchart(330, 10, 300, 220, data1);
23773                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23774                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23775                 */
23776                 
23777                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23778                 // r.barchart(30, 30, 560, 250,  xdata, {
23779                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23780                 //     axis : "0 0 1 1",
23781                 //     axisxlabels :  xdata
23782                 //     //yvalues : cols,
23783                    
23784                 // });
23785 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23786 //        
23787 //        this.load(null,xdata,{
23788 //                axis : "0 0 1 1",
23789 //                axisxlabels :  xdata
23790 //                });
23791
23792     },
23793
23794     load : function(graphtype,xdata,opts)
23795     {
23796         this.raphael.clear();
23797         if(!graphtype) {
23798             graphtype = this.graphtype;
23799         }
23800         if(!opts){
23801             opts = this.opts;
23802         }
23803         var r = this.raphael,
23804             fin = function () {
23805                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23806             },
23807             fout = function () {
23808                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23809             },
23810             pfin = function() {
23811                 this.sector.stop();
23812                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23813
23814                 if (this.label) {
23815                     this.label[0].stop();
23816                     this.label[0].attr({ r: 7.5 });
23817                     this.label[1].attr({ "font-weight": 800 });
23818                 }
23819             },
23820             pfout = function() {
23821                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23822
23823                 if (this.label) {
23824                     this.label[0].animate({ r: 5 }, 500, "bounce");
23825                     this.label[1].attr({ "font-weight": 400 });
23826                 }
23827             };
23828
23829         switch(graphtype){
23830             case 'bar':
23831                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23832                 break;
23833             case 'hbar':
23834                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23835                 break;
23836             case 'pie':
23837 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
23838 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23839 //            
23840                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23841                 
23842                 break;
23843
23844         }
23845         
23846         if(this.title){
23847             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23848         }
23849         
23850     },
23851     
23852     setTitle: function(o)
23853     {
23854         this.title = o;
23855     },
23856     
23857     initEvents: function() {
23858         
23859         if(!this.href){
23860             this.el.on('click', this.onClick, this);
23861         }
23862     },
23863     
23864     onClick : function(e)
23865     {
23866         Roo.log('img onclick');
23867         this.fireEvent('click', this, e);
23868     }
23869    
23870 });
23871
23872  
23873 /*
23874  * - LGPL
23875  *
23876  * numberBox
23877  * 
23878  */
23879 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23880
23881 /**
23882  * @class Roo.bootstrap.dash.NumberBox
23883  * @extends Roo.bootstrap.Component
23884  * Bootstrap NumberBox class
23885  * @cfg {String} headline Box headline
23886  * @cfg {String} content Box content
23887  * @cfg {String} icon Box icon
23888  * @cfg {String} footer Footer text
23889  * @cfg {String} fhref Footer href
23890  * 
23891  * @constructor
23892  * Create a new NumberBox
23893  * @param {Object} config The config object
23894  */
23895
23896
23897 Roo.bootstrap.dash.NumberBox = function(config){
23898     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23899     
23900 };
23901
23902 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
23903     
23904     headline : '',
23905     content : '',
23906     icon : '',
23907     footer : '',
23908     fhref : '',
23909     ficon : '',
23910     
23911     getAutoCreate : function(){
23912         
23913         var cfg = {
23914             tag : 'div',
23915             cls : 'small-box ',
23916             cn : [
23917                 {
23918                     tag : 'div',
23919                     cls : 'inner',
23920                     cn :[
23921                         {
23922                             tag : 'h3',
23923                             cls : 'roo-headline',
23924                             html : this.headline
23925                         },
23926                         {
23927                             tag : 'p',
23928                             cls : 'roo-content',
23929                             html : this.content
23930                         }
23931                     ]
23932                 }
23933             ]
23934         };
23935         
23936         if(this.icon){
23937             cfg.cn.push({
23938                 tag : 'div',
23939                 cls : 'icon',
23940                 cn :[
23941                     {
23942                         tag : 'i',
23943                         cls : 'ion ' + this.icon
23944                     }
23945                 ]
23946             });
23947         }
23948         
23949         if(this.footer){
23950             var footer = {
23951                 tag : 'a',
23952                 cls : 'small-box-footer',
23953                 href : this.fhref || '#',
23954                 html : this.footer
23955             };
23956             
23957             cfg.cn.push(footer);
23958             
23959         }
23960         
23961         return  cfg;
23962     },
23963
23964     onRender : function(ct,position){
23965         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
23966
23967
23968        
23969                 
23970     },
23971
23972     setHeadline: function (value)
23973     {
23974         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
23975     },
23976     
23977     setFooter: function (value, href)
23978     {
23979         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
23980         
23981         if(href){
23982             this.el.select('a.small-box-footer',true).first().attr('href', href);
23983         }
23984         
23985     },
23986
23987     setContent: function (value)
23988     {
23989         this.el.select('.roo-content',true).first().dom.innerHTML = value;
23990     },
23991
23992     initEvents: function() 
23993     {   
23994         
23995     }
23996     
23997 });
23998
23999  
24000 /*
24001  * - LGPL
24002  *
24003  * TabBox
24004  * 
24005  */
24006 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24007
24008 /**
24009  * @class Roo.bootstrap.dash.TabBox
24010  * @extends Roo.bootstrap.Component
24011  * Bootstrap TabBox class
24012  * @cfg {String} title Title of the TabBox
24013  * @cfg {String} icon Icon of the TabBox
24014  * @cfg {Boolean} showtabs (true|false) show the tabs default true
24015  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24016  * 
24017  * @constructor
24018  * Create a new TabBox
24019  * @param {Object} config The config object
24020  */
24021
24022
24023 Roo.bootstrap.dash.TabBox = function(config){
24024     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24025     this.addEvents({
24026         // raw events
24027         /**
24028          * @event addpane
24029          * When a pane is added
24030          * @param {Roo.bootstrap.dash.TabPane} pane
24031          */
24032         "addpane" : true,
24033         /**
24034          * @event activatepane
24035          * When a pane is activated
24036          * @param {Roo.bootstrap.dash.TabPane} pane
24037          */
24038         "activatepane" : true
24039         
24040          
24041     });
24042     
24043     this.panes = [];
24044 };
24045
24046 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
24047
24048     title : '',
24049     icon : false,
24050     showtabs : true,
24051     tabScrollable : false,
24052     
24053     getChildContainer : function()
24054     {
24055         return this.el.select('.tab-content', true).first();
24056     },
24057     
24058     getAutoCreate : function(){
24059         
24060         var header = {
24061             tag: 'li',
24062             cls: 'pull-left header',
24063             html: this.title,
24064             cn : []
24065         };
24066         
24067         if(this.icon){
24068             header.cn.push({
24069                 tag: 'i',
24070                 cls: 'fa ' + this.icon
24071             });
24072         }
24073         
24074         var h = {
24075             tag: 'ul',
24076             cls: 'nav nav-tabs pull-right',
24077             cn: [
24078                 header
24079             ]
24080         };
24081         
24082         if(this.tabScrollable){
24083             h = {
24084                 tag: 'div',
24085                 cls: 'tab-header',
24086                 cn: [
24087                     {
24088                         tag: 'ul',
24089                         cls: 'nav nav-tabs pull-right',
24090                         cn: [
24091                             header
24092                         ]
24093                     }
24094                 ]
24095             };
24096         }
24097         
24098         var cfg = {
24099             tag: 'div',
24100             cls: 'nav-tabs-custom',
24101             cn: [
24102                 h,
24103                 {
24104                     tag: 'div',
24105                     cls: 'tab-content no-padding',
24106                     cn: []
24107                 }
24108             ]
24109         };
24110
24111         return  cfg;
24112     },
24113     initEvents : function()
24114     {
24115         //Roo.log('add add pane handler');
24116         this.on('addpane', this.onAddPane, this);
24117     },
24118      /**
24119      * Updates the box title
24120      * @param {String} html to set the title to.
24121      */
24122     setTitle : function(value)
24123     {
24124         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
24125     },
24126     onAddPane : function(pane)
24127     {
24128         this.panes.push(pane);
24129         //Roo.log('addpane');
24130         //Roo.log(pane);
24131         // tabs are rendere left to right..
24132         if(!this.showtabs){
24133             return;
24134         }
24135         
24136         var ctr = this.el.select('.nav-tabs', true).first();
24137          
24138          
24139         var existing = ctr.select('.nav-tab',true);
24140         var qty = existing.getCount();;
24141         
24142         
24143         var tab = ctr.createChild({
24144             tag : 'li',
24145             cls : 'nav-tab' + (qty ? '' : ' active'),
24146             cn : [
24147                 {
24148                     tag : 'a',
24149                     href:'#',
24150                     html : pane.title
24151                 }
24152             ]
24153         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
24154         pane.tab = tab;
24155         
24156         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
24157         if (!qty) {
24158             pane.el.addClass('active');
24159         }
24160         
24161                 
24162     },
24163     onTabClick : function(ev,un,ob,pane)
24164     {
24165         //Roo.log('tab - prev default');
24166         ev.preventDefault();
24167         
24168         
24169         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
24170         pane.tab.addClass('active');
24171         //Roo.log(pane.title);
24172         this.getChildContainer().select('.tab-pane',true).removeClass('active');
24173         // technically we should have a deactivate event.. but maybe add later.
24174         // and it should not de-activate the selected tab...
24175         this.fireEvent('activatepane', pane);
24176         pane.el.addClass('active');
24177         pane.fireEvent('activate');
24178         
24179         
24180     },
24181     
24182     getActivePane : function()
24183     {
24184         var r = false;
24185         Roo.each(this.panes, function(p) {
24186             if(p.el.hasClass('active')){
24187                 r = p;
24188                 return false;
24189             }
24190             
24191             return;
24192         });
24193         
24194         return r;
24195     }
24196     
24197     
24198 });
24199
24200  
24201 /*
24202  * - LGPL
24203  *
24204  * Tab pane
24205  * 
24206  */
24207 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24208 /**
24209  * @class Roo.bootstrap.TabPane
24210  * @extends Roo.bootstrap.Component
24211  * Bootstrap TabPane class
24212  * @cfg {Boolean} active (false | true) Default false
24213  * @cfg {String} title title of panel
24214
24215  * 
24216  * @constructor
24217  * Create a new TabPane
24218  * @param {Object} config The config object
24219  */
24220
24221 Roo.bootstrap.dash.TabPane = function(config){
24222     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
24223     
24224     this.addEvents({
24225         // raw events
24226         /**
24227          * @event activate
24228          * When a pane is activated
24229          * @param {Roo.bootstrap.dash.TabPane} pane
24230          */
24231         "activate" : true
24232          
24233     });
24234 };
24235
24236 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
24237     
24238     active : false,
24239     title : '',
24240     
24241     // the tabBox that this is attached to.
24242     tab : false,
24243      
24244     getAutoCreate : function() 
24245     {
24246         var cfg = {
24247             tag: 'div',
24248             cls: 'tab-pane'
24249         };
24250         
24251         if(this.active){
24252             cfg.cls += ' active';
24253         }
24254         
24255         return cfg;
24256     },
24257     initEvents  : function()
24258     {
24259         //Roo.log('trigger add pane handler');
24260         this.parent().fireEvent('addpane', this)
24261     },
24262     
24263      /**
24264      * Updates the tab title 
24265      * @param {String} html to set the title to.
24266      */
24267     setTitle: function(str)
24268     {
24269         if (!this.tab) {
24270             return;
24271         }
24272         this.title = str;
24273         this.tab.select('a', true).first().dom.innerHTML = str;
24274         
24275     }
24276     
24277     
24278     
24279 });
24280
24281  
24282
24283
24284  /*
24285  * - LGPL
24286  *
24287  * menu
24288  * 
24289  */
24290 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24291
24292 /**
24293  * @class Roo.bootstrap.menu.Menu
24294  * @extends Roo.bootstrap.Component
24295  * Bootstrap Menu class - container for Menu
24296  * @cfg {String} html Text of the menu
24297  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
24298  * @cfg {String} icon Font awesome icon
24299  * @cfg {String} pos Menu align to (top | bottom) default bottom
24300  * 
24301  * 
24302  * @constructor
24303  * Create a new Menu
24304  * @param {Object} config The config object
24305  */
24306
24307
24308 Roo.bootstrap.menu.Menu = function(config){
24309     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
24310     
24311     this.addEvents({
24312         /**
24313          * @event beforeshow
24314          * Fires before this menu is displayed
24315          * @param {Roo.bootstrap.menu.Menu} this
24316          */
24317         beforeshow : true,
24318         /**
24319          * @event beforehide
24320          * Fires before this menu is hidden
24321          * @param {Roo.bootstrap.menu.Menu} this
24322          */
24323         beforehide : true,
24324         /**
24325          * @event show
24326          * Fires after this menu is displayed
24327          * @param {Roo.bootstrap.menu.Menu} this
24328          */
24329         show : true,
24330         /**
24331          * @event hide
24332          * Fires after this menu is hidden
24333          * @param {Roo.bootstrap.menu.Menu} this
24334          */
24335         hide : true,
24336         /**
24337          * @event click
24338          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
24339          * @param {Roo.bootstrap.menu.Menu} this
24340          * @param {Roo.EventObject} e
24341          */
24342         click : true
24343     });
24344     
24345 };
24346
24347 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
24348     
24349     submenu : false,
24350     html : '',
24351     weight : 'default',
24352     icon : false,
24353     pos : 'bottom',
24354     
24355     
24356     getChildContainer : function() {
24357         if(this.isSubMenu){
24358             return this.el;
24359         }
24360         
24361         return this.el.select('ul.dropdown-menu', true).first();  
24362     },
24363     
24364     getAutoCreate : function()
24365     {
24366         var text = [
24367             {
24368                 tag : 'span',
24369                 cls : 'roo-menu-text',
24370                 html : this.html
24371             }
24372         ];
24373         
24374         if(this.icon){
24375             text.unshift({
24376                 tag : 'i',
24377                 cls : 'fa ' + this.icon
24378             })
24379         }
24380         
24381         
24382         var cfg = {
24383             tag : 'div',
24384             cls : 'btn-group',
24385             cn : [
24386                 {
24387                     tag : 'button',
24388                     cls : 'dropdown-button btn btn-' + this.weight,
24389                     cn : text
24390                 },
24391                 {
24392                     tag : 'button',
24393                     cls : 'dropdown-toggle btn btn-' + this.weight,
24394                     cn : [
24395                         {
24396                             tag : 'span',
24397                             cls : 'caret'
24398                         }
24399                     ]
24400                 },
24401                 {
24402                     tag : 'ul',
24403                     cls : 'dropdown-menu'
24404                 }
24405             ]
24406             
24407         };
24408         
24409         if(this.pos == 'top'){
24410             cfg.cls += ' dropup';
24411         }
24412         
24413         if(this.isSubMenu){
24414             cfg = {
24415                 tag : 'ul',
24416                 cls : 'dropdown-menu'
24417             }
24418         }
24419         
24420         return cfg;
24421     },
24422     
24423     onRender : function(ct, position)
24424     {
24425         this.isSubMenu = ct.hasClass('dropdown-submenu');
24426         
24427         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
24428     },
24429     
24430     initEvents : function() 
24431     {
24432         if(this.isSubMenu){
24433             return;
24434         }
24435         
24436         this.hidden = true;
24437         
24438         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
24439         this.triggerEl.on('click', this.onTriggerPress, this);
24440         
24441         this.buttonEl = this.el.select('button.dropdown-button', true).first();
24442         this.buttonEl.on('click', this.onClick, this);
24443         
24444     },
24445     
24446     list : function()
24447     {
24448         if(this.isSubMenu){
24449             return this.el;
24450         }
24451         
24452         return this.el.select('ul.dropdown-menu', true).first();
24453     },
24454     
24455     onClick : function(e)
24456     {
24457         this.fireEvent("click", this, e);
24458     },
24459     
24460     onTriggerPress  : function(e)
24461     {   
24462         if (this.isVisible()) {
24463             this.hide();
24464         } else {
24465             this.show();
24466         }
24467     },
24468     
24469     isVisible : function(){
24470         return !this.hidden;
24471     },
24472     
24473     show : function()
24474     {
24475         this.fireEvent("beforeshow", this);
24476         
24477         this.hidden = false;
24478         this.el.addClass('open');
24479         
24480         Roo.get(document).on("mouseup", this.onMouseUp, this);
24481         
24482         this.fireEvent("show", this);
24483         
24484         
24485     },
24486     
24487     hide : function()
24488     {
24489         this.fireEvent("beforehide", this);
24490         
24491         this.hidden = true;
24492         this.el.removeClass('open');
24493         
24494         Roo.get(document).un("mouseup", this.onMouseUp);
24495         
24496         this.fireEvent("hide", this);
24497     },
24498     
24499     onMouseUp : function()
24500     {
24501         this.hide();
24502     }
24503     
24504 });
24505
24506  
24507  /*
24508  * - LGPL
24509  *
24510  * menu item
24511  * 
24512  */
24513 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24514
24515 /**
24516  * @class Roo.bootstrap.menu.Item
24517  * @extends Roo.bootstrap.Component
24518  * Bootstrap MenuItem class
24519  * @cfg {Boolean} submenu (true | false) default false
24520  * @cfg {String} html text of the item
24521  * @cfg {String} href the link
24522  * @cfg {Boolean} disable (true | false) default false
24523  * @cfg {Boolean} preventDefault (true | false) default true
24524  * @cfg {String} icon Font awesome icon
24525  * @cfg {String} pos Submenu align to (left | right) default right 
24526  * 
24527  * 
24528  * @constructor
24529  * Create a new Item
24530  * @param {Object} config The config object
24531  */
24532
24533
24534 Roo.bootstrap.menu.Item = function(config){
24535     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
24536     this.addEvents({
24537         /**
24538          * @event mouseover
24539          * Fires when the mouse is hovering over this menu
24540          * @param {Roo.bootstrap.menu.Item} this
24541          * @param {Roo.EventObject} e
24542          */
24543         mouseover : true,
24544         /**
24545          * @event mouseout
24546          * Fires when the mouse exits this menu
24547          * @param {Roo.bootstrap.menu.Item} this
24548          * @param {Roo.EventObject} e
24549          */
24550         mouseout : true,
24551         // raw events
24552         /**
24553          * @event click
24554          * The raw click event for the entire grid.
24555          * @param {Roo.EventObject} e
24556          */
24557         click : true
24558     });
24559 };
24560
24561 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
24562     
24563     submenu : false,
24564     href : '',
24565     html : '',
24566     preventDefault: true,
24567     disable : false,
24568     icon : false,
24569     pos : 'right',
24570     
24571     getAutoCreate : function()
24572     {
24573         var text = [
24574             {
24575                 tag : 'span',
24576                 cls : 'roo-menu-item-text',
24577                 html : this.html
24578             }
24579         ];
24580         
24581         if(this.icon){
24582             text.unshift({
24583                 tag : 'i',
24584                 cls : 'fa ' + this.icon
24585             })
24586         }
24587         
24588         var cfg = {
24589             tag : 'li',
24590             cn : [
24591                 {
24592                     tag : 'a',
24593                     href : this.href || '#',
24594                     cn : text
24595                 }
24596             ]
24597         };
24598         
24599         if(this.disable){
24600             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
24601         }
24602         
24603         if(this.submenu){
24604             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
24605             
24606             if(this.pos == 'left'){
24607                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
24608             }
24609         }
24610         
24611         return cfg;
24612     },
24613     
24614     initEvents : function() 
24615     {
24616         this.el.on('mouseover', this.onMouseOver, this);
24617         this.el.on('mouseout', this.onMouseOut, this);
24618         
24619         this.el.select('a', true).first().on('click', this.onClick, this);
24620         
24621     },
24622     
24623     onClick : function(e)
24624     {
24625         if(this.preventDefault){
24626             e.preventDefault();
24627         }
24628         
24629         this.fireEvent("click", this, e);
24630     },
24631     
24632     onMouseOver : function(e)
24633     {
24634         if(this.submenu && this.pos == 'left'){
24635             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
24636         }
24637         
24638         this.fireEvent("mouseover", this, e);
24639     },
24640     
24641     onMouseOut : function(e)
24642     {
24643         this.fireEvent("mouseout", this, e);
24644     }
24645 });
24646
24647  
24648
24649  /*
24650  * - LGPL
24651  *
24652  * menu separator
24653  * 
24654  */
24655 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24656
24657 /**
24658  * @class Roo.bootstrap.menu.Separator
24659  * @extends Roo.bootstrap.Component
24660  * Bootstrap Separator class
24661  * 
24662  * @constructor
24663  * Create a new Separator
24664  * @param {Object} config The config object
24665  */
24666
24667
24668 Roo.bootstrap.menu.Separator = function(config){
24669     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
24670 };
24671
24672 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
24673     
24674     getAutoCreate : function(){
24675         var cfg = {
24676             tag : 'li',
24677             cls: 'divider'
24678         };
24679         
24680         return cfg;
24681     }
24682    
24683 });
24684
24685  
24686
24687  /*
24688  * - LGPL
24689  *
24690  * Tooltip
24691  * 
24692  */
24693
24694 /**
24695  * @class Roo.bootstrap.Tooltip
24696  * Bootstrap Tooltip class
24697  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
24698  * to determine which dom element triggers the tooltip.
24699  * 
24700  * It needs to add support for additional attributes like tooltip-position
24701  * 
24702  * @constructor
24703  * Create a new Toolti
24704  * @param {Object} config The config object
24705  */
24706
24707 Roo.bootstrap.Tooltip = function(config){
24708     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
24709 };
24710
24711 Roo.apply(Roo.bootstrap.Tooltip, {
24712     /**
24713      * @function init initialize tooltip monitoring.
24714      * @static
24715      */
24716     currentEl : false,
24717     currentTip : false,
24718     currentRegion : false,
24719     
24720     //  init : delay?
24721     
24722     init : function()
24723     {
24724         Roo.get(document).on('mouseover', this.enter ,this);
24725         Roo.get(document).on('mouseout', this.leave, this);
24726          
24727         
24728         this.currentTip = new Roo.bootstrap.Tooltip();
24729     },
24730     
24731     enter : function(ev)
24732     {
24733         var dom = ev.getTarget();
24734         
24735         //Roo.log(['enter',dom]);
24736         var el = Roo.fly(dom);
24737         if (this.currentEl) {
24738             //Roo.log(dom);
24739             //Roo.log(this.currentEl);
24740             //Roo.log(this.currentEl.contains(dom));
24741             if (this.currentEl == el) {
24742                 return;
24743             }
24744             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24745                 return;
24746             }
24747
24748         }
24749         
24750         if (this.currentTip.el) {
24751             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24752         }    
24753         //Roo.log(ev);
24754         
24755         if(!el || el.dom == document){
24756             return;
24757         }
24758         
24759         var bindEl = el;
24760         
24761         // you can not look for children, as if el is the body.. then everythign is the child..
24762         if (!el.attr('tooltip')) { //
24763             if (!el.select("[tooltip]").elements.length) {
24764                 return;
24765             }
24766             // is the mouse over this child...?
24767             bindEl = el.select("[tooltip]").first();
24768             var xy = ev.getXY();
24769             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24770                 //Roo.log("not in region.");
24771                 return;
24772             }
24773             //Roo.log("child element over..");
24774             
24775         }
24776         this.currentEl = bindEl;
24777         this.currentTip.bind(bindEl);
24778         this.currentRegion = Roo.lib.Region.getRegion(dom);
24779         this.currentTip.enter();
24780         
24781     },
24782     leave : function(ev)
24783     {
24784         var dom = ev.getTarget();
24785         //Roo.log(['leave',dom]);
24786         if (!this.currentEl) {
24787             return;
24788         }
24789         
24790         
24791         if (dom != this.currentEl.dom) {
24792             return;
24793         }
24794         var xy = ev.getXY();
24795         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
24796             return;
24797         }
24798         // only activate leave if mouse cursor is outside... bounding box..
24799         
24800         
24801         
24802         
24803         if (this.currentTip) {
24804             this.currentTip.leave();
24805         }
24806         //Roo.log('clear currentEl');
24807         this.currentEl = false;
24808         
24809         
24810     },
24811     alignment : {
24812         'left' : ['r-l', [-2,0], 'right'],
24813         'right' : ['l-r', [2,0], 'left'],
24814         'bottom' : ['t-b', [0,2], 'top'],
24815         'top' : [ 'b-t', [0,-2], 'bottom']
24816     }
24817     
24818 });
24819
24820
24821 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
24822     
24823     
24824     bindEl : false,
24825     
24826     delay : null, // can be { show : 300 , hide: 500}
24827     
24828     timeout : null,
24829     
24830     hoverState : null, //???
24831     
24832     placement : 'bottom', 
24833     
24834     getAutoCreate : function(){
24835     
24836         var cfg = {
24837            cls : 'tooltip',
24838            role : 'tooltip',
24839            cn : [
24840                 {
24841                     cls : 'tooltip-arrow'
24842                 },
24843                 {
24844                     cls : 'tooltip-inner'
24845                 }
24846            ]
24847         };
24848         
24849         return cfg;
24850     },
24851     bind : function(el)
24852     {
24853         this.bindEl = el;
24854     },
24855       
24856     
24857     enter : function () {
24858        
24859         if (this.timeout != null) {
24860             clearTimeout(this.timeout);
24861         }
24862         
24863         this.hoverState = 'in';
24864          //Roo.log("enter - show");
24865         if (!this.delay || !this.delay.show) {
24866             this.show();
24867             return;
24868         }
24869         var _t = this;
24870         this.timeout = setTimeout(function () {
24871             if (_t.hoverState == 'in') {
24872                 _t.show();
24873             }
24874         }, this.delay.show);
24875     },
24876     leave : function()
24877     {
24878         clearTimeout(this.timeout);
24879     
24880         this.hoverState = 'out';
24881          if (!this.delay || !this.delay.hide) {
24882             this.hide();
24883             return;
24884         }
24885        
24886         var _t = this;
24887         this.timeout = setTimeout(function () {
24888             //Roo.log("leave - timeout");
24889             
24890             if (_t.hoverState == 'out') {
24891                 _t.hide();
24892                 Roo.bootstrap.Tooltip.currentEl = false;
24893             }
24894         }, delay);
24895     },
24896     
24897     show : function ()
24898     {
24899         if (!this.el) {
24900             this.render(document.body);
24901         }
24902         // set content.
24903         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24904         
24905         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24906         
24907         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24908         
24909         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24910         
24911         var placement = typeof this.placement == 'function' ?
24912             this.placement.call(this, this.el, on_el) :
24913             this.placement;
24914             
24915         var autoToken = /\s?auto?\s?/i;
24916         var autoPlace = autoToken.test(placement);
24917         if (autoPlace) {
24918             placement = placement.replace(autoToken, '') || 'top';
24919         }
24920         
24921         //this.el.detach()
24922         //this.el.setXY([0,0]);
24923         this.el.show();
24924         //this.el.dom.style.display='block';
24925         
24926         //this.el.appendTo(on_el);
24927         
24928         var p = this.getPosition();
24929         var box = this.el.getBox();
24930         
24931         if (autoPlace) {
24932             // fixme..
24933         }
24934         
24935         var align = Roo.bootstrap.Tooltip.alignment[placement];
24936         
24937         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
24938         
24939         if(placement == 'top' || placement == 'bottom'){
24940             if(xy[0] < 0){
24941                 placement = 'right';
24942             }
24943             
24944             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
24945                 placement = 'left';
24946             }
24947             
24948             var scroll = Roo.select('body', true).first().getScroll();
24949             
24950             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
24951                 placement = 'top';
24952             }
24953             
24954         }
24955         
24956         align = Roo.bootstrap.Tooltip.alignment[placement];
24957         
24958         this.el.alignTo(this.bindEl, align[0],align[1]);
24959         //var arrow = this.el.select('.arrow',true).first();
24960         //arrow.set(align[2], 
24961         
24962         this.el.addClass(placement);
24963         
24964         this.el.addClass('in fade');
24965         
24966         this.hoverState = null;
24967         
24968         if (this.el.hasClass('fade')) {
24969             // fade it?
24970         }
24971         
24972     },
24973     hide : function()
24974     {
24975          
24976         if (!this.el) {
24977             return;
24978         }
24979         //this.el.setXY([0,0]);
24980         this.el.removeClass('in');
24981         //this.el.hide();
24982         
24983     }
24984     
24985 });
24986  
24987
24988  /*
24989  * - LGPL
24990  *
24991  * Location Picker
24992  * 
24993  */
24994
24995 /**
24996  * @class Roo.bootstrap.LocationPicker
24997  * @extends Roo.bootstrap.Component
24998  * Bootstrap LocationPicker class
24999  * @cfg {Number} latitude Position when init default 0
25000  * @cfg {Number} longitude Position when init default 0
25001  * @cfg {Number} zoom default 15
25002  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25003  * @cfg {Boolean} mapTypeControl default false
25004  * @cfg {Boolean} disableDoubleClickZoom default false
25005  * @cfg {Boolean} scrollwheel default true
25006  * @cfg {Boolean} streetViewControl default false
25007  * @cfg {Number} radius default 0
25008  * @cfg {String} locationName
25009  * @cfg {Boolean} draggable default true
25010  * @cfg {Boolean} enableAutocomplete default false
25011  * @cfg {Boolean} enableReverseGeocode default true
25012  * @cfg {String} markerTitle
25013  * 
25014  * @constructor
25015  * Create a new LocationPicker
25016  * @param {Object} config The config object
25017  */
25018
25019
25020 Roo.bootstrap.LocationPicker = function(config){
25021     
25022     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25023     
25024     this.addEvents({
25025         /**
25026          * @event initial
25027          * Fires when the picker initialized.
25028          * @param {Roo.bootstrap.LocationPicker} this
25029          * @param {Google Location} location
25030          */
25031         initial : true,
25032         /**
25033          * @event positionchanged
25034          * Fires when the picker position changed.
25035          * @param {Roo.bootstrap.LocationPicker} this
25036          * @param {Google Location} location
25037          */
25038         positionchanged : true,
25039         /**
25040          * @event resize
25041          * Fires when the map resize.
25042          * @param {Roo.bootstrap.LocationPicker} this
25043          */
25044         resize : true,
25045         /**
25046          * @event show
25047          * Fires when the map show.
25048          * @param {Roo.bootstrap.LocationPicker} this
25049          */
25050         show : true,
25051         /**
25052          * @event hide
25053          * Fires when the map hide.
25054          * @param {Roo.bootstrap.LocationPicker} this
25055          */
25056         hide : true,
25057         /**
25058          * @event mapClick
25059          * Fires when click the map.
25060          * @param {Roo.bootstrap.LocationPicker} this
25061          * @param {Map event} e
25062          */
25063         mapClick : true,
25064         /**
25065          * @event mapRightClick
25066          * Fires when right click the map.
25067          * @param {Roo.bootstrap.LocationPicker} this
25068          * @param {Map event} e
25069          */
25070         mapRightClick : true,
25071         /**
25072          * @event markerClick
25073          * Fires when click the marker.
25074          * @param {Roo.bootstrap.LocationPicker} this
25075          * @param {Map event} e
25076          */
25077         markerClick : true,
25078         /**
25079          * @event markerRightClick
25080          * Fires when right click the marker.
25081          * @param {Roo.bootstrap.LocationPicker} this
25082          * @param {Map event} e
25083          */
25084         markerRightClick : true,
25085         /**
25086          * @event OverlayViewDraw
25087          * Fires when OverlayView Draw
25088          * @param {Roo.bootstrap.LocationPicker} this
25089          */
25090         OverlayViewDraw : true,
25091         /**
25092          * @event OverlayViewOnAdd
25093          * Fires when OverlayView Draw
25094          * @param {Roo.bootstrap.LocationPicker} this
25095          */
25096         OverlayViewOnAdd : true,
25097         /**
25098          * @event OverlayViewOnRemove
25099          * Fires when OverlayView Draw
25100          * @param {Roo.bootstrap.LocationPicker} this
25101          */
25102         OverlayViewOnRemove : true,
25103         /**
25104          * @event OverlayViewShow
25105          * Fires when OverlayView Draw
25106          * @param {Roo.bootstrap.LocationPicker} this
25107          * @param {Pixel} cpx
25108          */
25109         OverlayViewShow : true,
25110         /**
25111          * @event OverlayViewHide
25112          * Fires when OverlayView Draw
25113          * @param {Roo.bootstrap.LocationPicker} this
25114          */
25115         OverlayViewHide : true,
25116         /**
25117          * @event loadexception
25118          * Fires when load google lib failed.
25119          * @param {Roo.bootstrap.LocationPicker} this
25120          */
25121         loadexception : true
25122     });
25123         
25124 };
25125
25126 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
25127     
25128     gMapContext: false,
25129     
25130     latitude: 0,
25131     longitude: 0,
25132     zoom: 15,
25133     mapTypeId: false,
25134     mapTypeControl: false,
25135     disableDoubleClickZoom: false,
25136     scrollwheel: true,
25137     streetViewControl: false,
25138     radius: 0,
25139     locationName: '',
25140     draggable: true,
25141     enableAutocomplete: false,
25142     enableReverseGeocode: true,
25143     markerTitle: '',
25144     
25145     getAutoCreate: function()
25146     {
25147
25148         var cfg = {
25149             tag: 'div',
25150             cls: 'roo-location-picker'
25151         };
25152         
25153         return cfg
25154     },
25155     
25156     initEvents: function(ct, position)
25157     {       
25158         if(!this.el.getWidth() || this.isApplied()){
25159             return;
25160         }
25161         
25162         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25163         
25164         this.initial();
25165     },
25166     
25167     initial: function()
25168     {
25169         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
25170             this.fireEvent('loadexception', this);
25171             return;
25172         }
25173         
25174         if(!this.mapTypeId){
25175             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
25176         }
25177         
25178         this.gMapContext = this.GMapContext();
25179         
25180         this.initOverlayView();
25181         
25182         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
25183         
25184         var _this = this;
25185                 
25186         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
25187             _this.setPosition(_this.gMapContext.marker.position);
25188         });
25189         
25190         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
25191             _this.fireEvent('mapClick', this, event);
25192             
25193         });
25194
25195         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
25196             _this.fireEvent('mapRightClick', this, event);
25197             
25198         });
25199         
25200         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
25201             _this.fireEvent('markerClick', this, event);
25202             
25203         });
25204
25205         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
25206             _this.fireEvent('markerRightClick', this, event);
25207             
25208         });
25209         
25210         this.setPosition(this.gMapContext.location);
25211         
25212         this.fireEvent('initial', this, this.gMapContext.location);
25213     },
25214     
25215     initOverlayView: function()
25216     {
25217         var _this = this;
25218         
25219         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
25220             
25221             draw: function()
25222             {
25223                 _this.fireEvent('OverlayViewDraw', _this);
25224             },
25225             
25226             onAdd: function()
25227             {
25228                 _this.fireEvent('OverlayViewOnAdd', _this);
25229             },
25230             
25231             onRemove: function()
25232             {
25233                 _this.fireEvent('OverlayViewOnRemove', _this);
25234             },
25235             
25236             show: function(cpx)
25237             {
25238                 _this.fireEvent('OverlayViewShow', _this, cpx);
25239             },
25240             
25241             hide: function()
25242             {
25243                 _this.fireEvent('OverlayViewHide', _this);
25244             }
25245             
25246         });
25247     },
25248     
25249     fromLatLngToContainerPixel: function(event)
25250     {
25251         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
25252     },
25253     
25254     isApplied: function() 
25255     {
25256         return this.getGmapContext() == false ? false : true;
25257     },
25258     
25259     getGmapContext: function() 
25260     {
25261         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
25262     },
25263     
25264     GMapContext: function() 
25265     {
25266         var position = new google.maps.LatLng(this.latitude, this.longitude);
25267         
25268         var _map = new google.maps.Map(this.el.dom, {
25269             center: position,
25270             zoom: this.zoom,
25271             mapTypeId: this.mapTypeId,
25272             mapTypeControl: this.mapTypeControl,
25273             disableDoubleClickZoom: this.disableDoubleClickZoom,
25274             scrollwheel: this.scrollwheel,
25275             streetViewControl: this.streetViewControl,
25276             locationName: this.locationName,
25277             draggable: this.draggable,
25278             enableAutocomplete: this.enableAutocomplete,
25279             enableReverseGeocode: this.enableReverseGeocode
25280         });
25281         
25282         var _marker = new google.maps.Marker({
25283             position: position,
25284             map: _map,
25285             title: this.markerTitle,
25286             draggable: this.draggable
25287         });
25288         
25289         return {
25290             map: _map,
25291             marker: _marker,
25292             circle: null,
25293             location: position,
25294             radius: this.radius,
25295             locationName: this.locationName,
25296             addressComponents: {
25297                 formatted_address: null,
25298                 addressLine1: null,
25299                 addressLine2: null,
25300                 streetName: null,
25301                 streetNumber: null,
25302                 city: null,
25303                 district: null,
25304                 state: null,
25305                 stateOrProvince: null
25306             },
25307             settings: this,
25308             domContainer: this.el.dom,
25309             geodecoder: new google.maps.Geocoder()
25310         };
25311     },
25312     
25313     drawCircle: function(center, radius, options) 
25314     {
25315         if (this.gMapContext.circle != null) {
25316             this.gMapContext.circle.setMap(null);
25317         }
25318         if (radius > 0) {
25319             radius *= 1;
25320             options = Roo.apply({}, options, {
25321                 strokeColor: "#0000FF",
25322                 strokeOpacity: .35,
25323                 strokeWeight: 2,
25324                 fillColor: "#0000FF",
25325                 fillOpacity: .2
25326             });
25327             
25328             options.map = this.gMapContext.map;
25329             options.radius = radius;
25330             options.center = center;
25331             this.gMapContext.circle = new google.maps.Circle(options);
25332             return this.gMapContext.circle;
25333         }
25334         
25335         return null;
25336     },
25337     
25338     setPosition: function(location) 
25339     {
25340         this.gMapContext.location = location;
25341         this.gMapContext.marker.setPosition(location);
25342         this.gMapContext.map.panTo(location);
25343         this.drawCircle(location, this.gMapContext.radius, {});
25344         
25345         var _this = this;
25346         
25347         if (this.gMapContext.settings.enableReverseGeocode) {
25348             this.gMapContext.geodecoder.geocode({
25349                 latLng: this.gMapContext.location
25350             }, function(results, status) {
25351                 
25352                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
25353                     _this.gMapContext.locationName = results[0].formatted_address;
25354                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
25355                     
25356                     _this.fireEvent('positionchanged', this, location);
25357                 }
25358             });
25359             
25360             return;
25361         }
25362         
25363         this.fireEvent('positionchanged', this, location);
25364     },
25365     
25366     resize: function()
25367     {
25368         google.maps.event.trigger(this.gMapContext.map, "resize");
25369         
25370         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
25371         
25372         this.fireEvent('resize', this);
25373     },
25374     
25375     setPositionByLatLng: function(latitude, longitude)
25376     {
25377         this.setPosition(new google.maps.LatLng(latitude, longitude));
25378     },
25379     
25380     getCurrentPosition: function() 
25381     {
25382         return {
25383             latitude: this.gMapContext.location.lat(),
25384             longitude: this.gMapContext.location.lng()
25385         };
25386     },
25387     
25388     getAddressName: function() 
25389     {
25390         return this.gMapContext.locationName;
25391     },
25392     
25393     getAddressComponents: function() 
25394     {
25395         return this.gMapContext.addressComponents;
25396     },
25397     
25398     address_component_from_google_geocode: function(address_components) 
25399     {
25400         var result = {};
25401         
25402         for (var i = 0; i < address_components.length; i++) {
25403             var component = address_components[i];
25404             if (component.types.indexOf("postal_code") >= 0) {
25405                 result.postalCode = component.short_name;
25406             } else if (component.types.indexOf("street_number") >= 0) {
25407                 result.streetNumber = component.short_name;
25408             } else if (component.types.indexOf("route") >= 0) {
25409                 result.streetName = component.short_name;
25410             } else if (component.types.indexOf("neighborhood") >= 0) {
25411                 result.city = component.short_name;
25412             } else if (component.types.indexOf("locality") >= 0) {
25413                 result.city = component.short_name;
25414             } else if (component.types.indexOf("sublocality") >= 0) {
25415                 result.district = component.short_name;
25416             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
25417                 result.stateOrProvince = component.short_name;
25418             } else if (component.types.indexOf("country") >= 0) {
25419                 result.country = component.short_name;
25420             }
25421         }
25422         
25423         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
25424         result.addressLine2 = "";
25425         return result;
25426     },
25427     
25428     setZoomLevel: function(zoom)
25429     {
25430         this.gMapContext.map.setZoom(zoom);
25431     },
25432     
25433     show: function()
25434     {
25435         if(!this.el){
25436             return;
25437         }
25438         
25439         this.el.show();
25440         
25441         this.resize();
25442         
25443         this.fireEvent('show', this);
25444     },
25445     
25446     hide: function()
25447     {
25448         if(!this.el){
25449             return;
25450         }
25451         
25452         this.el.hide();
25453         
25454         this.fireEvent('hide', this);
25455     }
25456     
25457 });
25458
25459 Roo.apply(Roo.bootstrap.LocationPicker, {
25460     
25461     OverlayView : function(map, options)
25462     {
25463         options = options || {};
25464         
25465         this.setMap(map);
25466     }
25467     
25468     
25469 });/*
25470  * - LGPL
25471  *
25472  * Alert
25473  * 
25474  */
25475
25476 /**
25477  * @class Roo.bootstrap.Alert
25478  * @extends Roo.bootstrap.Component
25479  * Bootstrap Alert class
25480  * @cfg {String} title The title of alert
25481  * @cfg {String} html The content of alert
25482  * @cfg {String} weight (  success | info | warning | danger )
25483  * @cfg {String} faicon font-awesomeicon
25484  * 
25485  * @constructor
25486  * Create a new alert
25487  * @param {Object} config The config object
25488  */
25489
25490
25491 Roo.bootstrap.Alert = function(config){
25492     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
25493     
25494 };
25495
25496 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
25497     
25498     title: '',
25499     html: '',
25500     weight: false,
25501     faicon: false,
25502     
25503     getAutoCreate : function()
25504     {
25505         
25506         var cfg = {
25507             tag : 'div',
25508             cls : 'alert',
25509             cn : [
25510                 {
25511                     tag : 'i',
25512                     cls : 'roo-alert-icon'
25513                     
25514                 },
25515                 {
25516                     tag : 'b',
25517                     cls : 'roo-alert-title',
25518                     html : this.title
25519                 },
25520                 {
25521                     tag : 'span',
25522                     cls : 'roo-alert-text',
25523                     html : this.html
25524                 }
25525             ]
25526         };
25527         
25528         if(this.faicon){
25529             cfg.cn[0].cls += ' fa ' + this.faicon;
25530         }
25531         
25532         if(this.weight){
25533             cfg.cls += ' alert-' + this.weight;
25534         }
25535         
25536         return cfg;
25537     },
25538     
25539     initEvents: function() 
25540     {
25541         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25542     },
25543     
25544     setTitle : function(str)
25545     {
25546         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
25547     },
25548     
25549     setText : function(str)
25550     {
25551         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
25552     },
25553     
25554     setWeight : function(weight)
25555     {
25556         if(this.weight){
25557             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
25558         }
25559         
25560         this.weight = weight;
25561         
25562         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
25563     },
25564     
25565     setIcon : function(icon)
25566     {
25567         if(this.faicon){
25568             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
25569         }
25570         
25571         this.faicon = icon;
25572         
25573         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
25574     },
25575     
25576     hide: function() 
25577     {
25578         this.el.hide();   
25579     },
25580     
25581     show: function() 
25582     {  
25583         this.el.show();   
25584     }
25585     
25586 });
25587
25588  
25589 /*
25590 * Licence: LGPL
25591 */
25592
25593 /**
25594  * @class Roo.bootstrap.UploadCropbox
25595  * @extends Roo.bootstrap.Component
25596  * Bootstrap UploadCropbox class
25597  * @cfg {String} emptyText show when image has been loaded
25598  * @cfg {String} rotateNotify show when image too small to rotate
25599  * @cfg {Number} errorTimeout default 3000
25600  * @cfg {Number} minWidth default 300
25601  * @cfg {Number} minHeight default 300
25602  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
25603  * @cfg {Boolean} isDocument (true|false) default false
25604  * @cfg {String} url action url
25605  * @cfg {String} paramName default 'imageUpload'
25606  * @cfg {String} method default POST
25607  * @cfg {Boolean} loadMask (true|false) default true
25608  * @cfg {Boolean} loadingText default 'Loading...'
25609  * 
25610  * @constructor
25611  * Create a new UploadCropbox
25612  * @param {Object} config The config object
25613  */
25614
25615 Roo.bootstrap.UploadCropbox = function(config){
25616     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
25617     
25618     this.addEvents({
25619         /**
25620          * @event beforeselectfile
25621          * Fire before select file
25622          * @param {Roo.bootstrap.UploadCropbox} this
25623          */
25624         "beforeselectfile" : true,
25625         /**
25626          * @event initial
25627          * Fire after initEvent
25628          * @param {Roo.bootstrap.UploadCropbox} this
25629          */
25630         "initial" : true,
25631         /**
25632          * @event crop
25633          * Fire after initEvent
25634          * @param {Roo.bootstrap.UploadCropbox} this
25635          * @param {String} data
25636          */
25637         "crop" : true,
25638         /**
25639          * @event prepare
25640          * Fire when preparing the file data
25641          * @param {Roo.bootstrap.UploadCropbox} this
25642          * @param {Object} file
25643          */
25644         "prepare" : true,
25645         /**
25646          * @event exception
25647          * Fire when get exception
25648          * @param {Roo.bootstrap.UploadCropbox} this
25649          * @param {XMLHttpRequest} xhr
25650          */
25651         "exception" : true,
25652         /**
25653          * @event beforeloadcanvas
25654          * Fire before load the canvas
25655          * @param {Roo.bootstrap.UploadCropbox} this
25656          * @param {String} src
25657          */
25658         "beforeloadcanvas" : true,
25659         /**
25660          * @event trash
25661          * Fire when trash image
25662          * @param {Roo.bootstrap.UploadCropbox} this
25663          */
25664         "trash" : true,
25665         /**
25666          * @event download
25667          * Fire when download the image
25668          * @param {Roo.bootstrap.UploadCropbox} this
25669          */
25670         "download" : true,
25671         /**
25672          * @event footerbuttonclick
25673          * Fire when footerbuttonclick
25674          * @param {Roo.bootstrap.UploadCropbox} this
25675          * @param {String} type
25676          */
25677         "footerbuttonclick" : true,
25678         /**
25679          * @event resize
25680          * Fire when resize
25681          * @param {Roo.bootstrap.UploadCropbox} this
25682          */
25683         "resize" : true,
25684         /**
25685          * @event rotate
25686          * Fire when rotate the image
25687          * @param {Roo.bootstrap.UploadCropbox} this
25688          * @param {String} pos
25689          */
25690         "rotate" : true,
25691         /**
25692          * @event inspect
25693          * Fire when inspect the file
25694          * @param {Roo.bootstrap.UploadCropbox} this
25695          * @param {Object} file
25696          */
25697         "inspect" : true,
25698         /**
25699          * @event upload
25700          * Fire when xhr upload the file
25701          * @param {Roo.bootstrap.UploadCropbox} this
25702          * @param {Object} data
25703          */
25704         "upload" : true,
25705         /**
25706          * @event arrange
25707          * Fire when arrange the file data
25708          * @param {Roo.bootstrap.UploadCropbox} this
25709          * @param {Object} formData
25710          */
25711         "arrange" : true
25712     });
25713     
25714     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25715 };
25716
25717 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
25718     
25719     emptyText : 'Click to upload image',
25720     rotateNotify : 'Image is too small to rotate',
25721     errorTimeout : 3000,
25722     scale : 0,
25723     baseScale : 1,
25724     rotate : 0,
25725     dragable : false,
25726     pinching : false,
25727     mouseX : 0,
25728     mouseY : 0,
25729     cropData : false,
25730     minWidth : 300,
25731     minHeight : 300,
25732     file : false,
25733     exif : {},
25734     baseRotate : 1,
25735     cropType : 'image/jpeg',
25736     buttons : false,
25737     canvasLoaded : false,
25738     isDocument : false,
25739     method : 'POST',
25740     paramName : 'imageUpload',
25741     loadMask : true,
25742     loadingText : 'Loading...',
25743     maskEl : false,
25744     
25745     getAutoCreate : function()
25746     {
25747         var cfg = {
25748             tag : 'div',
25749             cls : 'roo-upload-cropbox',
25750             cn : [
25751                 {
25752                     tag : 'input',
25753                     cls : 'roo-upload-cropbox-selector',
25754                     type : 'file'
25755                 },
25756                 {
25757                     tag : 'div',
25758                     cls : 'roo-upload-cropbox-body',
25759                     style : 'cursor:pointer',
25760                     cn : [
25761                         {
25762                             tag : 'div',
25763                             cls : 'roo-upload-cropbox-preview'
25764                         },
25765                         {
25766                             tag : 'div',
25767                             cls : 'roo-upload-cropbox-thumb'
25768                         },
25769                         {
25770                             tag : 'div',
25771                             cls : 'roo-upload-cropbox-empty-notify',
25772                             html : this.emptyText
25773                         },
25774                         {
25775                             tag : 'div',
25776                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25777                             html : this.rotateNotify
25778                         }
25779                     ]
25780                 },
25781                 {
25782                     tag : 'div',
25783                     cls : 'roo-upload-cropbox-footer',
25784                     cn : {
25785                         tag : 'div',
25786                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25787                         cn : []
25788                     }
25789                 }
25790             ]
25791         };
25792         
25793         return cfg;
25794     },
25795     
25796     onRender : function(ct, position)
25797     {
25798         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25799         
25800         if (this.buttons.length) {
25801             
25802             Roo.each(this.buttons, function(bb) {
25803                 
25804                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25805                 
25806                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25807                 
25808             }, this);
25809         }
25810         
25811         if(this.loadMask){
25812             this.maskEl = this.el;
25813         }
25814     },
25815     
25816     initEvents : function()
25817     {
25818         this.urlAPI = (window.createObjectURL && window) || 
25819                                 (window.URL && URL.revokeObjectURL && URL) || 
25820                                 (window.webkitURL && webkitURL);
25821                         
25822         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25823         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25824         
25825         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25826         this.selectorEl.hide();
25827         
25828         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25829         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25830         
25831         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25832         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25833         this.thumbEl.hide();
25834         
25835         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25836         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25837         
25838         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25839         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25840         this.errorEl.hide();
25841         
25842         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25843         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25844         this.footerEl.hide();
25845         
25846         this.setThumbBoxSize();
25847         
25848         this.bind();
25849         
25850         this.resize();
25851         
25852         this.fireEvent('initial', this);
25853     },
25854
25855     bind : function()
25856     {
25857         var _this = this;
25858         
25859         window.addEventListener("resize", function() { _this.resize(); } );
25860         
25861         this.bodyEl.on('click', this.beforeSelectFile, this);
25862         
25863         if(Roo.isTouch){
25864             this.bodyEl.on('touchstart', this.onTouchStart, this);
25865             this.bodyEl.on('touchmove', this.onTouchMove, this);
25866             this.bodyEl.on('touchend', this.onTouchEnd, this);
25867         }
25868         
25869         if(!Roo.isTouch){
25870             this.bodyEl.on('mousedown', this.onMouseDown, this);
25871             this.bodyEl.on('mousemove', this.onMouseMove, this);
25872             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25873             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25874             Roo.get(document).on('mouseup', this.onMouseUp, this);
25875         }
25876         
25877         this.selectorEl.on('change', this.onFileSelected, this);
25878     },
25879     
25880     reset : function()
25881     {    
25882         this.scale = 0;
25883         this.baseScale = 1;
25884         this.rotate = 0;
25885         this.baseRotate = 1;
25886         this.dragable = false;
25887         this.pinching = false;
25888         this.mouseX = 0;
25889         this.mouseY = 0;
25890         this.cropData = false;
25891         this.notifyEl.dom.innerHTML = this.emptyText;
25892         
25893         this.selectorEl.dom.value = '';
25894         
25895     },
25896     
25897     resize : function()
25898     {
25899         if(this.fireEvent('resize', this) != false){
25900             this.setThumbBoxPosition();
25901             this.setCanvasPosition();
25902         }
25903     },
25904     
25905     onFooterButtonClick : function(e, el, o, type)
25906     {
25907         switch (type) {
25908             case 'rotate-left' :
25909                 this.onRotateLeft(e);
25910                 break;
25911             case 'rotate-right' :
25912                 this.onRotateRight(e);
25913                 break;
25914             case 'picture' :
25915                 this.beforeSelectFile(e);
25916                 break;
25917             case 'trash' :
25918                 this.trash(e);
25919                 break;
25920             case 'crop' :
25921                 this.crop(e);
25922                 break;
25923             case 'download' :
25924                 this.download(e);
25925                 break;
25926             default :
25927                 break;
25928         }
25929         
25930         this.fireEvent('footerbuttonclick', this, type);
25931     },
25932     
25933     beforeSelectFile : function(e)
25934     {
25935         e.preventDefault();
25936         
25937         if(this.fireEvent('beforeselectfile', this) != false){
25938             this.selectorEl.dom.click();
25939         }
25940     },
25941     
25942     onFileSelected : function(e)
25943     {
25944         e.preventDefault();
25945         
25946         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25947             return;
25948         }
25949         
25950         var file = this.selectorEl.dom.files[0];
25951         
25952         if(this.fireEvent('inspect', this, file) != false){
25953             this.prepare(file);
25954         }
25955         
25956     },
25957     
25958     trash : function(e)
25959     {
25960         this.fireEvent('trash', this);
25961     },
25962     
25963     download : function(e)
25964     {
25965         this.fireEvent('download', this);
25966     },
25967     
25968     loadCanvas : function(src)
25969     {   
25970         if(this.fireEvent('beforeloadcanvas', this, src) != false){
25971             
25972             this.reset();
25973             
25974             this.imageEl = document.createElement('img');
25975             
25976             var _this = this;
25977             
25978             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
25979             
25980             this.imageEl.src = src;
25981         }
25982     },
25983     
25984     onLoadCanvas : function()
25985     {   
25986         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
25987         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
25988         
25989         this.bodyEl.un('click', this.beforeSelectFile, this);
25990         
25991         this.notifyEl.hide();
25992         this.thumbEl.show();
25993         this.footerEl.show();
25994         
25995         this.baseRotateLevel();
25996         
25997         if(this.isDocument){
25998             this.setThumbBoxSize();
25999         }
26000         
26001         this.setThumbBoxPosition();
26002         
26003         this.baseScaleLevel();
26004         
26005         this.draw();
26006         
26007         this.resize();
26008         
26009         this.canvasLoaded = true;
26010         
26011         if(this.loadMask){
26012             this.maskEl.unmask();
26013         }
26014         
26015     },
26016     
26017     setCanvasPosition : function()
26018     {   
26019         if(!this.canvasEl){
26020             return;
26021         }
26022         
26023         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26024         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26025         
26026         this.previewEl.setLeft(pw);
26027         this.previewEl.setTop(ph);
26028         
26029     },
26030     
26031     onMouseDown : function(e)
26032     {   
26033         e.stopEvent();
26034         
26035         this.dragable = true;
26036         this.pinching = false;
26037         
26038         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26039             this.dragable = false;
26040             return;
26041         }
26042         
26043         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26044         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26045         
26046     },
26047     
26048     onMouseMove : function(e)
26049     {   
26050         e.stopEvent();
26051         
26052         if(!this.canvasLoaded){
26053             return;
26054         }
26055         
26056         if (!this.dragable){
26057             return;
26058         }
26059         
26060         var minX = Math.ceil(this.thumbEl.getLeft(true));
26061         var minY = Math.ceil(this.thumbEl.getTop(true));
26062         
26063         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
26064         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
26065         
26066         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26067         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26068         
26069         x = x - this.mouseX;
26070         y = y - this.mouseY;
26071         
26072         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
26073         var bgY = Math.ceil(y + this.previewEl.getTop(true));
26074         
26075         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
26076         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
26077         
26078         this.previewEl.setLeft(bgX);
26079         this.previewEl.setTop(bgY);
26080         
26081         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26082         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26083     },
26084     
26085     onMouseUp : function(e)
26086     {   
26087         e.stopEvent();
26088         
26089         this.dragable = false;
26090     },
26091     
26092     onMouseWheel : function(e)
26093     {   
26094         e.stopEvent();
26095         
26096         this.startScale = this.scale;
26097         
26098         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
26099         
26100         if(!this.zoomable()){
26101             this.scale = this.startScale;
26102             return;
26103         }
26104         
26105         this.draw();
26106         
26107         return;
26108     },
26109     
26110     zoomable : function()
26111     {
26112         var minScale = this.thumbEl.getWidth() / this.minWidth;
26113         
26114         if(this.minWidth < this.minHeight){
26115             minScale = this.thumbEl.getHeight() / this.minHeight;
26116         }
26117         
26118         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
26119         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
26120         
26121         if(
26122                 this.isDocument &&
26123                 (this.rotate == 0 || this.rotate == 180) && 
26124                 (
26125                     width > this.imageEl.OriginWidth || 
26126                     height > this.imageEl.OriginHeight ||
26127                     (width < this.minWidth && height < this.minHeight)
26128                 )
26129         ){
26130             return false;
26131         }
26132         
26133         if(
26134                 this.isDocument &&
26135                 (this.rotate == 90 || this.rotate == 270) && 
26136                 (
26137                     width > this.imageEl.OriginWidth || 
26138                     height > this.imageEl.OriginHeight ||
26139                     (width < this.minHeight && height < this.minWidth)
26140                 )
26141         ){
26142             return false;
26143         }
26144         
26145         if(
26146                 !this.isDocument &&
26147                 (this.rotate == 0 || this.rotate == 180) && 
26148                 (
26149                     width < this.minWidth || 
26150                     width > this.imageEl.OriginWidth || 
26151                     height < this.minHeight || 
26152                     height > this.imageEl.OriginHeight
26153                 )
26154         ){
26155             return false;
26156         }
26157         
26158         if(
26159                 !this.isDocument &&
26160                 (this.rotate == 90 || this.rotate == 270) && 
26161                 (
26162                     width < this.minHeight || 
26163                     width > this.imageEl.OriginWidth || 
26164                     height < this.minWidth || 
26165                     height > this.imageEl.OriginHeight
26166                 )
26167         ){
26168             return false;
26169         }
26170         
26171         return true;
26172         
26173     },
26174     
26175     onRotateLeft : function(e)
26176     {   
26177         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26178             
26179             var minScale = this.thumbEl.getWidth() / this.minWidth;
26180             
26181             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26182             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26183             
26184             this.startScale = this.scale;
26185             
26186             while (this.getScaleLevel() < minScale){
26187             
26188                 this.scale = this.scale + 1;
26189                 
26190                 if(!this.zoomable()){
26191                     break;
26192                 }
26193                 
26194                 if(
26195                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26196                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26197                 ){
26198                     continue;
26199                 }
26200                 
26201                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26202
26203                 this.draw();
26204                 
26205                 return;
26206             }
26207             
26208             this.scale = this.startScale;
26209             
26210             this.onRotateFail();
26211             
26212             return false;
26213         }
26214         
26215         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26216
26217         if(this.isDocument){
26218             this.setThumbBoxSize();
26219             this.setThumbBoxPosition();
26220             this.setCanvasPosition();
26221         }
26222         
26223         this.draw();
26224         
26225         this.fireEvent('rotate', this, 'left');
26226         
26227     },
26228     
26229     onRotateRight : function(e)
26230     {
26231         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26232             
26233             var minScale = this.thumbEl.getWidth() / this.minWidth;
26234         
26235             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26236             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26237             
26238             this.startScale = this.scale;
26239             
26240             while (this.getScaleLevel() < minScale){
26241             
26242                 this.scale = this.scale + 1;
26243                 
26244                 if(!this.zoomable()){
26245                     break;
26246                 }
26247                 
26248                 if(
26249                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26250                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26251                 ){
26252                     continue;
26253                 }
26254                 
26255                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26256
26257                 this.draw();
26258                 
26259                 return;
26260             }
26261             
26262             this.scale = this.startScale;
26263             
26264             this.onRotateFail();
26265             
26266             return false;
26267         }
26268         
26269         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26270
26271         if(this.isDocument){
26272             this.setThumbBoxSize();
26273             this.setThumbBoxPosition();
26274             this.setCanvasPosition();
26275         }
26276         
26277         this.draw();
26278         
26279         this.fireEvent('rotate', this, 'right');
26280     },
26281     
26282     onRotateFail : function()
26283     {
26284         this.errorEl.show(true);
26285         
26286         var _this = this;
26287         
26288         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
26289     },
26290     
26291     draw : function()
26292     {
26293         this.previewEl.dom.innerHTML = '';
26294         
26295         var canvasEl = document.createElement("canvas");
26296         
26297         var contextEl = canvasEl.getContext("2d");
26298         
26299         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26300         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26301         var center = this.imageEl.OriginWidth / 2;
26302         
26303         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
26304             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26305             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26306             center = this.imageEl.OriginHeight / 2;
26307         }
26308         
26309         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
26310         
26311         contextEl.translate(center, center);
26312         contextEl.rotate(this.rotate * Math.PI / 180);
26313
26314         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26315         
26316         this.canvasEl = document.createElement("canvas");
26317         
26318         this.contextEl = this.canvasEl.getContext("2d");
26319         
26320         switch (this.rotate) {
26321             case 0 :
26322                 
26323                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26324                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26325                 
26326                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26327                 
26328                 break;
26329             case 90 : 
26330                 
26331                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26332                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26333                 
26334                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26335                     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);
26336                     break;
26337                 }
26338                 
26339                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26340                 
26341                 break;
26342             case 180 :
26343                 
26344                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26345                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26346                 
26347                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26348                     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);
26349                     break;
26350                 }
26351                 
26352                 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);
26353                 
26354                 break;
26355             case 270 :
26356                 
26357                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26358                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26359         
26360                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26361                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26362                     break;
26363                 }
26364                 
26365                 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);
26366                 
26367                 break;
26368             default : 
26369                 break;
26370         }
26371         
26372         this.previewEl.appendChild(this.canvasEl);
26373         
26374         this.setCanvasPosition();
26375     },
26376     
26377     crop : function()
26378     {
26379         if(!this.canvasLoaded){
26380             return;
26381         }
26382         
26383         var imageCanvas = document.createElement("canvas");
26384         
26385         var imageContext = imageCanvas.getContext("2d");
26386         
26387         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26388         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26389         
26390         var center = imageCanvas.width / 2;
26391         
26392         imageContext.translate(center, center);
26393         
26394         imageContext.rotate(this.rotate * Math.PI / 180);
26395         
26396         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26397         
26398         var canvas = document.createElement("canvas");
26399         
26400         var context = canvas.getContext("2d");
26401                 
26402         canvas.width = this.minWidth;
26403         canvas.height = this.minHeight;
26404
26405         switch (this.rotate) {
26406             case 0 :
26407                 
26408                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26409                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26410                 
26411                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26412                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26413                 
26414                 var targetWidth = this.minWidth - 2 * x;
26415                 var targetHeight = this.minHeight - 2 * y;
26416                 
26417                 var scale = 1;
26418                 
26419                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26420                     scale = targetWidth / width;
26421                 }
26422                 
26423                 if(x > 0 && y == 0){
26424                     scale = targetHeight / height;
26425                 }
26426                 
26427                 if(x > 0 && y > 0){
26428                     scale = targetWidth / width;
26429                     
26430                     if(width < height){
26431                         scale = targetHeight / height;
26432                     }
26433                 }
26434                 
26435                 context.scale(scale, scale);
26436                 
26437                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26438                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26439
26440                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26441                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26442
26443                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26444                 
26445                 break;
26446             case 90 : 
26447                 
26448                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26449                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26450                 
26451                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26452                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26453                 
26454                 var targetWidth = this.minWidth - 2 * x;
26455                 var targetHeight = this.minHeight - 2 * y;
26456                 
26457                 var scale = 1;
26458                 
26459                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26460                     scale = targetWidth / width;
26461                 }
26462                 
26463                 if(x > 0 && y == 0){
26464                     scale = targetHeight / height;
26465                 }
26466                 
26467                 if(x > 0 && y > 0){
26468                     scale = targetWidth / width;
26469                     
26470                     if(width < height){
26471                         scale = targetHeight / height;
26472                     }
26473                 }
26474                 
26475                 context.scale(scale, scale);
26476                 
26477                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26478                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26479
26480                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26481                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26482                 
26483                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26484                 
26485                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26486                 
26487                 break;
26488             case 180 :
26489                 
26490                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26491                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26492                 
26493                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26494                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26495                 
26496                 var targetWidth = this.minWidth - 2 * x;
26497                 var targetHeight = this.minHeight - 2 * y;
26498                 
26499                 var scale = 1;
26500                 
26501                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26502                     scale = targetWidth / width;
26503                 }
26504                 
26505                 if(x > 0 && y == 0){
26506                     scale = targetHeight / height;
26507                 }
26508                 
26509                 if(x > 0 && y > 0){
26510                     scale = targetWidth / width;
26511                     
26512                     if(width < height){
26513                         scale = targetHeight / height;
26514                     }
26515                 }
26516                 
26517                 context.scale(scale, scale);
26518                 
26519                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26520                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26521
26522                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26523                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26524
26525                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26526                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26527                 
26528                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26529                 
26530                 break;
26531             case 270 :
26532                 
26533                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26534                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26535                 
26536                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26537                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26538                 
26539                 var targetWidth = this.minWidth - 2 * x;
26540                 var targetHeight = this.minHeight - 2 * y;
26541                 
26542                 var scale = 1;
26543                 
26544                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26545                     scale = targetWidth / width;
26546                 }
26547                 
26548                 if(x > 0 && y == 0){
26549                     scale = targetHeight / height;
26550                 }
26551                 
26552                 if(x > 0 && y > 0){
26553                     scale = targetWidth / width;
26554                     
26555                     if(width < height){
26556                         scale = targetHeight / height;
26557                     }
26558                 }
26559                 
26560                 context.scale(scale, scale);
26561                 
26562                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26563                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26564
26565                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26566                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26567                 
26568                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26569                 
26570                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26571                 
26572                 break;
26573             default : 
26574                 break;
26575         }
26576         
26577         this.cropData = canvas.toDataURL(this.cropType);
26578         
26579         if(this.fireEvent('crop', this, this.cropData) !== false){
26580             this.process(this.file, this.cropData);
26581         }
26582         
26583         return;
26584         
26585     },
26586     
26587     setThumbBoxSize : function()
26588     {
26589         var width, height;
26590         
26591         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
26592             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
26593             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
26594             
26595             this.minWidth = width;
26596             this.minHeight = height;
26597             
26598             if(this.rotate == 90 || this.rotate == 270){
26599                 this.minWidth = height;
26600                 this.minHeight = width;
26601             }
26602         }
26603         
26604         height = 300;
26605         width = Math.ceil(this.minWidth * height / this.minHeight);
26606         
26607         if(this.minWidth > this.minHeight){
26608             width = 300;
26609             height = Math.ceil(this.minHeight * width / this.minWidth);
26610         }
26611         
26612         this.thumbEl.setStyle({
26613             width : width + 'px',
26614             height : height + 'px'
26615         });
26616
26617         return;
26618             
26619     },
26620     
26621     setThumbBoxPosition : function()
26622     {
26623         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
26624         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
26625         
26626         this.thumbEl.setLeft(x);
26627         this.thumbEl.setTop(y);
26628         
26629     },
26630     
26631     baseRotateLevel : function()
26632     {
26633         this.baseRotate = 1;
26634         
26635         if(
26636                 typeof(this.exif) != 'undefined' &&
26637                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
26638                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
26639         ){
26640             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
26641         }
26642         
26643         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
26644         
26645     },
26646     
26647     baseScaleLevel : function()
26648     {
26649         var width, height;
26650         
26651         if(this.isDocument){
26652             
26653             if(this.baseRotate == 6 || this.baseRotate == 8){
26654             
26655                 height = this.thumbEl.getHeight();
26656                 this.baseScale = height / this.imageEl.OriginWidth;
26657
26658                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
26659                     width = this.thumbEl.getWidth();
26660                     this.baseScale = width / this.imageEl.OriginHeight;
26661                 }
26662
26663                 return;
26664             }
26665
26666             height = this.thumbEl.getHeight();
26667             this.baseScale = height / this.imageEl.OriginHeight;
26668
26669             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
26670                 width = this.thumbEl.getWidth();
26671                 this.baseScale = width / this.imageEl.OriginWidth;
26672             }
26673
26674             return;
26675         }
26676         
26677         if(this.baseRotate == 6 || this.baseRotate == 8){
26678             
26679             width = this.thumbEl.getHeight();
26680             this.baseScale = width / this.imageEl.OriginHeight;
26681             
26682             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
26683                 height = this.thumbEl.getWidth();
26684                 this.baseScale = height / this.imageEl.OriginHeight;
26685             }
26686             
26687             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26688                 height = this.thumbEl.getWidth();
26689                 this.baseScale = height / this.imageEl.OriginHeight;
26690                 
26691                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
26692                     width = this.thumbEl.getHeight();
26693                     this.baseScale = width / this.imageEl.OriginWidth;
26694                 }
26695             }
26696             
26697             return;
26698         }
26699         
26700         width = this.thumbEl.getWidth();
26701         this.baseScale = width / this.imageEl.OriginWidth;
26702         
26703         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
26704             height = this.thumbEl.getHeight();
26705             this.baseScale = height / this.imageEl.OriginHeight;
26706         }
26707         
26708         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26709             
26710             height = this.thumbEl.getHeight();
26711             this.baseScale = height / this.imageEl.OriginHeight;
26712             
26713             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26714                 width = this.thumbEl.getWidth();
26715                 this.baseScale = width / this.imageEl.OriginWidth;
26716             }
26717             
26718         }
26719         
26720         return;
26721     },
26722     
26723     getScaleLevel : function()
26724     {
26725         return this.baseScale * Math.pow(1.1, this.scale);
26726     },
26727     
26728     onTouchStart : function(e)
26729     {
26730         if(!this.canvasLoaded){
26731             this.beforeSelectFile(e);
26732             return;
26733         }
26734         
26735         var touches = e.browserEvent.touches;
26736         
26737         if(!touches){
26738             return;
26739         }
26740         
26741         if(touches.length == 1){
26742             this.onMouseDown(e);
26743             return;
26744         }
26745         
26746         if(touches.length != 2){
26747             return;
26748         }
26749         
26750         var coords = [];
26751         
26752         for(var i = 0, finger; finger = touches[i]; i++){
26753             coords.push(finger.pageX, finger.pageY);
26754         }
26755         
26756         var x = Math.pow(coords[0] - coords[2], 2);
26757         var y = Math.pow(coords[1] - coords[3], 2);
26758         
26759         this.startDistance = Math.sqrt(x + y);
26760         
26761         this.startScale = this.scale;
26762         
26763         this.pinching = true;
26764         this.dragable = false;
26765         
26766     },
26767     
26768     onTouchMove : function(e)
26769     {
26770         if(!this.pinching && !this.dragable){
26771             return;
26772         }
26773         
26774         var touches = e.browserEvent.touches;
26775         
26776         if(!touches){
26777             return;
26778         }
26779         
26780         if(this.dragable){
26781             this.onMouseMove(e);
26782             return;
26783         }
26784         
26785         var coords = [];
26786         
26787         for(var i = 0, finger; finger = touches[i]; i++){
26788             coords.push(finger.pageX, finger.pageY);
26789         }
26790         
26791         var x = Math.pow(coords[0] - coords[2], 2);
26792         var y = Math.pow(coords[1] - coords[3], 2);
26793         
26794         this.endDistance = Math.sqrt(x + y);
26795         
26796         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26797         
26798         if(!this.zoomable()){
26799             this.scale = this.startScale;
26800             return;
26801         }
26802         
26803         this.draw();
26804         
26805     },
26806     
26807     onTouchEnd : function(e)
26808     {
26809         this.pinching = false;
26810         this.dragable = false;
26811         
26812     },
26813     
26814     process : function(file, crop)
26815     {
26816         if(this.loadMask){
26817             this.maskEl.mask(this.loadingText);
26818         }
26819         
26820         this.xhr = new XMLHttpRequest();
26821         
26822         file.xhr = this.xhr;
26823
26824         this.xhr.open(this.method, this.url, true);
26825         
26826         var headers = {
26827             "Accept": "application/json",
26828             "Cache-Control": "no-cache",
26829             "X-Requested-With": "XMLHttpRequest"
26830         };
26831         
26832         for (var headerName in headers) {
26833             var headerValue = headers[headerName];
26834             if (headerValue) {
26835                 this.xhr.setRequestHeader(headerName, headerValue);
26836             }
26837         }
26838         
26839         var _this = this;
26840         
26841         this.xhr.onload = function()
26842         {
26843             _this.xhrOnLoad(_this.xhr);
26844         }
26845         
26846         this.xhr.onerror = function()
26847         {
26848             _this.xhrOnError(_this.xhr);
26849         }
26850         
26851         var formData = new FormData();
26852
26853         formData.append('returnHTML', 'NO');
26854         
26855         if(crop){
26856             formData.append('crop', crop);
26857         }
26858         
26859         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26860             formData.append(this.paramName, file, file.name);
26861         }
26862         
26863         if(typeof(file.filename) != 'undefined'){
26864             formData.append('filename', file.filename);
26865         }
26866         
26867         if(typeof(file.mimetype) != 'undefined'){
26868             formData.append('mimetype', file.mimetype);
26869         }
26870         
26871         if(this.fireEvent('arrange', this, formData) != false){
26872             this.xhr.send(formData);
26873         };
26874     },
26875     
26876     xhrOnLoad : function(xhr)
26877     {
26878         if(this.loadMask){
26879             this.maskEl.unmask();
26880         }
26881         
26882         if (xhr.readyState !== 4) {
26883             this.fireEvent('exception', this, xhr);
26884             return;
26885         }
26886
26887         var response = Roo.decode(xhr.responseText);
26888         
26889         if(!response.success){
26890             this.fireEvent('exception', this, xhr);
26891             return;
26892         }
26893         
26894         var response = Roo.decode(xhr.responseText);
26895         
26896         this.fireEvent('upload', this, response);
26897         
26898     },
26899     
26900     xhrOnError : function()
26901     {
26902         if(this.loadMask){
26903             this.maskEl.unmask();
26904         }
26905         
26906         Roo.log('xhr on error');
26907         
26908         var response = Roo.decode(xhr.responseText);
26909           
26910         Roo.log(response);
26911         
26912     },
26913     
26914     prepare : function(file)
26915     {   
26916         if(this.loadMask){
26917             this.maskEl.mask(this.loadingText);
26918         }
26919         
26920         this.file = false;
26921         this.exif = {};
26922         
26923         if(typeof(file) === 'string'){
26924             this.loadCanvas(file);
26925             return;
26926         }
26927         
26928         if(!file || !this.urlAPI){
26929             return;
26930         }
26931         
26932         this.file = file;
26933         this.cropType = file.type;
26934         
26935         var _this = this;
26936         
26937         if(this.fireEvent('prepare', this, this.file) != false){
26938             
26939             var reader = new FileReader();
26940             
26941             reader.onload = function (e) {
26942                 if (e.target.error) {
26943                     Roo.log(e.target.error);
26944                     return;
26945                 }
26946                 
26947                 var buffer = e.target.result,
26948                     dataView = new DataView(buffer),
26949                     offset = 2,
26950                     maxOffset = dataView.byteLength - 4,
26951                     markerBytes,
26952                     markerLength;
26953                 
26954                 if (dataView.getUint16(0) === 0xffd8) {
26955                     while (offset < maxOffset) {
26956                         markerBytes = dataView.getUint16(offset);
26957                         
26958                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
26959                             markerLength = dataView.getUint16(offset + 2) + 2;
26960                             if (offset + markerLength > dataView.byteLength) {
26961                                 Roo.log('Invalid meta data: Invalid segment size.');
26962                                 break;
26963                             }
26964                             
26965                             if(markerBytes == 0xffe1){
26966                                 _this.parseExifData(
26967                                     dataView,
26968                                     offset,
26969                                     markerLength
26970                                 );
26971                             }
26972                             
26973                             offset += markerLength;
26974                             
26975                             continue;
26976                         }
26977                         
26978                         break;
26979                     }
26980                     
26981                 }
26982                 
26983                 var url = _this.urlAPI.createObjectURL(_this.file);
26984                 
26985                 _this.loadCanvas(url);
26986                 
26987                 return;
26988             }
26989             
26990             reader.readAsArrayBuffer(this.file);
26991             
26992         }
26993         
26994     },
26995     
26996     parseExifData : function(dataView, offset, length)
26997     {
26998         var tiffOffset = offset + 10,
26999             littleEndian,
27000             dirOffset;
27001     
27002         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27003             // No Exif data, might be XMP data instead
27004             return;
27005         }
27006         
27007         // Check for the ASCII code for "Exif" (0x45786966):
27008         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27009             // No Exif data, might be XMP data instead
27010             return;
27011         }
27012         if (tiffOffset + 8 > dataView.byteLength) {
27013             Roo.log('Invalid Exif data: Invalid segment size.');
27014             return;
27015         }
27016         // Check for the two null bytes:
27017         if (dataView.getUint16(offset + 8) !== 0x0000) {
27018             Roo.log('Invalid Exif data: Missing byte alignment offset.');
27019             return;
27020         }
27021         // Check the byte alignment:
27022         switch (dataView.getUint16(tiffOffset)) {
27023         case 0x4949:
27024             littleEndian = true;
27025             break;
27026         case 0x4D4D:
27027             littleEndian = false;
27028             break;
27029         default:
27030             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27031             return;
27032         }
27033         // Check for the TIFF tag marker (0x002A):
27034         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27035             Roo.log('Invalid Exif data: Missing TIFF marker.');
27036             return;
27037         }
27038         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27039         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27040         
27041         this.parseExifTags(
27042             dataView,
27043             tiffOffset,
27044             tiffOffset + dirOffset,
27045             littleEndian
27046         );
27047     },
27048     
27049     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27050     {
27051         var tagsNumber,
27052             dirEndOffset,
27053             i;
27054         if (dirOffset + 6 > dataView.byteLength) {
27055             Roo.log('Invalid Exif data: Invalid directory offset.');
27056             return;
27057         }
27058         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
27059         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
27060         if (dirEndOffset + 4 > dataView.byteLength) {
27061             Roo.log('Invalid Exif data: Invalid directory size.');
27062             return;
27063         }
27064         for (i = 0; i < tagsNumber; i += 1) {
27065             this.parseExifTag(
27066                 dataView,
27067                 tiffOffset,
27068                 dirOffset + 2 + 12 * i, // tag offset
27069                 littleEndian
27070             );
27071         }
27072         // Return the offset to the next directory:
27073         return dataView.getUint32(dirEndOffset, littleEndian);
27074     },
27075     
27076     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
27077     {
27078         var tag = dataView.getUint16(offset, littleEndian);
27079         
27080         this.exif[tag] = this.getExifValue(
27081             dataView,
27082             tiffOffset,
27083             offset,
27084             dataView.getUint16(offset + 2, littleEndian), // tag type
27085             dataView.getUint32(offset + 4, littleEndian), // tag length
27086             littleEndian
27087         );
27088     },
27089     
27090     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
27091     {
27092         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
27093             tagSize,
27094             dataOffset,
27095             values,
27096             i,
27097             str,
27098             c;
27099     
27100         if (!tagType) {
27101             Roo.log('Invalid Exif data: Invalid tag type.');
27102             return;
27103         }
27104         
27105         tagSize = tagType.size * length;
27106         // Determine if the value is contained in the dataOffset bytes,
27107         // or if the value at the dataOffset is a pointer to the actual data:
27108         dataOffset = tagSize > 4 ?
27109                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
27110         if (dataOffset + tagSize > dataView.byteLength) {
27111             Roo.log('Invalid Exif data: Invalid data offset.');
27112             return;
27113         }
27114         if (length === 1) {
27115             return tagType.getValue(dataView, dataOffset, littleEndian);
27116         }
27117         values = [];
27118         for (i = 0; i < length; i += 1) {
27119             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
27120         }
27121         
27122         if (tagType.ascii) {
27123             str = '';
27124             // Concatenate the chars:
27125             for (i = 0; i < values.length; i += 1) {
27126                 c = values[i];
27127                 // Ignore the terminating NULL byte(s):
27128                 if (c === '\u0000') {
27129                     break;
27130                 }
27131                 str += c;
27132             }
27133             return str;
27134         }
27135         return values;
27136     }
27137     
27138 });
27139
27140 Roo.apply(Roo.bootstrap.UploadCropbox, {
27141     tags : {
27142         'Orientation': 0x0112
27143     },
27144     
27145     Orientation: {
27146             1: 0, //'top-left',
27147 //            2: 'top-right',
27148             3: 180, //'bottom-right',
27149 //            4: 'bottom-left',
27150 //            5: 'left-top',
27151             6: 90, //'right-top',
27152 //            7: 'right-bottom',
27153             8: 270 //'left-bottom'
27154     },
27155     
27156     exifTagTypes : {
27157         // byte, 8-bit unsigned int:
27158         1: {
27159             getValue: function (dataView, dataOffset) {
27160                 return dataView.getUint8(dataOffset);
27161             },
27162             size: 1
27163         },
27164         // ascii, 8-bit byte:
27165         2: {
27166             getValue: function (dataView, dataOffset) {
27167                 return String.fromCharCode(dataView.getUint8(dataOffset));
27168             },
27169             size: 1,
27170             ascii: true
27171         },
27172         // short, 16 bit int:
27173         3: {
27174             getValue: function (dataView, dataOffset, littleEndian) {
27175                 return dataView.getUint16(dataOffset, littleEndian);
27176             },
27177             size: 2
27178         },
27179         // long, 32 bit int:
27180         4: {
27181             getValue: function (dataView, dataOffset, littleEndian) {
27182                 return dataView.getUint32(dataOffset, littleEndian);
27183             },
27184             size: 4
27185         },
27186         // rational = two long values, first is numerator, second is denominator:
27187         5: {
27188             getValue: function (dataView, dataOffset, littleEndian) {
27189                 return dataView.getUint32(dataOffset, littleEndian) /
27190                     dataView.getUint32(dataOffset + 4, littleEndian);
27191             },
27192             size: 8
27193         },
27194         // slong, 32 bit signed int:
27195         9: {
27196             getValue: function (dataView, dataOffset, littleEndian) {
27197                 return dataView.getInt32(dataOffset, littleEndian);
27198             },
27199             size: 4
27200         },
27201         // srational, two slongs, first is numerator, second is denominator:
27202         10: {
27203             getValue: function (dataView, dataOffset, littleEndian) {
27204                 return dataView.getInt32(dataOffset, littleEndian) /
27205                     dataView.getInt32(dataOffset + 4, littleEndian);
27206             },
27207             size: 8
27208         }
27209     },
27210     
27211     footer : {
27212         STANDARD : [
27213             {
27214                 tag : 'div',
27215                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27216                 action : 'rotate-left',
27217                 cn : [
27218                     {
27219                         tag : 'button',
27220                         cls : 'btn btn-default',
27221                         html : '<i class="fa fa-undo"></i>'
27222                     }
27223                 ]
27224             },
27225             {
27226                 tag : 'div',
27227                 cls : 'btn-group roo-upload-cropbox-picture',
27228                 action : 'picture',
27229                 cn : [
27230                     {
27231                         tag : 'button',
27232                         cls : 'btn btn-default',
27233                         html : '<i class="fa fa-picture-o"></i>'
27234                     }
27235                 ]
27236             },
27237             {
27238                 tag : 'div',
27239                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27240                 action : 'rotate-right',
27241                 cn : [
27242                     {
27243                         tag : 'button',
27244                         cls : 'btn btn-default',
27245                         html : '<i class="fa fa-repeat"></i>'
27246                     }
27247                 ]
27248             }
27249         ],
27250         DOCUMENT : [
27251             {
27252                 tag : 'div',
27253                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27254                 action : 'rotate-left',
27255                 cn : [
27256                     {
27257                         tag : 'button',
27258                         cls : 'btn btn-default',
27259                         html : '<i class="fa fa-undo"></i>'
27260                     }
27261                 ]
27262             },
27263             {
27264                 tag : 'div',
27265                 cls : 'btn-group roo-upload-cropbox-download',
27266                 action : 'download',
27267                 cn : [
27268                     {
27269                         tag : 'button',
27270                         cls : 'btn btn-default',
27271                         html : '<i class="fa fa-download"></i>'
27272                     }
27273                 ]
27274             },
27275             {
27276                 tag : 'div',
27277                 cls : 'btn-group roo-upload-cropbox-crop',
27278                 action : 'crop',
27279                 cn : [
27280                     {
27281                         tag : 'button',
27282                         cls : 'btn btn-default',
27283                         html : '<i class="fa fa-crop"></i>'
27284                     }
27285                 ]
27286             },
27287             {
27288                 tag : 'div',
27289                 cls : 'btn-group roo-upload-cropbox-trash',
27290                 action : 'trash',
27291                 cn : [
27292                     {
27293                         tag : 'button',
27294                         cls : 'btn btn-default',
27295                         html : '<i class="fa fa-trash"></i>'
27296                     }
27297                 ]
27298             },
27299             {
27300                 tag : 'div',
27301                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27302                 action : 'rotate-right',
27303                 cn : [
27304                     {
27305                         tag : 'button',
27306                         cls : 'btn btn-default',
27307                         html : '<i class="fa fa-repeat"></i>'
27308                     }
27309                 ]
27310             }
27311         ],
27312         ROTATOR : [
27313             {
27314                 tag : 'div',
27315                 cls : 'btn-group roo-upload-cropbox-rotate-left',
27316                 action : 'rotate-left',
27317                 cn : [
27318                     {
27319                         tag : 'button',
27320                         cls : 'btn btn-default',
27321                         html : '<i class="fa fa-undo"></i>'
27322                     }
27323                 ]
27324             },
27325             {
27326                 tag : 'div',
27327                 cls : 'btn-group roo-upload-cropbox-rotate-right',
27328                 action : 'rotate-right',
27329                 cn : [
27330                     {
27331                         tag : 'button',
27332                         cls : 'btn btn-default',
27333                         html : '<i class="fa fa-repeat"></i>'
27334                     }
27335                 ]
27336             }
27337         ]
27338     }
27339 });
27340
27341 /*
27342 * Licence: LGPL
27343 */
27344
27345 /**
27346  * @class Roo.bootstrap.DocumentManager
27347  * @extends Roo.bootstrap.Component
27348  * Bootstrap DocumentManager class
27349  * @cfg {String} paramName default 'imageUpload'
27350  * @cfg {String} method default POST
27351  * @cfg {String} url action url
27352  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
27353  * @cfg {Boolean} multiple multiple upload default true
27354  * @cfg {Number} thumbSize default 300
27355  * @cfg {String} fieldLabel
27356  * @cfg {Number} labelWidth default 4
27357  * @cfg {String} labelAlign (left|top) default left
27358  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
27359  * 
27360  * @constructor
27361  * Create a new DocumentManager
27362  * @param {Object} config The config object
27363  */
27364
27365 Roo.bootstrap.DocumentManager = function(config){
27366     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
27367     
27368     this.files = [];
27369     this.delegates = [];
27370     
27371     this.addEvents({
27372         /**
27373          * @event initial
27374          * Fire when initial the DocumentManager
27375          * @param {Roo.bootstrap.DocumentManager} this
27376          */
27377         "initial" : true,
27378         /**
27379          * @event inspect
27380          * inspect selected file
27381          * @param {Roo.bootstrap.DocumentManager} this
27382          * @param {File} file
27383          */
27384         "inspect" : true,
27385         /**
27386          * @event exception
27387          * Fire when xhr load exception
27388          * @param {Roo.bootstrap.DocumentManager} this
27389          * @param {XMLHttpRequest} xhr
27390          */
27391         "exception" : true,
27392         /**
27393          * @event prepare
27394          * prepare the form data
27395          * @param {Roo.bootstrap.DocumentManager} this
27396          * @param {Object} formData
27397          */
27398         "prepare" : true,
27399         /**
27400          * @event remove
27401          * Fire when remove the file
27402          * @param {Roo.bootstrap.DocumentManager} this
27403          * @param {Object} file
27404          */
27405         "remove" : true,
27406         /**
27407          * @event refresh
27408          * Fire after refresh the file
27409          * @param {Roo.bootstrap.DocumentManager} this
27410          */
27411         "refresh" : true,
27412         /**
27413          * @event click
27414          * Fire after click the image
27415          * @param {Roo.bootstrap.DocumentManager} this
27416          * @param {Object} file
27417          */
27418         "click" : true,
27419         /**
27420          * @event edit
27421          * Fire when upload a image and editable set to true
27422          * @param {Roo.bootstrap.DocumentManager} this
27423          * @param {Object} file
27424          */
27425         "edit" : true,
27426         /**
27427          * @event beforeselectfile
27428          * Fire before select file
27429          * @param {Roo.bootstrap.DocumentManager} this
27430          */
27431         "beforeselectfile" : true,
27432         /**
27433          * @event process
27434          * Fire before process file
27435          * @param {Roo.bootstrap.DocumentManager} this
27436          * @param {Object} file
27437          */
27438         "process" : true
27439         
27440     });
27441 };
27442
27443 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
27444     
27445     boxes : 0,
27446     inputName : '',
27447     thumbSize : 300,
27448     multiple : true,
27449     files : false,
27450     method : 'POST',
27451     url : '',
27452     paramName : 'imageUpload',
27453     fieldLabel : '',
27454     labelWidth : 4,
27455     labelAlign : 'left',
27456     editable : true,
27457     delegates : false,
27458     
27459     
27460     xhr : false, 
27461     
27462     getAutoCreate : function()
27463     {   
27464         var managerWidget = {
27465             tag : 'div',
27466             cls : 'roo-document-manager',
27467             cn : [
27468                 {
27469                     tag : 'input',
27470                     cls : 'roo-document-manager-selector',
27471                     type : 'file'
27472                 },
27473                 {
27474                     tag : 'div',
27475                     cls : 'roo-document-manager-uploader',
27476                     cn : [
27477                         {
27478                             tag : 'div',
27479                             cls : 'roo-document-manager-upload-btn',
27480                             html : '<i class="fa fa-plus"></i>'
27481                         }
27482                     ]
27483                     
27484                 }
27485             ]
27486         };
27487         
27488         var content = [
27489             {
27490                 tag : 'div',
27491                 cls : 'column col-md-12',
27492                 cn : managerWidget
27493             }
27494         ];
27495         
27496         if(this.fieldLabel.length){
27497             
27498             content = [
27499                 {
27500                     tag : 'div',
27501                     cls : 'column col-md-12',
27502                     html : this.fieldLabel
27503                 },
27504                 {
27505                     tag : 'div',
27506                     cls : 'column col-md-12',
27507                     cn : managerWidget
27508                 }
27509             ];
27510
27511             if(this.labelAlign == 'left'){
27512                 content = [
27513                     {
27514                         tag : 'div',
27515                         cls : 'column col-md-' + this.labelWidth,
27516                         html : this.fieldLabel
27517                     },
27518                     {
27519                         tag : 'div',
27520                         cls : 'column col-md-' + (12 - this.labelWidth),
27521                         cn : managerWidget
27522                     }
27523                 ];
27524                 
27525             }
27526         }
27527         
27528         var cfg = {
27529             tag : 'div',
27530             cls : 'row clearfix',
27531             cn : content
27532         };
27533         
27534         return cfg;
27535         
27536     },
27537     
27538     initEvents : function()
27539     {
27540         this.managerEl = this.el.select('.roo-document-manager', true).first();
27541         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27542         
27543         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
27544         this.selectorEl.hide();
27545         
27546         if(this.multiple){
27547             this.selectorEl.attr('multiple', 'multiple');
27548         }
27549         
27550         this.selectorEl.on('change', this.onFileSelected, this);
27551         
27552         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
27553         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27554         
27555         this.uploader.on('click', this.onUploaderClick, this);
27556         
27557         this.renderProgressDialog();
27558         
27559         var _this = this;
27560         
27561         window.addEventListener("resize", function() { _this.refresh(); } );
27562         
27563         this.fireEvent('initial', this);
27564     },
27565     
27566     renderProgressDialog : function()
27567     {
27568         var _this = this;
27569         
27570         this.progressDialog = new Roo.bootstrap.Modal({
27571             cls : 'roo-document-manager-progress-dialog',
27572             allow_close : false,
27573             title : '',
27574             buttons : [
27575                 {
27576                     name  :'cancel',
27577                     weight : 'danger',
27578                     html : 'Cancel'
27579                 }
27580             ], 
27581             listeners : { 
27582                 btnclick : function() {
27583                     _this.uploadCancel();
27584                     this.hide();
27585                 }
27586             }
27587         });
27588          
27589         this.progressDialog.render(Roo.get(document.body));
27590          
27591         this.progress = new Roo.bootstrap.Progress({
27592             cls : 'roo-document-manager-progress',
27593             active : true,
27594             striped : true
27595         });
27596         
27597         this.progress.render(this.progressDialog.getChildContainer());
27598         
27599         this.progressBar = new Roo.bootstrap.ProgressBar({
27600             cls : 'roo-document-manager-progress-bar',
27601             aria_valuenow : 0,
27602             aria_valuemin : 0,
27603             aria_valuemax : 12,
27604             panel : 'success'
27605         });
27606         
27607         this.progressBar.render(this.progress.getChildContainer());
27608     },
27609     
27610     onUploaderClick : function(e)
27611     {
27612         e.preventDefault();
27613      
27614         if(this.fireEvent('beforeselectfile', this) != false){
27615             this.selectorEl.dom.click();
27616         }
27617         
27618     },
27619     
27620     onFileSelected : function(e)
27621     {
27622         e.preventDefault();
27623         
27624         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27625             return;
27626         }
27627         
27628         Roo.each(this.selectorEl.dom.files, function(file){
27629             if(this.fireEvent('inspect', this, file) != false){
27630                 this.files.push(file);
27631             }
27632         }, this);
27633         
27634         this.queue();
27635         
27636     },
27637     
27638     queue : function()
27639     {
27640         this.selectorEl.dom.value = '';
27641         
27642         if(!this.files.length){
27643             return;
27644         }
27645         
27646         if(this.boxes > 0 && this.files.length > this.boxes){
27647             this.files = this.files.slice(0, this.boxes);
27648         }
27649         
27650         this.uploader.show();
27651         
27652         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27653             this.uploader.hide();
27654         }
27655         
27656         var _this = this;
27657         
27658         var files = [];
27659         
27660         var docs = [];
27661         
27662         Roo.each(this.files, function(file){
27663             
27664             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27665                 var f = this.renderPreview(file);
27666                 files.push(f);
27667                 return;
27668             }
27669             
27670             if(file.type.indexOf('image') != -1){
27671                 this.delegates.push(
27672                     (function(){
27673                         _this.process(file);
27674                     }).createDelegate(this)
27675                 );
27676         
27677                 return;
27678             }
27679             
27680             docs.push(
27681                 (function(){
27682                     _this.process(file);
27683                 }).createDelegate(this)
27684             );
27685             
27686         }, this);
27687         
27688         this.files = files;
27689         
27690         this.delegates = this.delegates.concat(docs);
27691         
27692         if(!this.delegates.length){
27693             this.refresh();
27694             return;
27695         }
27696         
27697         this.progressBar.aria_valuemax = this.delegates.length;
27698         
27699         this.arrange();
27700         
27701         return;
27702     },
27703     
27704     arrange : function()
27705     {
27706         if(!this.delegates.length){
27707             this.progressDialog.hide();
27708             this.refresh();
27709             return;
27710         }
27711         
27712         var delegate = this.delegates.shift();
27713         
27714         this.progressDialog.show();
27715         
27716         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27717         
27718         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27719         
27720         delegate();
27721     },
27722     
27723     refresh : function()
27724     {
27725         this.uploader.show();
27726         
27727         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27728             this.uploader.hide();
27729         }
27730         
27731         Roo.isTouch ? this.closable(false) : this.closable(true);
27732         
27733         this.fireEvent('refresh', this);
27734     },
27735     
27736     onRemove : function(e, el, o)
27737     {
27738         e.preventDefault();
27739         
27740         this.fireEvent('remove', this, o);
27741         
27742     },
27743     
27744     remove : function(o)
27745     {
27746         var files = [];
27747         
27748         Roo.each(this.files, function(file){
27749             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27750                 files.push(file);
27751                 return;
27752             }
27753
27754             o.target.remove();
27755
27756         }, this);
27757         
27758         this.files = files;
27759         
27760         this.refresh();
27761     },
27762     
27763     clear : function()
27764     {
27765         Roo.each(this.files, function(file){
27766             if(!file.target){
27767                 return;
27768             }
27769             
27770             file.target.remove();
27771
27772         }, this);
27773         
27774         this.files = [];
27775         
27776         this.refresh();
27777     },
27778     
27779     onClick : function(e, el, o)
27780     {
27781         e.preventDefault();
27782         
27783         this.fireEvent('click', this, o);
27784         
27785     },
27786     
27787     closable : function(closable)
27788     {
27789         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27790             
27791             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27792             
27793             if(closable){
27794                 el.show();
27795                 return;
27796             }
27797             
27798             el.hide();
27799             
27800         }, this);
27801     },
27802     
27803     xhrOnLoad : function(xhr)
27804     {
27805         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27806             el.remove();
27807         }, this);
27808         
27809         if (xhr.readyState !== 4) {
27810             this.arrange();
27811             this.fireEvent('exception', this, xhr);
27812             return;
27813         }
27814
27815         var response = Roo.decode(xhr.responseText);
27816         
27817         if(!response.success){
27818             this.arrange();
27819             this.fireEvent('exception', this, xhr);
27820             return;
27821         }
27822         
27823         var file = this.renderPreview(response.data);
27824         
27825         this.files.push(file);
27826         
27827         this.arrange();
27828         
27829     },
27830     
27831     xhrOnError : function(xhr)
27832     {
27833         Roo.log('xhr on error');
27834         
27835         var response = Roo.decode(xhr.responseText);
27836           
27837         Roo.log(response);
27838         
27839         this.arrange();
27840     },
27841     
27842     process : function(file)
27843     {
27844         if(this.fireEvent('process', this, file) !== false){
27845             if(this.editable && file.type.indexOf('image') != -1){
27846                 this.fireEvent('edit', this, file);
27847                 return;
27848             }
27849
27850             this.uploadStart(file, false);
27851
27852             return;
27853         }
27854         
27855     },
27856     
27857     uploadStart : function(file, crop)
27858     {
27859         this.xhr = new XMLHttpRequest();
27860         
27861         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27862             this.arrange();
27863             return;
27864         }
27865         
27866         file.xhr = this.xhr;
27867             
27868         this.managerEl.createChild({
27869             tag : 'div',
27870             cls : 'roo-document-manager-loading',
27871             cn : [
27872                 {
27873                     tag : 'div',
27874                     tooltip : file.name,
27875                     cls : 'roo-document-manager-thumb',
27876                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27877                 }
27878             ]
27879
27880         });
27881
27882         this.xhr.open(this.method, this.url, true);
27883         
27884         var headers = {
27885             "Accept": "application/json",
27886             "Cache-Control": "no-cache",
27887             "X-Requested-With": "XMLHttpRequest"
27888         };
27889         
27890         for (var headerName in headers) {
27891             var headerValue = headers[headerName];
27892             if (headerValue) {
27893                 this.xhr.setRequestHeader(headerName, headerValue);
27894             }
27895         }
27896         
27897         var _this = this;
27898         
27899         this.xhr.onload = function()
27900         {
27901             _this.xhrOnLoad(_this.xhr);
27902         }
27903         
27904         this.xhr.onerror = function()
27905         {
27906             _this.xhrOnError(_this.xhr);
27907         }
27908         
27909         var formData = new FormData();
27910
27911         formData.append('returnHTML', 'NO');
27912         
27913         if(crop){
27914             formData.append('crop', crop);
27915         }
27916         
27917         formData.append(this.paramName, file, file.name);
27918         
27919         if(this.fireEvent('prepare', this, formData) != false){
27920             this.xhr.send(formData);
27921         };
27922     },
27923     
27924     uploadCancel : function()
27925     {
27926         if (this.xhr) {
27927             this.xhr.abort();
27928         }
27929         
27930         
27931         this.delegates = [];
27932         
27933         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27934             el.remove();
27935         }, this);
27936         
27937         this.arrange();
27938     },
27939     
27940     renderPreview : function(file)
27941     {
27942         if(typeof(file.target) != 'undefined' && file.target){
27943             return file;
27944         }
27945         
27946         var previewEl = this.managerEl.createChild({
27947             tag : 'div',
27948             cls : 'roo-document-manager-preview',
27949             cn : [
27950                 {
27951                     tag : 'div',
27952                     tooltip : file.filename,
27953                     cls : 'roo-document-manager-thumb',
27954                     html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
27955                 },
27956                 {
27957                     tag : 'button',
27958                     cls : 'close',
27959                     html : '<i class="fa fa-times-circle"></i>'
27960                 }
27961             ]
27962         });
27963
27964         var close = previewEl.select('button.close', true).first();
27965
27966         close.on('click', this.onRemove, this, file);
27967
27968         file.target = previewEl;
27969
27970         var image = previewEl.select('img', true).first();
27971         
27972         var _this = this;
27973         
27974         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
27975         
27976         image.on('click', this.onClick, this, file);
27977         
27978         return file;
27979         
27980     },
27981     
27982     onPreviewLoad : function(file, image)
27983     {
27984         if(typeof(file.target) == 'undefined' || !file.target){
27985             return;
27986         }
27987         
27988         var width = image.dom.naturalWidth || image.dom.width;
27989         var height = image.dom.naturalHeight || image.dom.height;
27990         
27991         if(width > height){
27992             file.target.addClass('wide');
27993             return;
27994         }
27995         
27996         file.target.addClass('tall');
27997         return;
27998         
27999     },
28000     
28001     uploadFromSource : function(file, crop)
28002     {
28003         this.xhr = new XMLHttpRequest();
28004         
28005         this.managerEl.createChild({
28006             tag : 'div',
28007             cls : 'roo-document-manager-loading',
28008             cn : [
28009                 {
28010                     tag : 'div',
28011                     tooltip : file.name,
28012                     cls : 'roo-document-manager-thumb',
28013                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28014                 }
28015             ]
28016
28017         });
28018
28019         this.xhr.open(this.method, this.url, true);
28020         
28021         var headers = {
28022             "Accept": "application/json",
28023             "Cache-Control": "no-cache",
28024             "X-Requested-With": "XMLHttpRequest"
28025         };
28026         
28027         for (var headerName in headers) {
28028             var headerValue = headers[headerName];
28029             if (headerValue) {
28030                 this.xhr.setRequestHeader(headerName, headerValue);
28031             }
28032         }
28033         
28034         var _this = this;
28035         
28036         this.xhr.onload = function()
28037         {
28038             _this.xhrOnLoad(_this.xhr);
28039         }
28040         
28041         this.xhr.onerror = function()
28042         {
28043             _this.xhrOnError(_this.xhr);
28044         }
28045         
28046         var formData = new FormData();
28047
28048         formData.append('returnHTML', 'NO');
28049         
28050         formData.append('crop', crop);
28051         
28052         if(typeof(file.filename) != 'undefined'){
28053             formData.append('filename', file.filename);
28054         }
28055         
28056         if(typeof(file.mimetype) != 'undefined'){
28057             formData.append('mimetype', file.mimetype);
28058         }
28059         
28060         if(this.fireEvent('prepare', this, formData) != false){
28061             this.xhr.send(formData);
28062         };
28063     }
28064 });
28065
28066 /*
28067 * Licence: LGPL
28068 */
28069
28070 /**
28071  * @class Roo.bootstrap.DocumentViewer
28072  * @extends Roo.bootstrap.Component
28073  * Bootstrap DocumentViewer class
28074  * @cfg {Boolean} showDownload (true|false) show download button (default true)
28075  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
28076  * 
28077  * @constructor
28078  * Create a new DocumentViewer
28079  * @param {Object} config The config object
28080  */
28081
28082 Roo.bootstrap.DocumentViewer = function(config){
28083     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
28084     
28085     this.addEvents({
28086         /**
28087          * @event initial
28088          * Fire after initEvent
28089          * @param {Roo.bootstrap.DocumentViewer} this
28090          */
28091         "initial" : true,
28092         /**
28093          * @event click
28094          * Fire after click
28095          * @param {Roo.bootstrap.DocumentViewer} this
28096          */
28097         "click" : true,
28098         /**
28099          * @event download
28100          * Fire after download button
28101          * @param {Roo.bootstrap.DocumentViewer} this
28102          */
28103         "download" : true,
28104         /**
28105          * @event trash
28106          * Fire after trash button
28107          * @param {Roo.bootstrap.DocumentViewer} this
28108          */
28109         "trash" : true
28110         
28111     });
28112 };
28113
28114 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
28115     
28116     showDownload : true,
28117     
28118     showTrash : true,
28119     
28120     getAutoCreate : function()
28121     {
28122         var cfg = {
28123             tag : 'div',
28124             cls : 'roo-document-viewer',
28125             cn : [
28126                 {
28127                     tag : 'div',
28128                     cls : 'roo-document-viewer-body',
28129                     cn : [
28130                         {
28131                             tag : 'div',
28132                             cls : 'roo-document-viewer-thumb',
28133                             cn : [
28134                                 {
28135                                     tag : 'img',
28136                                     cls : 'roo-document-viewer-image'
28137                                 }
28138                             ]
28139                         }
28140                     ]
28141                 },
28142                 {
28143                     tag : 'div',
28144                     cls : 'roo-document-viewer-footer',
28145                     cn : {
28146                         tag : 'div',
28147                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
28148                         cn : [
28149                             {
28150                                 tag : 'div',
28151                                 cls : 'btn-group roo-document-viewer-download',
28152                                 cn : [
28153                                     {
28154                                         tag : 'button',
28155                                         cls : 'btn btn-default',
28156                                         html : '<i class="fa fa-download"></i>'
28157                                     }
28158                                 ]
28159                             },
28160                             {
28161                                 tag : 'div',
28162                                 cls : 'btn-group roo-document-viewer-trash',
28163                                 cn : [
28164                                     {
28165                                         tag : 'button',
28166                                         cls : 'btn btn-default',
28167                                         html : '<i class="fa fa-trash"></i>'
28168                                     }
28169                                 ]
28170                             }
28171                         ]
28172                     }
28173                 }
28174             ]
28175         };
28176         
28177         return cfg;
28178     },
28179     
28180     initEvents : function()
28181     {
28182         
28183         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
28184         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
28185         
28186         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
28187         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
28188         
28189         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
28190         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
28191         
28192         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
28193         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
28194         
28195         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
28196         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
28197         
28198         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
28199         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
28200         
28201         this.bodyEl.on('click', this.onClick, this);
28202         this.downloadBtn.on('click', this.onDownload, this);
28203         this.trashBtn.on('click', this.onTrash, this);
28204         
28205         this.downloadBtn.hide();
28206         this.trashBtn.hide();
28207         
28208         if(this.showDownload){
28209             this.downloadBtn.show();
28210         }
28211         
28212         if(this.showTrash){
28213             this.trashBtn.show();
28214         }
28215         
28216         if(!this.showDownload && !this.showTrash) {
28217             this.footerEl.hide();
28218         }
28219         
28220     },
28221     
28222     initial : function()
28223     {
28224 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
28225         
28226         
28227         this.fireEvent('initial', this);
28228         
28229     },
28230     
28231     onClick : function(e)
28232     {
28233         e.preventDefault();
28234         
28235         this.fireEvent('click', this);
28236     },
28237     
28238     onDownload : function(e)
28239     {
28240         e.preventDefault();
28241         
28242         this.fireEvent('download', this);
28243     },
28244     
28245     onTrash : function(e)
28246     {
28247         e.preventDefault();
28248         
28249         this.fireEvent('trash', this);
28250     }
28251     
28252 });
28253 /*
28254  * - LGPL
28255  *
28256  * nav progress bar
28257  * 
28258  */
28259
28260 /**
28261  * @class Roo.bootstrap.NavProgressBar
28262  * @extends Roo.bootstrap.Component
28263  * Bootstrap NavProgressBar class
28264  * 
28265  * @constructor
28266  * Create a new nav progress bar
28267  * @param {Object} config The config object
28268  */
28269
28270 Roo.bootstrap.NavProgressBar = function(config){
28271     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
28272
28273     this.bullets = this.bullets || [];
28274    
28275 //    Roo.bootstrap.NavProgressBar.register(this);
28276      this.addEvents({
28277         /**
28278              * @event changed
28279              * Fires when the active item changes
28280              * @param {Roo.bootstrap.NavProgressBar} this
28281              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
28282              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
28283          */
28284         'changed': true
28285      });
28286     
28287 };
28288
28289 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
28290     
28291     bullets : [],
28292     barItems : [],
28293     
28294     getAutoCreate : function()
28295     {
28296         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
28297         
28298         cfg = {
28299             tag : 'div',
28300             cls : 'roo-navigation-bar-group',
28301             cn : [
28302                 {
28303                     tag : 'div',
28304                     cls : 'roo-navigation-top-bar'
28305                 },
28306                 {
28307                     tag : 'div',
28308                     cls : 'roo-navigation-bullets-bar',
28309                     cn : [
28310                         {
28311                             tag : 'ul',
28312                             cls : 'roo-navigation-bar'
28313                         }
28314                     ]
28315                 },
28316                 
28317                 {
28318                     tag : 'div',
28319                     cls : 'roo-navigation-bottom-bar'
28320                 }
28321             ]
28322             
28323         };
28324         
28325         return cfg;
28326         
28327     },
28328     
28329     initEvents: function() 
28330     {
28331         
28332     },
28333     
28334     onRender : function(ct, position) 
28335     {
28336         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28337         
28338         if(this.bullets.length){
28339             Roo.each(this.bullets, function(b){
28340                this.addItem(b);
28341             }, this);
28342         }
28343         
28344         this.format();
28345         
28346     },
28347     
28348     addItem : function(cfg)
28349     {
28350         var item = new Roo.bootstrap.NavProgressItem(cfg);
28351         
28352         item.parentId = this.id;
28353         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
28354         
28355         if(cfg.html){
28356             var top = new Roo.bootstrap.Element({
28357                 tag : 'div',
28358                 cls : 'roo-navigation-bar-text'
28359             });
28360             
28361             var bottom = new Roo.bootstrap.Element({
28362                 tag : 'div',
28363                 cls : 'roo-navigation-bar-text'
28364             });
28365             
28366             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
28367             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
28368             
28369             var topText = new Roo.bootstrap.Element({
28370                 tag : 'span',
28371                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
28372             });
28373             
28374             var bottomText = new Roo.bootstrap.Element({
28375                 tag : 'span',
28376                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
28377             });
28378             
28379             topText.onRender(top.el, null);
28380             bottomText.onRender(bottom.el, null);
28381             
28382             item.topEl = top;
28383             item.bottomEl = bottom;
28384         }
28385         
28386         this.barItems.push(item);
28387         
28388         return item;
28389     },
28390     
28391     getActive : function()
28392     {
28393         var active = false;
28394         
28395         Roo.each(this.barItems, function(v){
28396             
28397             if (!v.isActive()) {
28398                 return;
28399             }
28400             
28401             active = v;
28402             return false;
28403             
28404         });
28405         
28406         return active;
28407     },
28408     
28409     setActiveItem : function(item)
28410     {
28411         var prev = false;
28412         
28413         Roo.each(this.barItems, function(v){
28414             if (v.rid == item.rid) {
28415                 return ;
28416             }
28417             
28418             if (v.isActive()) {
28419                 v.setActive(false);
28420                 prev = v;
28421             }
28422         });
28423
28424         item.setActive(true);
28425         
28426         this.fireEvent('changed', this, item, prev);
28427     },
28428     
28429     getBarItem: function(rid)
28430     {
28431         var ret = false;
28432         
28433         Roo.each(this.barItems, function(e) {
28434             if (e.rid != rid) {
28435                 return;
28436             }
28437             
28438             ret =  e;
28439             return false;
28440         });
28441         
28442         return ret;
28443     },
28444     
28445     indexOfItem : function(item)
28446     {
28447         var index = false;
28448         
28449         Roo.each(this.barItems, function(v, i){
28450             
28451             if (v.rid != item.rid) {
28452                 return;
28453             }
28454             
28455             index = i;
28456             return false
28457         });
28458         
28459         return index;
28460     },
28461     
28462     setActiveNext : function()
28463     {
28464         var i = this.indexOfItem(this.getActive());
28465         
28466         if (i > this.barItems.length) {
28467             return;
28468         }
28469         
28470         this.setActiveItem(this.barItems[i+1]);
28471     },
28472     
28473     setActivePrev : function()
28474     {
28475         var i = this.indexOfItem(this.getActive());
28476         
28477         if (i  < 1) {
28478             return;
28479         }
28480         
28481         this.setActiveItem(this.barItems[i-1]);
28482     },
28483     
28484     format : function()
28485     {
28486         if(!this.barItems.length){
28487             return;
28488         }
28489      
28490         var width = 100 / this.barItems.length;
28491         
28492         Roo.each(this.barItems, function(i){
28493             i.el.setStyle('width', width + '%');
28494             i.topEl.el.setStyle('width', width + '%');
28495             i.bottomEl.el.setStyle('width', width + '%');
28496         }, this);
28497         
28498     }
28499     
28500 });
28501 /*
28502  * - LGPL
28503  *
28504  * Nav Progress Item
28505  * 
28506  */
28507
28508 /**
28509  * @class Roo.bootstrap.NavProgressItem
28510  * @extends Roo.bootstrap.Component
28511  * Bootstrap NavProgressItem class
28512  * @cfg {String} rid the reference id
28513  * @cfg {Boolean} active (true|false) Is item active default false
28514  * @cfg {Boolean} disabled (true|false) Is item active default false
28515  * @cfg {String} html
28516  * @cfg {String} position (top|bottom) text position default bottom
28517  * @cfg {String} icon show icon instead of number
28518  * 
28519  * @constructor
28520  * Create a new NavProgressItem
28521  * @param {Object} config The config object
28522  */
28523 Roo.bootstrap.NavProgressItem = function(config){
28524     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
28525     this.addEvents({
28526         // raw events
28527         /**
28528          * @event click
28529          * The raw click event for the entire grid.
28530          * @param {Roo.bootstrap.NavProgressItem} this
28531          * @param {Roo.EventObject} e
28532          */
28533         "click" : true
28534     });
28535    
28536 };
28537
28538 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
28539     
28540     rid : '',
28541     active : false,
28542     disabled : false,
28543     html : '',
28544     position : 'bottom',
28545     icon : false,
28546     
28547     getAutoCreate : function()
28548     {
28549         var iconCls = 'roo-navigation-bar-item-icon';
28550         
28551         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
28552         
28553         var cfg = {
28554             tag: 'li',
28555             cls: 'roo-navigation-bar-item',
28556             cn : [
28557                 {
28558                     tag : 'i',
28559                     cls : iconCls
28560                 }
28561             ]
28562         };
28563         
28564         if(this.active){
28565             cfg.cls += ' active';
28566         }
28567         if(this.disabled){
28568             cfg.cls += ' disabled';
28569         }
28570         
28571         return cfg;
28572     },
28573     
28574     disable : function()
28575     {
28576         this.setDisabled(true);
28577     },
28578     
28579     enable : function()
28580     {
28581         this.setDisabled(false);
28582     },
28583     
28584     initEvents: function() 
28585     {
28586         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
28587         
28588         this.iconEl.on('click', this.onClick, this);
28589     },
28590     
28591     onClick : function(e)
28592     {
28593         e.preventDefault();
28594         
28595         if(this.disabled){
28596             return;
28597         }
28598         
28599         if(this.fireEvent('click', this, e) === false){
28600             return;
28601         };
28602         
28603         this.parent().setActiveItem(this);
28604     },
28605     
28606     isActive: function () 
28607     {
28608         return this.active;
28609     },
28610     
28611     setActive : function(state)
28612     {
28613         if(this.active == state){
28614             return;
28615         }
28616         
28617         this.active = state;
28618         
28619         if (state) {
28620             this.el.addClass('active');
28621             return;
28622         }
28623         
28624         this.el.removeClass('active');
28625         
28626         return;
28627     },
28628     
28629     setDisabled : function(state)
28630     {
28631         if(this.disabled == state){
28632             return;
28633         }
28634         
28635         this.disabled = state;
28636         
28637         if (state) {
28638             this.el.addClass('disabled');
28639             return;
28640         }
28641         
28642         this.el.removeClass('disabled');
28643     },
28644     
28645     tooltipEl : function()
28646     {
28647         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
28648     }
28649 });
28650  
28651
28652  /*
28653  * - LGPL
28654  *
28655  * FieldLabel
28656  * 
28657  */
28658
28659 /**
28660  * @class Roo.bootstrap.FieldLabel
28661  * @extends Roo.bootstrap.Component
28662  * Bootstrap FieldLabel class
28663  * @cfg {String} html contents of the element
28664  * @cfg {String} tag tag of the element default label
28665  * @cfg {String} cls class of the element
28666  * @cfg {String} target label target 
28667  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
28668  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
28669  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
28670  * @cfg {String} iconTooltip default "This field is required"
28671  * 
28672  * @constructor
28673  * Create a new FieldLabel
28674  * @param {Object} config The config object
28675  */
28676
28677 Roo.bootstrap.FieldLabel = function(config){
28678     Roo.bootstrap.Element.superclass.constructor.call(this, config);
28679     
28680     this.addEvents({
28681             /**
28682              * @event invalid
28683              * Fires after the field has been marked as invalid.
28684              * @param {Roo.form.FieldLabel} this
28685              * @param {String} msg The validation message
28686              */
28687             invalid : true,
28688             /**
28689              * @event valid
28690              * Fires after the field has been validated with no errors.
28691              * @param {Roo.form.FieldLabel} this
28692              */
28693             valid : true
28694         });
28695 };
28696
28697 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
28698     
28699     tag: 'label',
28700     cls: '',
28701     html: '',
28702     target: '',
28703     allowBlank : true,
28704     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
28705     validClass : 'text-success fa fa-lg fa-check',
28706     iconTooltip : 'This field is required',
28707     
28708     getAutoCreate : function(){
28709         
28710         var cfg = {
28711             tag : this.tag,
28712             cls : 'roo-bootstrap-field-label ' + this.cls,
28713             for : this.target,
28714             cn : [
28715                 {
28716                     tag : 'i',
28717                     cls : '',
28718                     tooltip : this.iconTooltip
28719                 },
28720                 {
28721                     tag : 'span',
28722                     html : this.html
28723                 }
28724             ] 
28725         };
28726         
28727         return cfg;
28728     },
28729     
28730     initEvents: function() 
28731     {
28732         Roo.bootstrap.Element.superclass.initEvents.call(this);
28733         
28734         this.iconEl = this.el.select('i', true).first();
28735         
28736         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
28737         
28738         Roo.bootstrap.FieldLabel.register(this);
28739     },
28740     
28741     /**
28742      * Mark this field as valid
28743      */
28744     markValid : function()
28745     {
28746         this.iconEl.show();
28747         
28748         this.iconEl.removeClass(this.invalidClass);
28749         
28750         this.iconEl.addClass(this.validClass);
28751         
28752         this.fireEvent('valid', this);
28753     },
28754     
28755     /**
28756      * Mark this field as invalid
28757      * @param {String} msg The validation message
28758      */
28759     markInvalid : function(msg)
28760     {
28761         this.iconEl.show();
28762         
28763         this.iconEl.removeClass(this.validClass);
28764         
28765         this.iconEl.addClass(this.invalidClass);
28766         
28767         this.fireEvent('invalid', this, msg);
28768     }
28769     
28770    
28771 });
28772
28773 Roo.apply(Roo.bootstrap.FieldLabel, {
28774     
28775     groups: {},
28776     
28777      /**
28778     * register a FieldLabel Group
28779     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28780     */
28781     register : function(label)
28782     {
28783         if(this.groups.hasOwnProperty(label.target)){
28784             return;
28785         }
28786      
28787         this.groups[label.target] = label;
28788         
28789     },
28790     /**
28791     * fetch a FieldLabel Group based on the target
28792     * @param {string} target
28793     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28794     */
28795     get: function(target) {
28796         if (typeof(this.groups[target]) == 'undefined') {
28797             return false;
28798         }
28799         
28800         return this.groups[target] ;
28801     }
28802 });
28803
28804  
28805
28806  /*
28807  * - LGPL
28808  *
28809  * page DateSplitField.
28810  * 
28811  */
28812
28813
28814 /**
28815  * @class Roo.bootstrap.DateSplitField
28816  * @extends Roo.bootstrap.Component
28817  * Bootstrap DateSplitField class
28818  * @cfg {string} fieldLabel - the label associated
28819  * @cfg {Number} labelWidth set the width of label (0-12)
28820  * @cfg {String} labelAlign (top|left)
28821  * @cfg {Boolean} dayAllowBlank (true|false) default false
28822  * @cfg {Boolean} monthAllowBlank (true|false) default false
28823  * @cfg {Boolean} yearAllowBlank (true|false) default false
28824  * @cfg {string} dayPlaceholder 
28825  * @cfg {string} monthPlaceholder
28826  * @cfg {string} yearPlaceholder
28827  * @cfg {string} dayFormat default 'd'
28828  * @cfg {string} monthFormat default 'm'
28829  * @cfg {string} yearFormat default 'Y'
28830
28831  *     
28832  * @constructor
28833  * Create a new DateSplitField
28834  * @param {Object} config The config object
28835  */
28836
28837 Roo.bootstrap.DateSplitField = function(config){
28838     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28839     
28840     this.addEvents({
28841         // raw events
28842          /**
28843          * @event years
28844          * getting the data of years
28845          * @param {Roo.bootstrap.DateSplitField} this
28846          * @param {Object} years
28847          */
28848         "years" : true,
28849         /**
28850          * @event days
28851          * getting the data of days
28852          * @param {Roo.bootstrap.DateSplitField} this
28853          * @param {Object} days
28854          */
28855         "days" : true,
28856         /**
28857          * @event invalid
28858          * Fires after the field has been marked as invalid.
28859          * @param {Roo.form.Field} this
28860          * @param {String} msg The validation message
28861          */
28862         invalid : true,
28863        /**
28864          * @event valid
28865          * Fires after the field has been validated with no errors.
28866          * @param {Roo.form.Field} this
28867          */
28868         valid : true
28869     });
28870 };
28871
28872 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
28873     
28874     fieldLabel : '',
28875     labelAlign : 'top',
28876     labelWidth : 3,
28877     dayAllowBlank : false,
28878     monthAllowBlank : false,
28879     yearAllowBlank : false,
28880     dayPlaceholder : '',
28881     monthPlaceholder : '',
28882     yearPlaceholder : '',
28883     dayFormat : 'd',
28884     monthFormat : 'm',
28885     yearFormat : 'Y',
28886     isFormField : true,
28887     
28888     getAutoCreate : function()
28889     {
28890         var cfg = {
28891             tag : 'div',
28892             cls : 'row roo-date-split-field-group',
28893             cn : [
28894                 {
28895                     tag : 'input',
28896                     type : 'hidden',
28897                     cls : 'form-hidden-field roo-date-split-field-group-value',
28898                     name : this.name
28899                 }
28900             ]
28901         };
28902         
28903         if(this.fieldLabel){
28904             cfg.cn.push({
28905                 tag : 'div',
28906                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
28907                 cn : [
28908                     {
28909                         tag : 'label',
28910                         html : this.fieldLabel
28911                     }
28912                 ]
28913             });
28914         }
28915         
28916         Roo.each(['day', 'month', 'year'], function(t){
28917             cfg.cn.push({
28918                 tag : 'div',
28919                 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
28920             });
28921         }, this);
28922         
28923         return cfg;
28924     },
28925     
28926     inputEl: function ()
28927     {
28928         return this.el.select('.roo-date-split-field-group-value', true).first();
28929     },
28930     
28931     onRender : function(ct, position) 
28932     {
28933         var _this = this;
28934         
28935         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28936         
28937         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
28938         
28939         this.dayField = new Roo.bootstrap.ComboBox({
28940             allowBlank : this.dayAllowBlank,
28941             alwaysQuery : true,
28942             displayField : 'value',
28943             editable : false,
28944             fieldLabel : '',
28945             forceSelection : true,
28946             mode : 'local',
28947             placeholder : this.dayPlaceholder,
28948             selectOnFocus : true,
28949             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28950             triggerAction : 'all',
28951             typeAhead : true,
28952             valueField : 'value',
28953             store : new Roo.data.SimpleStore({
28954                 data : (function() {    
28955                     var days = [];
28956                     _this.fireEvent('days', _this, days);
28957                     return days;
28958                 })(),
28959                 fields : [ 'value' ]
28960             }),
28961             listeners : {
28962                 select : function (_self, record, index)
28963                 {
28964                     _this.setValue(_this.getValue());
28965                 }
28966             }
28967         });
28968
28969         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
28970         
28971         this.monthField = new Roo.bootstrap.MonthField({
28972             after : '<i class=\"fa fa-calendar\"></i>',
28973             allowBlank : this.monthAllowBlank,
28974             placeholder : this.monthPlaceholder,
28975             readOnly : true,
28976             listeners : {
28977                 render : function (_self)
28978                 {
28979                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
28980                         e.preventDefault();
28981                         _self.focus();
28982                     });
28983                 },
28984                 select : function (_self, oldvalue, newvalue)
28985                 {
28986                     _this.setValue(_this.getValue());
28987                 }
28988             }
28989         });
28990         
28991         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
28992         
28993         this.yearField = new Roo.bootstrap.ComboBox({
28994             allowBlank : this.yearAllowBlank,
28995             alwaysQuery : true,
28996             displayField : 'value',
28997             editable : false,
28998             fieldLabel : '',
28999             forceSelection : true,
29000             mode : 'local',
29001             placeholder : this.yearPlaceholder,
29002             selectOnFocus : true,
29003             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29004             triggerAction : 'all',
29005             typeAhead : true,
29006             valueField : 'value',
29007             store : new Roo.data.SimpleStore({
29008                 data : (function() {
29009                     var years = [];
29010                     _this.fireEvent('years', _this, years);
29011                     return years;
29012                 })(),
29013                 fields : [ 'value' ]
29014             }),
29015             listeners : {
29016                 select : function (_self, record, index)
29017                 {
29018                     _this.setValue(_this.getValue());
29019                 }
29020             }
29021         });
29022
29023         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
29024     },
29025     
29026     setValue : function(v, format)
29027     {
29028         this.inputEl.dom.value = v;
29029         
29030         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
29031         
29032         var d = Date.parseDate(v, f);
29033         
29034         if(!d){
29035             this.validate();
29036             return;
29037         }
29038         
29039         this.setDay(d.format(this.dayFormat));
29040         this.setMonth(d.format(this.monthFormat));
29041         this.setYear(d.format(this.yearFormat));
29042         
29043         this.validate();
29044         
29045         return;
29046     },
29047     
29048     setDay : function(v)
29049     {
29050         this.dayField.setValue(v);
29051         this.inputEl.dom.value = this.getValue();
29052         this.validate();
29053         return;
29054     },
29055     
29056     setMonth : function(v)
29057     {
29058         this.monthField.setValue(v, true);
29059         this.inputEl.dom.value = this.getValue();
29060         this.validate();
29061         return;
29062     },
29063     
29064     setYear : function(v)
29065     {
29066         this.yearField.setValue(v);
29067         this.inputEl.dom.value = this.getValue();
29068         this.validate();
29069         return;
29070     },
29071     
29072     getDay : function()
29073     {
29074         return this.dayField.getValue();
29075     },
29076     
29077     getMonth : function()
29078     {
29079         return this.monthField.getValue();
29080     },
29081     
29082     getYear : function()
29083     {
29084         return this.yearField.getValue();
29085     },
29086     
29087     getValue : function()
29088     {
29089         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
29090         
29091         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
29092         
29093         return date;
29094     },
29095     
29096     reset : function()
29097     {
29098         this.setDay('');
29099         this.setMonth('');
29100         this.setYear('');
29101         this.inputEl.dom.value = '';
29102         this.validate();
29103         return;
29104     },
29105     
29106     validate : function()
29107     {
29108         var d = this.dayField.validate();
29109         var m = this.monthField.validate();
29110         var y = this.yearField.validate();
29111         
29112         var valid = true;
29113         
29114         if(
29115                 (!this.dayAllowBlank && !d) ||
29116                 (!this.monthAllowBlank && !m) ||
29117                 (!this.yearAllowBlank && !y)
29118         ){
29119             valid = false;
29120         }
29121         
29122         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
29123             return valid;
29124         }
29125         
29126         if(valid){
29127             this.markValid();
29128             return valid;
29129         }
29130         
29131         this.markInvalid();
29132         
29133         return valid;
29134     },
29135     
29136     markValid : function()
29137     {
29138         
29139         var label = this.el.select('label', true).first();
29140         var icon = this.el.select('i.fa-star', true).first();
29141
29142         if(label && icon){
29143             icon.remove();
29144         }
29145         
29146         this.fireEvent('valid', this);
29147     },
29148     
29149      /**
29150      * Mark this field as invalid
29151      * @param {String} msg The validation message
29152      */
29153     markInvalid : function(msg)
29154     {
29155         
29156         var label = this.el.select('label', true).first();
29157         var icon = this.el.select('i.fa-star', true).first();
29158
29159         if(label && !icon){
29160             this.el.select('.roo-date-split-field-label', true).createChild({
29161                 tag : 'i',
29162                 cls : 'text-danger fa fa-lg fa-star',
29163                 tooltip : 'This field is required',
29164                 style : 'margin-right:5px;'
29165             }, label, true);
29166         }
29167         
29168         this.fireEvent('invalid', this, msg);
29169     },
29170     
29171     clearInvalid : function()
29172     {
29173         var label = this.el.select('label', true).first();
29174         var icon = this.el.select('i.fa-star', true).first();
29175
29176         if(label && icon){
29177             icon.remove();
29178         }
29179         
29180         this.fireEvent('valid', this);
29181     },
29182     
29183     getName: function()
29184     {
29185         return this.name;
29186     }
29187     
29188 });
29189
29190  /**
29191  *
29192  * This is based on 
29193  * http://masonry.desandro.com
29194  *
29195  * The idea is to render all the bricks based on vertical width...
29196  *
29197  * The original code extends 'outlayer' - we might need to use that....
29198  * 
29199  */
29200
29201
29202 /**
29203  * @class Roo.bootstrap.LayoutMasonry
29204  * @extends Roo.bootstrap.Component
29205  * Bootstrap Layout Masonry class
29206  * 
29207  * @constructor
29208  * Create a new Element
29209  * @param {Object} config The config object
29210  */
29211
29212 Roo.bootstrap.LayoutMasonry = function(config){
29213     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
29214     
29215     this.bricks = [];
29216     
29217 };
29218
29219 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
29220     
29221     /**
29222      * @cfg {Boolean} isLayoutInstant = no animation?
29223      */   
29224     isLayoutInstant : false, // needed?
29225    
29226     /**
29227      * @cfg {Number} boxWidth  width of the columns
29228      */   
29229     boxWidth : 450,
29230     
29231       /**
29232      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
29233      */   
29234     boxHeight : 0,
29235     
29236     /**
29237      * @cfg {Number} padWidth padding below box..
29238      */   
29239     padWidth : 10, 
29240     
29241     /**
29242      * @cfg {Number} gutter gutter width..
29243      */   
29244     gutter : 10,
29245     
29246      /**
29247      * @cfg {Number} maxCols maximum number of columns
29248      */   
29249     
29250     maxCols: 0,
29251     
29252     /**
29253      * @cfg {Boolean} isAutoInitial defalut true
29254      */   
29255     isAutoInitial : true, 
29256     
29257     containerWidth: 0,
29258     
29259     /**
29260      * @cfg {Boolean} isHorizontal defalut false
29261      */   
29262     isHorizontal : false, 
29263
29264     currentSize : null,
29265     
29266     tag: 'div',
29267     
29268     cls: '',
29269     
29270     bricks: null, //CompositeElement
29271     
29272     cols : 1,
29273     
29274     _isLayoutInited : false,
29275     
29276 //    isAlternative : false, // only use for vertical layout...
29277     
29278     /**
29279      * @cfg {Number} alternativePadWidth padding below box..
29280      */   
29281     alternativePadWidth : 50, 
29282     
29283     getAutoCreate : function(){
29284         
29285         var cfg = {
29286             tag: this.tag,
29287             cls: 'blog-masonary-wrapper ' + this.cls,
29288             cn : {
29289                 cls : 'mas-boxes masonary'
29290             }
29291         };
29292         
29293         return cfg;
29294     },
29295     
29296     getChildContainer: function( )
29297     {
29298         if (this.boxesEl) {
29299             return this.boxesEl;
29300         }
29301         
29302         this.boxesEl = this.el.select('.mas-boxes').first();
29303         
29304         return this.boxesEl;
29305     },
29306     
29307     
29308     initEvents : function()
29309     {
29310         var _this = this;
29311         
29312         if(this.isAutoInitial){
29313             Roo.log('hook children rendered');
29314             this.on('childrenrendered', function() {
29315                 Roo.log('children rendered');
29316                 _this.initial();
29317             } ,this);
29318         }
29319     },
29320     
29321     initial : function()
29322     {
29323         this.currentSize = this.el.getBox(true);
29324         
29325         Roo.EventManager.onWindowResize(this.resize, this); 
29326
29327         if(!this.isAutoInitial){
29328             this.layout();
29329             return;
29330         }
29331         
29332         this.layout();
29333         
29334         return;
29335         //this.layout.defer(500,this);
29336         
29337     },
29338     
29339     resize : function()
29340     {
29341         Roo.log('resize');
29342         
29343         var cs = this.el.getBox(true);
29344         
29345         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29346             Roo.log("no change in with or X");
29347             return;
29348         }
29349         
29350         this.currentSize = cs;
29351         
29352         this.layout();
29353         
29354     },
29355     
29356     layout : function()
29357     {   
29358         this._resetLayout();
29359         
29360         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29361         
29362         this.layoutItems( isInstant );
29363       
29364         this._isLayoutInited = true;
29365         
29366     },
29367     
29368     _resetLayout : function()
29369     {
29370         if(this.isHorizontal){
29371             this.horizontalMeasureColumns();
29372             return;
29373         }
29374         
29375         this.verticalMeasureColumns();
29376         
29377     },
29378     
29379     verticalMeasureColumns : function()
29380     {
29381         this.getContainerWidth();
29382         
29383 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29384 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
29385 //            return;
29386 //        }
29387         
29388         var boxWidth = this.boxWidth + this.padWidth;
29389         
29390         if(this.containerWidth < this.boxWidth){
29391             boxWidth = this.containerWidth
29392         }
29393         
29394         var containerWidth = this.containerWidth;
29395         
29396         var cols = Math.floor(containerWidth / boxWidth);
29397         
29398         this.cols = Math.max( cols, 1 );
29399         
29400         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
29401         
29402         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
29403         
29404         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
29405         
29406         this.colWidth = boxWidth + avail - this.padWidth;
29407         
29408         this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
29409         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
29410     },
29411     
29412     horizontalMeasureColumns : function()
29413     {
29414         this.getContainerWidth();
29415         
29416         var boxWidth = this.boxWidth;
29417         
29418         if(this.containerWidth < boxWidth){
29419             boxWidth = this.containerWidth;
29420         }
29421         
29422         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
29423         
29424         this.el.setHeight(boxWidth);
29425         
29426     },
29427     
29428     getContainerWidth : function()
29429     {
29430         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
29431     },
29432     
29433     layoutItems : function( isInstant )
29434     {
29435         var items = Roo.apply([], this.bricks);
29436         
29437         if(this.isHorizontal){
29438             this._horizontalLayoutItems( items , isInstant );
29439             return;
29440         }
29441         
29442 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29443 //            this._verticalAlternativeLayoutItems( items , isInstant );
29444 //            return;
29445 //        }
29446         
29447         this._verticalLayoutItems( items , isInstant );
29448         
29449     },
29450     
29451     _verticalLayoutItems : function ( items , isInstant)
29452     {
29453         if ( !items || !items.length ) {
29454             return;
29455         }
29456         
29457         var standard = [
29458             ['xs', 'xs', 'xs', 'tall'],
29459             ['xs', 'xs', 'tall'],
29460             ['xs', 'xs', 'sm'],
29461             ['xs', 'xs', 'xs'],
29462             ['xs', 'tall'],
29463             ['xs', 'sm'],
29464             ['xs', 'xs'],
29465             ['xs'],
29466             
29467             ['sm', 'xs', 'xs'],
29468             ['sm', 'xs'],
29469             ['sm'],
29470             
29471             ['tall', 'xs', 'xs', 'xs'],
29472             ['tall', 'xs', 'xs'],
29473             ['tall', 'xs'],
29474             ['tall']
29475             
29476         ];
29477         
29478         var queue = [];
29479         
29480         var boxes = [];
29481         
29482         var box = [];
29483         
29484         Roo.each(items, function(item, k){
29485             
29486             switch (item.size) {
29487                 // these layouts take up a full box,
29488                 case 'md' :
29489                 case 'md-left' :
29490                 case 'md-right' :
29491                 case 'wide' :
29492                     
29493                     if(box.length){
29494                         boxes.push(box);
29495                         box = [];
29496                     }
29497                     
29498                     boxes.push([item]);
29499                     
29500                     break;
29501                     
29502                 case 'xs' :
29503                 case 'sm' :
29504                 case 'tall' :
29505                     
29506                     box.push(item);
29507                     
29508                     break;
29509                 default :
29510                     break;
29511                     
29512             }
29513             
29514         }, this);
29515         
29516         if(box.length){
29517             boxes.push(box);
29518             box = [];
29519         }
29520         
29521         var filterPattern = function(box, length)
29522         {
29523             if(!box.length){
29524                 return;
29525             }
29526             
29527             var match = false;
29528             
29529             var pattern = box.slice(0, length);
29530             
29531             var format = [];
29532             
29533             Roo.each(pattern, function(i){
29534                 format.push(i.size);
29535             }, this);
29536             
29537             Roo.each(standard, function(s){
29538                 
29539                 if(String(s) != String(format)){
29540                     return;
29541                 }
29542                 
29543                 match = true;
29544                 return false;
29545                 
29546             }, this);
29547             
29548             if(!match && length == 1){
29549                 return;
29550             }
29551             
29552             if(!match){
29553                 filterPattern(box, length - 1);
29554                 return;
29555             }
29556                 
29557             queue.push(pattern);
29558
29559             box = box.slice(length, box.length);
29560
29561             filterPattern(box, 4);
29562
29563             return;
29564             
29565         }
29566         
29567         Roo.each(boxes, function(box, k){
29568             
29569             if(!box.length){
29570                 return;
29571             }
29572             
29573             if(box.length == 1){
29574                 queue.push(box);
29575                 return;
29576             }
29577             
29578             filterPattern(box, 4);
29579             
29580         }, this);
29581         
29582         this._processVerticalLayoutQueue( queue, isInstant );
29583         
29584     },
29585     
29586 //    _verticalAlternativeLayoutItems : function( items , isInstant )
29587 //    {
29588 //        if ( !items || !items.length ) {
29589 //            return;
29590 //        }
29591 //
29592 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
29593 //        
29594 //    },
29595     
29596     _horizontalLayoutItems : function ( items , isInstant)
29597     {
29598         if ( !items || !items.length || items.length < 3) {
29599             return;
29600         }
29601         
29602         items.reverse();
29603         
29604         var eItems = items.slice(0, 3);
29605         
29606         items = items.slice(3, items.length);
29607         
29608         var standard = [
29609             ['xs', 'xs', 'xs', 'wide'],
29610             ['xs', 'xs', 'wide'],
29611             ['xs', 'xs', 'sm'],
29612             ['xs', 'xs', 'xs'],
29613             ['xs', 'wide'],
29614             ['xs', 'sm'],
29615             ['xs', 'xs'],
29616             ['xs'],
29617             
29618             ['sm', 'xs', 'xs'],
29619             ['sm', 'xs'],
29620             ['sm'],
29621             
29622             ['wide', 'xs', 'xs', 'xs'],
29623             ['wide', 'xs', 'xs'],
29624             ['wide', 'xs'],
29625             ['wide'],
29626             
29627             ['wide-thin']
29628         ];
29629         
29630         var queue = [];
29631         
29632         var boxes = [];
29633         
29634         var box = [];
29635         
29636         Roo.each(items, function(item, k){
29637             
29638             switch (item.size) {
29639                 case 'md' :
29640                 case 'md-left' :
29641                 case 'md-right' :
29642                 case 'tall' :
29643                     
29644                     if(box.length){
29645                         boxes.push(box);
29646                         box = [];
29647                     }
29648                     
29649                     boxes.push([item]);
29650                     
29651                     break;
29652                     
29653                 case 'xs' :
29654                 case 'sm' :
29655                 case 'wide' :
29656                 case 'wide-thin' :
29657                     
29658                     box.push(item);
29659                     
29660                     break;
29661                 default :
29662                     break;
29663                     
29664             }
29665             
29666         }, this);
29667         
29668         if(box.length){
29669             boxes.push(box);
29670             box = [];
29671         }
29672         
29673         var filterPattern = function(box, length)
29674         {
29675             if(!box.length){
29676                 return;
29677             }
29678             
29679             var match = false;
29680             
29681             var pattern = box.slice(0, length);
29682             
29683             var format = [];
29684             
29685             Roo.each(pattern, function(i){
29686                 format.push(i.size);
29687             }, this);
29688             
29689             Roo.each(standard, function(s){
29690                 
29691                 if(String(s) != String(format)){
29692                     return;
29693                 }
29694                 
29695                 match = true;
29696                 return false;
29697                 
29698             }, this);
29699             
29700             if(!match && length == 1){
29701                 return;
29702             }
29703             
29704             if(!match){
29705                 filterPattern(box, length - 1);
29706                 return;
29707             }
29708                 
29709             queue.push(pattern);
29710
29711             box = box.slice(length, box.length);
29712
29713             filterPattern(box, 4);
29714
29715             return;
29716             
29717         }
29718         
29719         Roo.each(boxes, function(box, k){
29720             
29721             if(!box.length){
29722                 return;
29723             }
29724             
29725             if(box.length == 1){
29726                 queue.push(box);
29727                 return;
29728             }
29729             
29730             filterPattern(box, 4);
29731             
29732         }, this);
29733         
29734         
29735         var prune = [];
29736         
29737         var pos = this.el.getBox(true);
29738         
29739         var minX = pos.x;
29740         
29741         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29742         
29743         var hit_end = false;
29744         
29745         Roo.each(queue, function(box){
29746             
29747             if(hit_end){
29748                 
29749                 Roo.each(box, function(b){
29750                 
29751                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29752                     b.el.hide();
29753
29754                 }, this);
29755
29756                 return;
29757             }
29758             
29759             var mx = 0;
29760             
29761             Roo.each(box, function(b){
29762                 
29763                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29764                 b.el.show();
29765
29766                 mx = Math.max(mx, b.x);
29767                 
29768             }, this);
29769             
29770             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29771             
29772             if(maxX < minX){
29773                 
29774                 Roo.each(box, function(b){
29775                 
29776                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29777                     b.el.hide();
29778                     
29779                 }, this);
29780                 
29781                 hit_end = true;
29782                 
29783                 return;
29784             }
29785             
29786             prune.push(box);
29787             
29788         }, this);
29789         
29790         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29791     },
29792     
29793     /** Sets position of item in DOM
29794     * @param {Element} item
29795     * @param {Number} x - horizontal position
29796     * @param {Number} y - vertical position
29797     * @param {Boolean} isInstant - disables transitions
29798     */
29799     _processVerticalLayoutQueue : function( queue, isInstant )
29800     {
29801         var pos = this.el.getBox(true);
29802         var x = pos.x;
29803         var y = pos.y;
29804         var maxY = [];
29805         
29806         for (var i = 0; i < this.cols; i++){
29807             maxY[i] = pos.y;
29808         }
29809         
29810         Roo.each(queue, function(box, k){
29811             
29812             var col = k % this.cols;
29813             
29814             Roo.each(box, function(b,kk){
29815                 
29816                 b.el.position('absolute');
29817                 
29818                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29819                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29820                 
29821                 if(b.size == 'md-left' || b.size == 'md-right'){
29822                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29823                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29824                 }
29825                 
29826                 b.el.setWidth(width);
29827                 b.el.setHeight(height);
29828                 // iframe?
29829                 b.el.select('iframe',true).setSize(width,height);
29830                 
29831             }, this);
29832             
29833             for (var i = 0; i < this.cols; i++){
29834                 
29835                 if(maxY[i] < maxY[col]){
29836                     col = i;
29837                     continue;
29838                 }
29839                 
29840                 col = Math.min(col, i);
29841                 
29842             }
29843             
29844             x = pos.x + col * (this.colWidth + this.padWidth);
29845             
29846             y = maxY[col];
29847             
29848             var positions = [];
29849             
29850             switch (box.length){
29851                 case 1 :
29852                     positions = this.getVerticalOneBoxColPositions(x, y, box);
29853                     break;
29854                 case 2 :
29855                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
29856                     break;
29857                 case 3 :
29858                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
29859                     break;
29860                 case 4 :
29861                     positions = this.getVerticalFourBoxColPositions(x, y, box);
29862                     break;
29863                 default :
29864                     break;
29865             }
29866             
29867             Roo.each(box, function(b,kk){
29868                 
29869                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29870                 
29871                 var sz = b.el.getSize();
29872                 
29873                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29874                 
29875             }, this);
29876             
29877         }, this);
29878         
29879         var mY = 0;
29880         
29881         for (var i = 0; i < this.cols; i++){
29882             mY = Math.max(mY, maxY[i]);
29883         }
29884         
29885         this.el.setHeight(mY - pos.y);
29886         
29887     },
29888     
29889 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29890 //    {
29891 //        var pos = this.el.getBox(true);
29892 //        var x = pos.x;
29893 //        var y = pos.y;
29894 //        var maxX = pos.right;
29895 //        
29896 //        var maxHeight = 0;
29897 //        
29898 //        Roo.each(items, function(item, k){
29899 //            
29900 //            var c = k % 2;
29901 //            
29902 //            item.el.position('absolute');
29903 //                
29904 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29905 //
29906 //            item.el.setWidth(width);
29907 //
29908 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
29909 //
29910 //            item.el.setHeight(height);
29911 //            
29912 //            if(c == 0){
29913 //                item.el.setXY([x, y], isInstant ? false : true);
29914 //            } else {
29915 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
29916 //            }
29917 //            
29918 //            y = y + height + this.alternativePadWidth;
29919 //            
29920 //            maxHeight = maxHeight + height + this.alternativePadWidth;
29921 //            
29922 //        }, this);
29923 //        
29924 //        this.el.setHeight(maxHeight);
29925 //        
29926 //    },
29927     
29928     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
29929     {
29930         var pos = this.el.getBox(true);
29931         
29932         var minX = pos.x;
29933         var minY = pos.y;
29934         
29935         var maxX = pos.right;
29936         
29937         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
29938         
29939         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29940         
29941         Roo.each(queue, function(box, k){
29942             
29943             Roo.each(box, function(b, kk){
29944                 
29945                 b.el.position('absolute');
29946                 
29947                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29948                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29949                 
29950                 if(b.size == 'md-left' || b.size == 'md-right'){
29951                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29952                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29953                 }
29954                 
29955                 b.el.setWidth(width);
29956                 b.el.setHeight(height);
29957                 
29958             }, this);
29959             
29960             if(!box.length){
29961                 return;
29962             }
29963             
29964             var positions = [];
29965             
29966             switch (box.length){
29967                 case 1 :
29968                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
29969                     break;
29970                 case 2 :
29971                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
29972                     break;
29973                 case 3 :
29974                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
29975                     break;
29976                 case 4 :
29977                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
29978                     break;
29979                 default :
29980                     break;
29981             }
29982             
29983             Roo.each(box, function(b,kk){
29984                 
29985                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29986                 
29987                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
29988                 
29989             }, this);
29990             
29991         }, this);
29992         
29993     },
29994     
29995     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
29996     {
29997         Roo.each(eItems, function(b,k){
29998             
29999             b.size = (k == 0) ? 'sm' : 'xs';
30000             b.x = (k == 0) ? 2 : 1;
30001             b.y = (k == 0) ? 2 : 1;
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                 
30007             b.el.setWidth(width);
30008             
30009             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30010             
30011             b.el.setHeight(height);
30012             
30013         }, this);
30014
30015         var positions = [];
30016         
30017         positions.push({
30018             x : maxX - this.unitWidth * 2 - this.gutter,
30019             y : minY
30020         });
30021         
30022         positions.push({
30023             x : maxX - this.unitWidth,
30024             y : minY + (this.unitWidth + this.gutter) * 2
30025         });
30026         
30027         positions.push({
30028             x : maxX - this.unitWidth * 3 - this.gutter * 2,
30029             y : minY
30030         });
30031         
30032         Roo.each(eItems, function(b,k){
30033             
30034             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
30035
30036         }, this);
30037         
30038     },
30039     
30040     getVerticalOneBoxColPositions : function(x, y, box)
30041     {
30042         var pos = [];
30043         
30044         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
30045         
30046         if(box[0].size == 'md-left'){
30047             rand = 0;
30048         }
30049         
30050         if(box[0].size == 'md-right'){
30051             rand = 1;
30052         }
30053         
30054         pos.push({
30055             x : x + (this.unitWidth + this.gutter) * rand,
30056             y : y
30057         });
30058         
30059         return pos;
30060     },
30061     
30062     getVerticalTwoBoxColPositions : function(x, y, box)
30063     {
30064         var pos = [];
30065         
30066         if(box[0].size == 'xs'){
30067             
30068             pos.push({
30069                 x : x,
30070                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
30071             });
30072
30073             pos.push({
30074                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
30075                 y : y
30076             });
30077             
30078             return pos;
30079             
30080         }
30081         
30082         pos.push({
30083             x : x,
30084             y : y
30085         });
30086
30087         pos.push({
30088             x : x + (this.unitWidth + this.gutter) * 2,
30089             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
30090         });
30091         
30092         return pos;
30093         
30094     },
30095     
30096     getVerticalThreeBoxColPositions : function(x, y, box)
30097     {
30098         var pos = [];
30099         
30100         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30101             
30102             pos.push({
30103                 x : x,
30104                 y : y
30105             });
30106
30107             pos.push({
30108                 x : x + (this.unitWidth + this.gutter) * 1,
30109                 y : y
30110             });
30111             
30112             pos.push({
30113                 x : x + (this.unitWidth + this.gutter) * 2,
30114                 y : y
30115             });
30116             
30117             return pos;
30118             
30119         }
30120         
30121         if(box[0].size == 'xs' && box[1].size == 'xs'){
30122             
30123             pos.push({
30124                 x : x,
30125                 y : y
30126             });
30127
30128             pos.push({
30129                 x : x,
30130                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
30131             });
30132             
30133             pos.push({
30134                 x : x + (this.unitWidth + this.gutter) * 1,
30135                 y : y
30136             });
30137             
30138             return pos;
30139             
30140         }
30141         
30142         pos.push({
30143             x : x,
30144             y : y
30145         });
30146
30147         pos.push({
30148             x : x + (this.unitWidth + this.gutter) * 2,
30149             y : y
30150         });
30151
30152         pos.push({
30153             x : x + (this.unitWidth + this.gutter) * 2,
30154             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
30155         });
30156             
30157         return pos;
30158         
30159     },
30160     
30161     getVerticalFourBoxColPositions : function(x, y, box)
30162     {
30163         var pos = [];
30164         
30165         if(box[0].size == 'xs'){
30166             
30167             pos.push({
30168                 x : x,
30169                 y : y
30170             });
30171
30172             pos.push({
30173                 x : x,
30174                 y : y + (this.unitHeight + this.gutter) * 1
30175             });
30176             
30177             pos.push({
30178                 x : x,
30179                 y : y + (this.unitHeight + this.gutter) * 2
30180             });
30181             
30182             pos.push({
30183                 x : x + (this.unitWidth + this.gutter) * 1,
30184                 y : y
30185             });
30186             
30187             return pos;
30188             
30189         }
30190         
30191         pos.push({
30192             x : x,
30193             y : y
30194         });
30195
30196         pos.push({
30197             x : x + (this.unitWidth + this.gutter) * 2,
30198             y : y
30199         });
30200
30201         pos.push({
30202             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
30203             y : y + (this.unitHeight + this.gutter) * 1
30204         });
30205
30206         pos.push({
30207             x : x + (this.unitWidth + this.gutter) * 2,
30208             y : y + (this.unitWidth + this.gutter) * 2
30209         });
30210
30211         return pos;
30212         
30213     },
30214     
30215     getHorizontalOneBoxColPositions : function(maxX, minY, box)
30216     {
30217         var pos = [];
30218         
30219         if(box[0].size == 'md-left'){
30220             pos.push({
30221                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30222                 y : minY
30223             });
30224             
30225             return pos;
30226         }
30227         
30228         if(box[0].size == 'md-right'){
30229             pos.push({
30230                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30231                 y : minY + (this.unitWidth + this.gutter) * 1
30232             });
30233             
30234             return pos;
30235         }
30236         
30237         var rand = Math.floor(Math.random() * (4 - box[0].y));
30238         
30239         pos.push({
30240             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30241             y : minY + (this.unitWidth + this.gutter) * rand
30242         });
30243         
30244         return pos;
30245         
30246     },
30247     
30248     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
30249     {
30250         var pos = [];
30251         
30252         if(box[0].size == 'xs'){
30253             
30254             pos.push({
30255                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30256                 y : minY
30257             });
30258
30259             pos.push({
30260                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30261                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
30262             });
30263             
30264             return pos;
30265             
30266         }
30267         
30268         pos.push({
30269             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30270             y : minY
30271         });
30272
30273         pos.push({
30274             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30275             y : minY + (this.unitWidth + this.gutter) * 2
30276         });
30277         
30278         return pos;
30279         
30280     },
30281     
30282     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
30283     {
30284         var pos = [];
30285         
30286         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30287             
30288             pos.push({
30289                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30290                 y : minY
30291             });
30292
30293             pos.push({
30294                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30295                 y : minY + (this.unitWidth + this.gutter) * 1
30296             });
30297             
30298             pos.push({
30299                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30300                 y : minY + (this.unitWidth + this.gutter) * 2
30301             });
30302             
30303             return pos;
30304             
30305         }
30306         
30307         if(box[0].size == 'xs' && box[1].size == 'xs'){
30308             
30309             pos.push({
30310                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30311                 y : minY
30312             });
30313
30314             pos.push({
30315                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30316                 y : minY
30317             });
30318             
30319             pos.push({
30320                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30321                 y : minY + (this.unitWidth + this.gutter) * 1
30322             });
30323             
30324             return pos;
30325             
30326         }
30327         
30328         pos.push({
30329             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30330             y : minY
30331         });
30332
30333         pos.push({
30334             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30335             y : minY + (this.unitWidth + this.gutter) * 2
30336         });
30337
30338         pos.push({
30339             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30340             y : minY + (this.unitWidth + this.gutter) * 2
30341         });
30342             
30343         return pos;
30344         
30345     },
30346     
30347     getHorizontalFourBoxColPositions : function(maxX, minY, box)
30348     {
30349         var pos = [];
30350         
30351         if(box[0].size == 'xs'){
30352             
30353             pos.push({
30354                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30355                 y : minY
30356             });
30357
30358             pos.push({
30359                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30360                 y : minY
30361             });
30362             
30363             pos.push({
30364                 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),
30365                 y : minY
30366             });
30367             
30368             pos.push({
30369                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
30370                 y : minY + (this.unitWidth + this.gutter) * 1
30371             });
30372             
30373             return pos;
30374             
30375         }
30376         
30377         pos.push({
30378             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30379             y : minY
30380         });
30381         
30382         pos.push({
30383             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30384             y : minY + (this.unitWidth + this.gutter) * 2
30385         });
30386         
30387         pos.push({
30388             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30389             y : minY + (this.unitWidth + this.gutter) * 2
30390         });
30391         
30392         pos.push({
30393             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),
30394             y : minY + (this.unitWidth + this.gutter) * 2
30395         });
30396
30397         return pos;
30398         
30399     }
30400     
30401 });
30402
30403  
30404
30405  /**
30406  *
30407  * This is based on 
30408  * http://masonry.desandro.com
30409  *
30410  * The idea is to render all the bricks based on vertical width...
30411  *
30412  * The original code extends 'outlayer' - we might need to use that....
30413  * 
30414  */
30415
30416
30417 /**
30418  * @class Roo.bootstrap.LayoutMasonryAuto
30419  * @extends Roo.bootstrap.Component
30420  * Bootstrap Layout Masonry class
30421  * 
30422  * @constructor
30423  * Create a new Element
30424  * @param {Object} config The config object
30425  */
30426
30427 Roo.bootstrap.LayoutMasonryAuto = function(config){
30428     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
30429 };
30430
30431 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
30432     
30433       /**
30434      * @cfg {Boolean} isFitWidth  - resize the width..
30435      */   
30436     isFitWidth : false,  // options..
30437     /**
30438      * @cfg {Boolean} isOriginLeft = left align?
30439      */   
30440     isOriginLeft : true,
30441     /**
30442      * @cfg {Boolean} isOriginTop = top align?
30443      */   
30444     isOriginTop : false,
30445     /**
30446      * @cfg {Boolean} isLayoutInstant = no animation?
30447      */   
30448     isLayoutInstant : false, // needed?
30449     /**
30450      * @cfg {Boolean} isResizingContainer = not sure if this is used..
30451      */   
30452     isResizingContainer : true,
30453     /**
30454      * @cfg {Number} columnWidth  width of the columns 
30455      */   
30456     
30457     columnWidth : 0,
30458     
30459     /**
30460      * @cfg {Number} maxCols maximum number of columns
30461      */   
30462     
30463     maxCols: 0,
30464     /**
30465      * @cfg {Number} padHeight padding below box..
30466      */   
30467     
30468     padHeight : 10, 
30469     
30470     /**
30471      * @cfg {Boolean} isAutoInitial defalut true
30472      */   
30473     
30474     isAutoInitial : true, 
30475     
30476     // private?
30477     gutter : 0,
30478     
30479     containerWidth: 0,
30480     initialColumnWidth : 0,
30481     currentSize : null,
30482     
30483     colYs : null, // array.
30484     maxY : 0,
30485     padWidth: 10,
30486     
30487     
30488     tag: 'div',
30489     cls: '',
30490     bricks: null, //CompositeElement
30491     cols : 0, // array?
30492     // element : null, // wrapped now this.el
30493     _isLayoutInited : null, 
30494     
30495     
30496     getAutoCreate : function(){
30497         
30498         var cfg = {
30499             tag: this.tag,
30500             cls: 'blog-masonary-wrapper ' + this.cls,
30501             cn : {
30502                 cls : 'mas-boxes masonary'
30503             }
30504         };
30505         
30506         return cfg;
30507     },
30508     
30509     getChildContainer: function( )
30510     {
30511         if (this.boxesEl) {
30512             return this.boxesEl;
30513         }
30514         
30515         this.boxesEl = this.el.select('.mas-boxes').first();
30516         
30517         return this.boxesEl;
30518     },
30519     
30520     
30521     initEvents : function()
30522     {
30523         var _this = this;
30524         
30525         if(this.isAutoInitial){
30526             Roo.log('hook children rendered');
30527             this.on('childrenrendered', function() {
30528                 Roo.log('children rendered');
30529                 _this.initial();
30530             } ,this);
30531         }
30532         
30533     },
30534     
30535     initial : function()
30536     {
30537         this.reloadItems();
30538
30539         this.currentSize = this.el.getBox(true);
30540
30541         /// was window resize... - let's see if this works..
30542         Roo.EventManager.onWindowResize(this.resize, this); 
30543
30544         if(!this.isAutoInitial){
30545             this.layout();
30546             return;
30547         }
30548         
30549         this.layout.defer(500,this);
30550     },
30551     
30552     reloadItems: function()
30553     {
30554         this.bricks = this.el.select('.masonry-brick', true);
30555         
30556         this.bricks.each(function(b) {
30557             //Roo.log(b.getSize());
30558             if (!b.attr('originalwidth')) {
30559                 b.attr('originalwidth',  b.getSize().width);
30560             }
30561             
30562         });
30563         
30564         Roo.log(this.bricks.elements.length);
30565     },
30566     
30567     resize : function()
30568     {
30569         Roo.log('resize');
30570         var cs = this.el.getBox(true);
30571         
30572         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
30573             Roo.log("no change in with or X");
30574             return;
30575         }
30576         this.currentSize = cs;
30577         this.layout();
30578     },
30579     
30580     layout : function()
30581     {
30582          Roo.log('layout');
30583         this._resetLayout();
30584         //this._manageStamps();
30585       
30586         // don't animate first layout
30587         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30588         this.layoutItems( isInstant );
30589       
30590         // flag for initalized
30591         this._isLayoutInited = true;
30592     },
30593     
30594     layoutItems : function( isInstant )
30595     {
30596         //var items = this._getItemsForLayout( this.items );
30597         // original code supports filtering layout items.. we just ignore it..
30598         
30599         this._layoutItems( this.bricks , isInstant );
30600       
30601         this._postLayout();
30602     },
30603     _layoutItems : function ( items , isInstant)
30604     {
30605        //this.fireEvent( 'layout', this, items );
30606     
30607
30608         if ( !items || !items.elements.length ) {
30609           // no items, emit event with empty array
30610             return;
30611         }
30612
30613         var queue = [];
30614         items.each(function(item) {
30615             Roo.log("layout item");
30616             Roo.log(item);
30617             // get x/y object from method
30618             var position = this._getItemLayoutPosition( item );
30619             // enqueue
30620             position.item = item;
30621             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
30622             queue.push( position );
30623         }, this);
30624       
30625         this._processLayoutQueue( queue );
30626     },
30627     /** Sets position of item in DOM
30628     * @param {Element} item
30629     * @param {Number} x - horizontal position
30630     * @param {Number} y - vertical position
30631     * @param {Boolean} isInstant - disables transitions
30632     */
30633     _processLayoutQueue : function( queue )
30634     {
30635         for ( var i=0, len = queue.length; i < len; i++ ) {
30636             var obj = queue[i];
30637             obj.item.position('absolute');
30638             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
30639         }
30640     },
30641       
30642     
30643     /**
30644     * Any logic you want to do after each layout,
30645     * i.e. size the container
30646     */
30647     _postLayout : function()
30648     {
30649         this.resizeContainer();
30650     },
30651     
30652     resizeContainer : function()
30653     {
30654         if ( !this.isResizingContainer ) {
30655             return;
30656         }
30657         var size = this._getContainerSize();
30658         if ( size ) {
30659             this.el.setSize(size.width,size.height);
30660             this.boxesEl.setSize(size.width,size.height);
30661         }
30662     },
30663     
30664     
30665     
30666     _resetLayout : function()
30667     {
30668         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
30669         this.colWidth = this.el.getWidth();
30670         //this.gutter = this.el.getWidth(); 
30671         
30672         this.measureColumns();
30673
30674         // reset column Y
30675         var i = this.cols;
30676         this.colYs = [];
30677         while (i--) {
30678             this.colYs.push( 0 );
30679         }
30680     
30681         this.maxY = 0;
30682     },
30683
30684     measureColumns : function()
30685     {
30686         this.getContainerWidth();
30687       // if columnWidth is 0, default to outerWidth of first item
30688         if ( !this.columnWidth ) {
30689             var firstItem = this.bricks.first();
30690             Roo.log(firstItem);
30691             this.columnWidth  = this.containerWidth;
30692             if (firstItem && firstItem.attr('originalwidth') ) {
30693                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
30694             }
30695             // columnWidth fall back to item of first element
30696             Roo.log("set column width?");
30697                         this.initialColumnWidth = this.columnWidth  ;
30698
30699             // if first elem has no width, default to size of container
30700             
30701         }
30702         
30703         
30704         if (this.initialColumnWidth) {
30705             this.columnWidth = this.initialColumnWidth;
30706         }
30707         
30708         
30709             
30710         // column width is fixed at the top - however if container width get's smaller we should
30711         // reduce it...
30712         
30713         // this bit calcs how man columns..
30714             
30715         var columnWidth = this.columnWidth += this.gutter;
30716       
30717         // calculate columns
30718         var containerWidth = this.containerWidth + this.gutter;
30719         
30720         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
30721         // fix rounding errors, typically with gutters
30722         var excess = columnWidth - containerWidth % columnWidth;
30723         
30724         
30725         // if overshoot is less than a pixel, round up, otherwise floor it
30726         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
30727         cols = Math[ mathMethod ]( cols );
30728         this.cols = Math.max( cols, 1 );
30729         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30730         
30731          // padding positioning..
30732         var totalColWidth = this.cols * this.columnWidth;
30733         var padavail = this.containerWidth - totalColWidth;
30734         // so for 2 columns - we need 3 'pads'
30735         
30736         var padNeeded = (1+this.cols) * this.padWidth;
30737         
30738         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
30739         
30740         this.columnWidth += padExtra
30741         //this.padWidth = Math.floor(padavail /  ( this.cols));
30742         
30743         // adjust colum width so that padding is fixed??
30744         
30745         // we have 3 columns ... total = width * 3
30746         // we have X left over... that should be used by 
30747         
30748         //if (this.expandC) {
30749             
30750         //}
30751         
30752         
30753         
30754     },
30755     
30756     getContainerWidth : function()
30757     {
30758        /* // container is parent if fit width
30759         var container = this.isFitWidth ? this.element.parentNode : this.element;
30760         // check that this.size and size are there
30761         // IE8 triggers resize on body size change, so they might not be
30762         
30763         var size = getSize( container );  //FIXME
30764         this.containerWidth = size && size.innerWidth; //FIXME
30765         */
30766          
30767         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30768         
30769     },
30770     
30771     _getItemLayoutPosition : function( item )  // what is item?
30772     {
30773         // we resize the item to our columnWidth..
30774       
30775         item.setWidth(this.columnWidth);
30776         item.autoBoxAdjust  = false;
30777         
30778         var sz = item.getSize();
30779  
30780         // how many columns does this brick span
30781         var remainder = this.containerWidth % this.columnWidth;
30782         
30783         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30784         // round if off by 1 pixel, otherwise use ceil
30785         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
30786         colSpan = Math.min( colSpan, this.cols );
30787         
30788         // normally this should be '1' as we dont' currently allow multi width columns..
30789         
30790         var colGroup = this._getColGroup( colSpan );
30791         // get the minimum Y value from the columns
30792         var minimumY = Math.min.apply( Math, colGroup );
30793         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30794         
30795         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
30796          
30797         // position the brick
30798         var position = {
30799             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30800             y: this.currentSize.y + minimumY + this.padHeight
30801         };
30802         
30803         Roo.log(position);
30804         // apply setHeight to necessary columns
30805         var setHeight = minimumY + sz.height + this.padHeight;
30806         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30807         
30808         var setSpan = this.cols + 1 - colGroup.length;
30809         for ( var i = 0; i < setSpan; i++ ) {
30810           this.colYs[ shortColIndex + i ] = setHeight ;
30811         }
30812       
30813         return position;
30814     },
30815     
30816     /**
30817      * @param {Number} colSpan - number of columns the element spans
30818      * @returns {Array} colGroup
30819      */
30820     _getColGroup : function( colSpan )
30821     {
30822         if ( colSpan < 2 ) {
30823           // if brick spans only one column, use all the column Ys
30824           return this.colYs;
30825         }
30826       
30827         var colGroup = [];
30828         // how many different places could this brick fit horizontally
30829         var groupCount = this.cols + 1 - colSpan;
30830         // for each group potential horizontal position
30831         for ( var i = 0; i < groupCount; i++ ) {
30832           // make an array of colY values for that one group
30833           var groupColYs = this.colYs.slice( i, i + colSpan );
30834           // and get the max value of the array
30835           colGroup[i] = Math.max.apply( Math, groupColYs );
30836         }
30837         return colGroup;
30838     },
30839     /*
30840     _manageStamp : function( stamp )
30841     {
30842         var stampSize =  stamp.getSize();
30843         var offset = stamp.getBox();
30844         // get the columns that this stamp affects
30845         var firstX = this.isOriginLeft ? offset.x : offset.right;
30846         var lastX = firstX + stampSize.width;
30847         var firstCol = Math.floor( firstX / this.columnWidth );
30848         firstCol = Math.max( 0, firstCol );
30849         
30850         var lastCol = Math.floor( lastX / this.columnWidth );
30851         // lastCol should not go over if multiple of columnWidth #425
30852         lastCol -= lastX % this.columnWidth ? 0 : 1;
30853         lastCol = Math.min( this.cols - 1, lastCol );
30854         
30855         // set colYs to bottom of the stamp
30856         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30857             stampSize.height;
30858             
30859         for ( var i = firstCol; i <= lastCol; i++ ) {
30860           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30861         }
30862     },
30863     */
30864     
30865     _getContainerSize : function()
30866     {
30867         this.maxY = Math.max.apply( Math, this.colYs );
30868         var size = {
30869             height: this.maxY
30870         };
30871       
30872         if ( this.isFitWidth ) {
30873             size.width = this._getContainerFitWidth();
30874         }
30875       
30876         return size;
30877     },
30878     
30879     _getContainerFitWidth : function()
30880     {
30881         var unusedCols = 0;
30882         // count unused columns
30883         var i = this.cols;
30884         while ( --i ) {
30885           if ( this.colYs[i] !== 0 ) {
30886             break;
30887           }
30888           unusedCols++;
30889         }
30890         // fit container to columns that have been used
30891         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30892     },
30893     
30894     needsResizeLayout : function()
30895     {
30896         var previousWidth = this.containerWidth;
30897         this.getContainerWidth();
30898         return previousWidth !== this.containerWidth;
30899     }
30900  
30901 });
30902
30903  
30904
30905  /*
30906  * - LGPL
30907  *
30908  * element
30909  * 
30910  */
30911
30912 /**
30913  * @class Roo.bootstrap.MasonryBrick
30914  * @extends Roo.bootstrap.Component
30915  * Bootstrap MasonryBrick class
30916  * 
30917  * @constructor
30918  * Create a new MasonryBrick
30919  * @param {Object} config The config object
30920  */
30921
30922 Roo.bootstrap.MasonryBrick = function(config){
30923     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
30924     
30925     this.addEvents({
30926         // raw events
30927         /**
30928          * @event click
30929          * When a MasonryBrick is clcik
30930          * @param {Roo.bootstrap.MasonryBrick} this
30931          * @param {Roo.EventObject} e
30932          */
30933         "click" : true
30934     });
30935 };
30936
30937 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
30938     
30939     /**
30940      * @cfg {String} title
30941      */   
30942     title : '',
30943     /**
30944      * @cfg {String} html
30945      */   
30946     html : '',
30947     /**
30948      * @cfg {String} bgimage
30949      */   
30950     bgimage : '',
30951     /**
30952      * @cfg {String} videourl
30953      */   
30954     videourl : '',
30955     /**
30956      * @cfg {String} cls
30957      */   
30958     cls : '',
30959     /**
30960      * @cfg {String} href
30961      */   
30962     href : '',
30963     /**
30964      * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
30965      */   
30966     size : 'xs',
30967     
30968     /**
30969      * @cfg {String} (center|bottom) placetitle
30970      */   
30971     placetitle : '',
30972     
30973     getAutoCreate : function()
30974     {
30975         var cls = 'masonry-brick';
30976         
30977         if(this.href.length){
30978             cls += ' masonry-brick-link';
30979         }
30980         
30981         if(this.bgimage.length){
30982             cls += ' masonry-brick-image';
30983         }
30984         
30985         if(this.size){
30986             cls += ' masonry-' + this.size + '-brick';
30987         }
30988         
30989         if(this.placetitle.length){
30990             
30991             switch (this.placetitle) {
30992                 case 'center' :
30993                     cls += ' masonry-center-title';
30994                     break;
30995                 case 'bottom' :
30996                     cls += ' masonry-bottom-title';
30997                     break;
30998                 default:
30999                     break;
31000             }
31001             
31002         } else {
31003             if(!this.html.length && !this.bgimage.length){
31004                 cls += ' masonry-center-title';
31005             }
31006
31007             if(!this.html.length && this.bgimage.length){
31008                 cls += ' masonry-bottom-title';
31009             }
31010         }
31011         
31012         if(this.cls){
31013             cls += ' ' + this.cls;
31014         }
31015         
31016         var cfg = {
31017             tag: (this.href.length) ? 'a' : 'div',
31018             cls: cls,
31019             cn: [
31020                 {
31021                     tag: 'div',
31022                     cls: 'masonry-brick-paragraph',
31023                     cn: []
31024                 }
31025             ]
31026         };
31027         
31028         if(this.href.length){
31029             cfg.href = this.href;
31030         }
31031         
31032         var cn = cfg.cn[0].cn;
31033         
31034         if(this.title.length){
31035             cn.push({
31036                 tag: 'h4',
31037                 cls: 'masonry-brick-title',
31038                 html: this.title
31039             });
31040         }
31041         
31042         if(this.html.length){
31043             cn.push({
31044                 tag: 'p',
31045                 cls: 'masonry-brick-text',
31046                 html: this.html
31047             });
31048         }  
31049         if (!this.title.length && !this.html.length) {
31050             cfg.cn[0].cls += ' hide';
31051         }
31052         
31053         if(this.bgimage.length){
31054             cfg.cn.push({
31055                 tag: 'img',
31056                 cls: 'masonry-brick-image-view',
31057                 src: this.bgimage
31058             });
31059         }
31060         if(this.videourl.length){
31061             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31062             // youtube support only?
31063             cfg.cn.push({
31064                 tag: 'iframe',
31065                 cls: 'masonry-brick-image-view',
31066                 src: vurl,
31067                 frameborder : 0,
31068                 allowfullscreen : true
31069             });
31070             
31071             
31072         }
31073         return cfg;
31074         
31075     },
31076     
31077     initEvents: function() 
31078     {
31079         switch (this.size) {
31080             case 'xs' :
31081 //                this.intSize = 1;
31082                 this.x = 1;
31083                 this.y = 1;
31084                 break;
31085             case 'sm' :
31086 //                this.intSize = 2;
31087                 this.x = 2;
31088                 this.y = 2;
31089                 break;
31090             case 'md' :
31091             case 'md-left' :
31092             case 'md-right' :
31093 //                this.intSize = 3;
31094                 this.x = 3;
31095                 this.y = 3;
31096                 break;
31097             case 'tall' :
31098 //                this.intSize = 3;
31099                 this.x = 2;
31100                 this.y = 3;
31101                 break;
31102             case 'wide' :
31103 //                this.intSize = 3;
31104                 this.x = 3;
31105                 this.y = 2;
31106                 break;
31107             case 'wide-thin' :
31108 //                this.intSize = 3;
31109                 this.x = 3;
31110                 this.y = 1;
31111                 break;
31112                         
31113             default :
31114                 break;
31115         }
31116         
31117         
31118         
31119         if(Roo.isTouch){
31120             this.el.on('touchstart', this.onTouchStart, this);
31121             this.el.on('touchmove', this.onTouchMove, this);
31122             this.el.on('touchend', this.onTouchEnd, this);
31123             this.el.on('contextmenu', this.onContextMenu, this);
31124         } else {
31125             this.el.on('mouseenter'  ,this.enter, this);
31126             this.el.on('mouseleave', this.leave, this);
31127         }
31128         
31129         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
31130             this.parent().bricks.push(this);   
31131         }
31132         
31133     },
31134     
31135     onClick: function(e, el)
31136     {
31137         if(!Roo.isTouch){
31138             return;
31139         }
31140         
31141         var time = this.endTimer - this.startTimer;
31142         
31143         //alert(time);
31144         
31145         if(time < 1000){
31146             return;
31147         }
31148         
31149         e.preventDefault();
31150     },
31151     
31152     enter: function(e, el)
31153     {
31154         e.preventDefault();
31155         
31156         if(this.bgimage.length && this.html.length){
31157             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31158         }
31159     },
31160     
31161     leave: function(e, el)
31162     {
31163         e.preventDefault();
31164         
31165         if(this.bgimage.length && this.html.length){
31166             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31167         }
31168     },
31169     
31170     onTouchStart: function(e, el)
31171     {
31172 //        e.preventDefault();
31173         
31174         this.touchmoved = false;
31175         
31176         if(!this.bgimage.length || !this.html.length){
31177             return;
31178         }
31179         
31180         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31181         
31182         this.timer = new Date().getTime();
31183         
31184     },
31185     
31186     onTouchMove: function(e, el)
31187     {
31188         this.touchmoved = true;
31189     },
31190     
31191     onContextMenu : function(e,el)
31192     {
31193         e.preventDefault();
31194         e.stopPropagation();
31195         return false;
31196     },
31197     
31198     onTouchEnd: function(e, el)
31199     {
31200 //        e.preventDefault();
31201         
31202         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
31203         
31204             this.leave(e,el);
31205             
31206             return;
31207         }
31208         
31209         if(!this.bgimage.length || !this.html.length){
31210             
31211             if(this.href.length){
31212                 window.location.href = this.href;
31213             }
31214             
31215             return;
31216         }
31217         
31218         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31219         
31220         window.location.href = this.href;
31221     }
31222     
31223 });
31224
31225  
31226
31227  /*
31228  * - LGPL
31229  *
31230  * element
31231  * 
31232  */
31233
31234 /**
31235  * @class Roo.bootstrap.Brick
31236  * @extends Roo.bootstrap.Component
31237  * Bootstrap Brick class
31238  * 
31239  * @constructor
31240  * Create a new Brick
31241  * @param {Object} config The config object
31242  */
31243
31244 Roo.bootstrap.Brick = function(config){
31245     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
31246     
31247     this.addEvents({
31248         // raw events
31249         /**
31250          * @event click
31251          * When a Brick is click
31252          * @param {Roo.bootstrap.Brick} this
31253          * @param {Roo.EventObject} e
31254          */
31255         "click" : true
31256     });
31257 };
31258
31259 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
31260     
31261     /**
31262      * @cfg {String} title
31263      */   
31264     title : '',
31265     /**
31266      * @cfg {String} html
31267      */   
31268     html : '',
31269     /**
31270      * @cfg {String} bgimage
31271      */   
31272     bgimage : '',
31273     /**
31274      * @cfg {String} cls
31275      */   
31276     cls : '',
31277     /**
31278      * @cfg {String} href
31279      */   
31280     href : '',
31281     /**
31282      * @cfg {String} video
31283      */   
31284     video : '',
31285     /**
31286      * @cfg {Boolean} square
31287      */   
31288     square : true,
31289     
31290     getAutoCreate : function()
31291     {
31292         var cls = 'roo-brick';
31293         
31294         if(this.href.length){
31295             cls += ' roo-brick-link';
31296         }
31297         
31298         if(this.bgimage.length){
31299             cls += ' roo-brick-image';
31300         }
31301         
31302         if(!this.html.length && !this.bgimage.length){
31303             cls += ' roo-brick-center-title';
31304         }
31305         
31306         if(!this.html.length && this.bgimage.length){
31307             cls += ' roo-brick-bottom-title';
31308         }
31309         
31310         if(this.cls){
31311             cls += ' ' + this.cls;
31312         }
31313         
31314         var cfg = {
31315             tag: (this.href.length) ? 'a' : 'div',
31316             cls: cls,
31317             cn: [
31318                 {
31319                     tag: 'div',
31320                     cls: 'roo-brick-paragraph',
31321                     cn: []
31322                 }
31323             ]
31324         };
31325         
31326         if(this.href.length){
31327             cfg.href = this.href;
31328         }
31329         
31330         var cn = cfg.cn[0].cn;
31331         
31332         if(this.title.length){
31333             cn.push({
31334                 tag: 'h4',
31335                 cls: 'roo-brick-title',
31336                 html: this.title
31337             });
31338         }
31339         
31340         if(this.html.length){
31341             cn.push({
31342                 tag: 'p',
31343                 cls: 'roo-brick-text',
31344                 html: this.html
31345             });
31346         } else {
31347             cn.cls += ' hide';
31348         }
31349         
31350         if(this.bgimage.length){
31351             cfg.cn.push({
31352                 tag: 'img',
31353                 cls: 'roo-brick-image-view',
31354                 src: this.bgimage
31355             });
31356         }
31357         
31358         return cfg;
31359     },
31360     
31361     initEvents: function() 
31362     {
31363         if(this.title.length || this.html.length){
31364             this.el.on('mouseenter'  ,this.enter, this);
31365             this.el.on('mouseleave', this.leave, this);
31366         }
31367         
31368         
31369         Roo.EventManager.onWindowResize(this.resize, this); 
31370         
31371         this.resize();
31372     },
31373     
31374     resize : function()
31375     {
31376         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
31377         
31378         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
31379 //        paragraph.setHeight(paragraph.getWidth());
31380         
31381         if(this.bgimage.length){
31382             var image = this.el.select('.roo-brick-image-view', true).first();
31383             image.setWidth(paragraph.getWidth());
31384             image.setHeight(paragraph.getWidth());
31385         }
31386         
31387     },
31388     
31389     enter: function(e, el)
31390     {
31391         e.preventDefault();
31392         
31393         if(this.bgimage.length){
31394             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
31395             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
31396         }
31397     },
31398     
31399     leave: function(e, el)
31400     {
31401         e.preventDefault();
31402         
31403         if(this.bgimage.length){
31404             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
31405             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
31406         }
31407     }
31408     
31409 });
31410
31411  
31412
31413  /*
31414  * Based on:
31415  * Ext JS Library 1.1.1
31416  * Copyright(c) 2006-2007, Ext JS, LLC.
31417  *
31418  * Originally Released Under LGPL - original licence link has changed is not relivant.
31419  *
31420  * Fork - LGPL
31421  * <script type="text/javascript">
31422  */
31423
31424
31425 /**
31426  * @class Roo.bootstrap.SplitBar
31427  * @extends Roo.util.Observable
31428  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
31429  * <br><br>
31430  * Usage:
31431  * <pre><code>
31432 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
31433                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
31434 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
31435 split.minSize = 100;
31436 split.maxSize = 600;
31437 split.animate = true;
31438 split.on('moved', splitterMoved);
31439 </code></pre>
31440  * @constructor
31441  * Create a new SplitBar
31442  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
31443  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
31444  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31445  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
31446                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
31447                         position of the SplitBar).
31448  */
31449 Roo.bootstrap.SplitBar = function(cfg){
31450     
31451     /** @private */
31452     
31453     //{
31454     //  dragElement : elm
31455     //  resizingElement: el,
31456         // optional..
31457     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
31458     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
31459         // existingProxy ???
31460     //}
31461     
31462     this.el = Roo.get(cfg.dragElement, true);
31463     this.el.dom.unselectable = "on";
31464     /** @private */
31465     this.resizingEl = Roo.get(cfg.resizingElement, true);
31466
31467     /**
31468      * @private
31469      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
31470      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
31471      * @type Number
31472      */
31473     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
31474     
31475     /**
31476      * The minimum size of the resizing element. (Defaults to 0)
31477      * @type Number
31478      */
31479     this.minSize = 0;
31480     
31481     /**
31482      * The maximum size of the resizing element. (Defaults to 2000)
31483      * @type Number
31484      */
31485     this.maxSize = 2000;
31486     
31487     /**
31488      * Whether to animate the transition to the new size
31489      * @type Boolean
31490      */
31491     this.animate = false;
31492     
31493     /**
31494      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
31495      * @type Boolean
31496      */
31497     this.useShim = false;
31498     
31499     /** @private */
31500     this.shim = null;
31501     
31502     if(!cfg.existingProxy){
31503         /** @private */
31504         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
31505     }else{
31506         this.proxy = Roo.get(cfg.existingProxy).dom;
31507     }
31508     /** @private */
31509     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
31510     
31511     /** @private */
31512     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
31513     
31514     /** @private */
31515     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
31516     
31517     /** @private */
31518     this.dragSpecs = {};
31519     
31520     /**
31521      * @private The adapter to use to positon and resize elements
31522      */
31523     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31524     this.adapter.init(this);
31525     
31526     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31527         /** @private */
31528         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
31529         this.el.addClass("roo-splitbar-h");
31530     }else{
31531         /** @private */
31532         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
31533         this.el.addClass("roo-splitbar-v");
31534     }
31535     
31536     this.addEvents({
31537         /**
31538          * @event resize
31539          * Fires when the splitter is moved (alias for {@link #event-moved})
31540          * @param {Roo.bootstrap.SplitBar} this
31541          * @param {Number} newSize the new width or height
31542          */
31543         "resize" : true,
31544         /**
31545          * @event moved
31546          * Fires when the splitter is moved
31547          * @param {Roo.bootstrap.SplitBar} this
31548          * @param {Number} newSize the new width or height
31549          */
31550         "moved" : true,
31551         /**
31552          * @event beforeresize
31553          * Fires before the splitter is dragged
31554          * @param {Roo.bootstrap.SplitBar} this
31555          */
31556         "beforeresize" : true,
31557
31558         "beforeapply" : true
31559     });
31560
31561     Roo.util.Observable.call(this);
31562 };
31563
31564 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
31565     onStartProxyDrag : function(x, y){
31566         this.fireEvent("beforeresize", this);
31567         if(!this.overlay){
31568             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
31569             o.unselectable();
31570             o.enableDisplayMode("block");
31571             // all splitbars share the same overlay
31572             Roo.bootstrap.SplitBar.prototype.overlay = o;
31573         }
31574         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
31575         this.overlay.show();
31576         Roo.get(this.proxy).setDisplayed("block");
31577         var size = this.adapter.getElementSize(this);
31578         this.activeMinSize = this.getMinimumSize();;
31579         this.activeMaxSize = this.getMaximumSize();;
31580         var c1 = size - this.activeMinSize;
31581         var c2 = Math.max(this.activeMaxSize - size, 0);
31582         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31583             this.dd.resetConstraints();
31584             this.dd.setXConstraint(
31585                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
31586                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
31587             );
31588             this.dd.setYConstraint(0, 0);
31589         }else{
31590             this.dd.resetConstraints();
31591             this.dd.setXConstraint(0, 0);
31592             this.dd.setYConstraint(
31593                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
31594                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
31595             );
31596          }
31597         this.dragSpecs.startSize = size;
31598         this.dragSpecs.startPoint = [x, y];
31599         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
31600     },
31601     
31602     /** 
31603      * @private Called after the drag operation by the DDProxy
31604      */
31605     onEndProxyDrag : function(e){
31606         Roo.get(this.proxy).setDisplayed(false);
31607         var endPoint = Roo.lib.Event.getXY(e);
31608         if(this.overlay){
31609             this.overlay.hide();
31610         }
31611         var newSize;
31612         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31613             newSize = this.dragSpecs.startSize + 
31614                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
31615                     endPoint[0] - this.dragSpecs.startPoint[0] :
31616                     this.dragSpecs.startPoint[0] - endPoint[0]
31617                 );
31618         }else{
31619             newSize = this.dragSpecs.startSize + 
31620                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
31621                     endPoint[1] - this.dragSpecs.startPoint[1] :
31622                     this.dragSpecs.startPoint[1] - endPoint[1]
31623                 );
31624         }
31625         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
31626         if(newSize != this.dragSpecs.startSize){
31627             if(this.fireEvent('beforeapply', this, newSize) !== false){
31628                 this.adapter.setElementSize(this, newSize);
31629                 this.fireEvent("moved", this, newSize);
31630                 this.fireEvent("resize", this, newSize);
31631             }
31632         }
31633     },
31634     
31635     /**
31636      * Get the adapter this SplitBar uses
31637      * @return The adapter object
31638      */
31639     getAdapter : function(){
31640         return this.adapter;
31641     },
31642     
31643     /**
31644      * Set the adapter this SplitBar uses
31645      * @param {Object} adapter A SplitBar adapter object
31646      */
31647     setAdapter : function(adapter){
31648         this.adapter = adapter;
31649         this.adapter.init(this);
31650     },
31651     
31652     /**
31653      * Gets the minimum size for the resizing element
31654      * @return {Number} The minimum size
31655      */
31656     getMinimumSize : function(){
31657         return this.minSize;
31658     },
31659     
31660     /**
31661      * Sets the minimum size for the resizing element
31662      * @param {Number} minSize The minimum size
31663      */
31664     setMinimumSize : function(minSize){
31665         this.minSize = minSize;
31666     },
31667     
31668     /**
31669      * Gets the maximum size for the resizing element
31670      * @return {Number} The maximum size
31671      */
31672     getMaximumSize : function(){
31673         return this.maxSize;
31674     },
31675     
31676     /**
31677      * Sets the maximum size for the resizing element
31678      * @param {Number} maxSize The maximum size
31679      */
31680     setMaximumSize : function(maxSize){
31681         this.maxSize = maxSize;
31682     },
31683     
31684     /**
31685      * Sets the initialize size for the resizing element
31686      * @param {Number} size The initial size
31687      */
31688     setCurrentSize : function(size){
31689         var oldAnimate = this.animate;
31690         this.animate = false;
31691         this.adapter.setElementSize(this, size);
31692         this.animate = oldAnimate;
31693     },
31694     
31695     /**
31696      * Destroy this splitbar. 
31697      * @param {Boolean} removeEl True to remove the element
31698      */
31699     destroy : function(removeEl){
31700         if(this.shim){
31701             this.shim.remove();
31702         }
31703         this.dd.unreg();
31704         this.proxy.parentNode.removeChild(this.proxy);
31705         if(removeEl){
31706             this.el.remove();
31707         }
31708     }
31709 });
31710
31711 /**
31712  * @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.
31713  */
31714 Roo.bootstrap.SplitBar.createProxy = function(dir){
31715     var proxy = new Roo.Element(document.createElement("div"));
31716     proxy.unselectable();
31717     var cls = 'roo-splitbar-proxy';
31718     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
31719     document.body.appendChild(proxy.dom);
31720     return proxy.dom;
31721 };
31722
31723 /** 
31724  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
31725  * Default Adapter. It assumes the splitter and resizing element are not positioned
31726  * elements and only gets/sets the width of the element. Generally used for table based layouts.
31727  */
31728 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
31729 };
31730
31731 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
31732     // do nothing for now
31733     init : function(s){
31734     
31735     },
31736     /**
31737      * Called before drag operations to get the current size of the resizing element. 
31738      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31739      */
31740      getElementSize : function(s){
31741         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31742             return s.resizingEl.getWidth();
31743         }else{
31744             return s.resizingEl.getHeight();
31745         }
31746     },
31747     
31748     /**
31749      * Called after drag operations to set the size of the resizing element.
31750      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31751      * @param {Number} newSize The new size to set
31752      * @param {Function} onComplete A function to be invoked when resizing is complete
31753      */
31754     setElementSize : function(s, newSize, onComplete){
31755         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31756             if(!s.animate){
31757                 s.resizingEl.setWidth(newSize);
31758                 if(onComplete){
31759                     onComplete(s, newSize);
31760                 }
31761             }else{
31762                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
31763             }
31764         }else{
31765             
31766             if(!s.animate){
31767                 s.resizingEl.setHeight(newSize);
31768                 if(onComplete){
31769                     onComplete(s, newSize);
31770                 }
31771             }else{
31772                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
31773             }
31774         }
31775     }
31776 };
31777
31778 /** 
31779  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
31780  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
31781  * Adapter that  moves the splitter element to align with the resized sizing element. 
31782  * Used with an absolute positioned SplitBar.
31783  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
31784  * document.body, make sure you assign an id to the body element.
31785  */
31786 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
31787     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31788     this.container = Roo.get(container);
31789 };
31790
31791 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
31792     init : function(s){
31793         this.basic.init(s);
31794     },
31795     
31796     getElementSize : function(s){
31797         return this.basic.getElementSize(s);
31798     },
31799     
31800     setElementSize : function(s, newSize, onComplete){
31801         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
31802     },
31803     
31804     moveSplitter : function(s){
31805         var yes = Roo.bootstrap.SplitBar;
31806         switch(s.placement){
31807             case yes.LEFT:
31808                 s.el.setX(s.resizingEl.getRight());
31809                 break;
31810             case yes.RIGHT:
31811                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
31812                 break;
31813             case yes.TOP:
31814                 s.el.setY(s.resizingEl.getBottom());
31815                 break;
31816             case yes.BOTTOM:
31817                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
31818                 break;
31819         }
31820     }
31821 };
31822
31823 /**
31824  * Orientation constant - Create a vertical SplitBar
31825  * @static
31826  * @type Number
31827  */
31828 Roo.bootstrap.SplitBar.VERTICAL = 1;
31829
31830 /**
31831  * Orientation constant - Create a horizontal SplitBar
31832  * @static
31833  * @type Number
31834  */
31835 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
31836
31837 /**
31838  * Placement constant - The resizing element is to the left of the splitter element
31839  * @static
31840  * @type Number
31841  */
31842 Roo.bootstrap.SplitBar.LEFT = 1;
31843
31844 /**
31845  * Placement constant - The resizing element is to the right of the splitter element
31846  * @static
31847  * @type Number
31848  */
31849 Roo.bootstrap.SplitBar.RIGHT = 2;
31850
31851 /**
31852  * Placement constant - The resizing element is positioned above the splitter element
31853  * @static
31854  * @type Number
31855  */
31856 Roo.bootstrap.SplitBar.TOP = 3;
31857
31858 /**
31859  * Placement constant - The resizing element is positioned under splitter element
31860  * @static
31861  * @type Number
31862  */
31863 Roo.bootstrap.SplitBar.BOTTOM = 4;
31864 Roo.namespace("Roo.bootstrap.layout");/*
31865  * Based on:
31866  * Ext JS Library 1.1.1
31867  * Copyright(c) 2006-2007, Ext JS, LLC.
31868  *
31869  * Originally Released Under LGPL - original licence link has changed is not relivant.
31870  *
31871  * Fork - LGPL
31872  * <script type="text/javascript">
31873  */
31874
31875 /**
31876  * @class Roo.bootstrap.layout.Manager
31877  * @extends Roo.bootstrap.Component
31878  * Base class for layout managers.
31879  */
31880 Roo.bootstrap.layout.Manager = function(config)
31881 {
31882     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
31883
31884
31885
31886
31887
31888     /** false to disable window resize monitoring @type Boolean */
31889     this.monitorWindowResize = true;
31890     this.regions = {};
31891     this.addEvents({
31892         /**
31893          * @event layout
31894          * Fires when a layout is performed.
31895          * @param {Roo.LayoutManager} this
31896          */
31897         "layout" : true,
31898         /**
31899          * @event regionresized
31900          * Fires when the user resizes a region.
31901          * @param {Roo.LayoutRegion} region The resized region
31902          * @param {Number} newSize The new size (width for east/west, height for north/south)
31903          */
31904         "regionresized" : true,
31905         /**
31906          * @event regioncollapsed
31907          * Fires when a region is collapsed.
31908          * @param {Roo.LayoutRegion} region The collapsed region
31909          */
31910         "regioncollapsed" : true,
31911         /**
31912          * @event regionexpanded
31913          * Fires when a region is expanded.
31914          * @param {Roo.LayoutRegion} region The expanded region
31915          */
31916         "regionexpanded" : true
31917     });
31918     this.updating = false;
31919
31920     if (config.el) {
31921         this.el = Roo.get(config.el);
31922         this.initEvents();
31923     }
31924
31925 };
31926
31927 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
31928
31929
31930     regions : null,
31931
31932     monitorWindowResize : true,
31933
31934
31935     updating : false,
31936
31937
31938     onRender : function(ct, position)
31939     {
31940         if(!this.el){
31941             this.el = Roo.get(ct);
31942             this.initEvents();
31943         }
31944         //this.fireEvent('render',this);
31945     },
31946
31947
31948     initEvents: function()
31949     {
31950
31951
31952         // ie scrollbar fix
31953         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
31954             document.body.scroll = "no";
31955         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
31956             this.el.position('relative');
31957         }
31958         this.id = this.el.id;
31959         this.el.addClass("roo-layout-container");
31960         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
31961         if(this.el.dom != document.body ) {
31962             this.el.on('resize', this.layout,this);
31963             this.el.on('show', this.layout,this);
31964         }
31965
31966     },
31967
31968     /**
31969      * Returns true if this layout is currently being updated
31970      * @return {Boolean}
31971      */
31972     isUpdating : function(){
31973         return this.updating;
31974     },
31975
31976     /**
31977      * Suspend the LayoutManager from doing auto-layouts while
31978      * making multiple add or remove calls
31979      */
31980     beginUpdate : function(){
31981         this.updating = true;
31982     },
31983
31984     /**
31985      * Restore auto-layouts and optionally disable the manager from performing a layout
31986      * @param {Boolean} noLayout true to disable a layout update
31987      */
31988     endUpdate : function(noLayout){
31989         this.updating = false;
31990         if(!noLayout){
31991             this.layout();
31992         }
31993     },
31994
31995     layout: function(){
31996         // abstract...
31997     },
31998
31999     onRegionResized : function(region, newSize){
32000         this.fireEvent("regionresized", region, newSize);
32001         this.layout();
32002     },
32003
32004     onRegionCollapsed : function(region){
32005         this.fireEvent("regioncollapsed", region);
32006     },
32007
32008     onRegionExpanded : function(region){
32009         this.fireEvent("regionexpanded", region);
32010     },
32011
32012     /**
32013      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32014      * performs box-model adjustments.
32015      * @return {Object} The size as an object {width: (the width), height: (the height)}
32016      */
32017     getViewSize : function()
32018     {
32019         var size;
32020         if(this.el.dom != document.body){
32021             size = this.el.getSize();
32022         }else{
32023             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32024         }
32025         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32026         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32027         return size;
32028     },
32029
32030     /**
32031      * Returns the Element this layout is bound to.
32032      * @return {Roo.Element}
32033      */
32034     getEl : function(){
32035         return this.el;
32036     },
32037
32038     /**
32039      * Returns the specified region.
32040      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32041      * @return {Roo.LayoutRegion}
32042      */
32043     getRegion : function(target){
32044         return this.regions[target.toLowerCase()];
32045     },
32046
32047     onWindowResize : function(){
32048         if(this.monitorWindowResize){
32049             this.layout();
32050         }
32051     }
32052 });
32053 /*
32054  * Based on:
32055  * Ext JS Library 1.1.1
32056  * Copyright(c) 2006-2007, Ext JS, LLC.
32057  *
32058  * Originally Released Under LGPL - original licence link has changed is not relivant.
32059  *
32060  * Fork - LGPL
32061  * <script type="text/javascript">
32062  */
32063 /**
32064  * @class Roo.bootstrap.layout.Border
32065  * @extends Roo.bootstrap.layout.Manager
32066  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32067  * please see: examples/bootstrap/nested.html<br><br>
32068  
32069 <b>The container the layout is rendered into can be either the body element or any other element.
32070 If it is not the body element, the container needs to either be an absolute positioned element,
32071 or you will need to add "position:relative" to the css of the container.  You will also need to specify
32072 the container size if it is not the body element.</b>
32073
32074 * @constructor
32075 * Create a new Border
32076 * @param {Object} config Configuration options
32077  */
32078 Roo.bootstrap.layout.Border = function(config){
32079     config = config || {};
32080     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
32081     
32082     
32083     
32084     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
32085         if(config[region]){
32086             config[region].region = region;
32087             this.addRegion(config[region]);
32088         }
32089     },this);
32090     
32091 };
32092
32093 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
32094
32095 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
32096     /**
32097      * Creates and adds a new region if it doesn't already exist.
32098      * @param {String} target The target region key (north, south, east, west or center).
32099      * @param {Object} config The regions config object
32100      * @return {BorderLayoutRegion} The new region
32101      */
32102     addRegion : function(config)
32103     {
32104         if(!this.regions[config.region]){
32105             var r = this.factory(config);
32106             this.bindRegion(r);
32107         }
32108         return this.regions[config.region];
32109     },
32110
32111     // private (kinda)
32112     bindRegion : function(r){
32113         this.regions[r.config.region] = r;
32114         
32115         r.on("visibilitychange",    this.layout, this);
32116         r.on("paneladded",          this.layout, this);
32117         r.on("panelremoved",        this.layout, this);
32118         r.on("invalidated",         this.layout, this);
32119         r.on("resized",             this.onRegionResized, this);
32120         r.on("collapsed",           this.onRegionCollapsed, this);
32121         r.on("expanded",            this.onRegionExpanded, this);
32122     },
32123
32124     /**
32125      * Performs a layout update.
32126      */
32127     layout : function()
32128     {
32129         if(this.updating) {
32130             return;
32131         }
32132         
32133         // render all the rebions if they have not been done alreayd?
32134         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
32135             if(this.regions[region] && !this.regions[region].bodyEl){
32136                 this.regions[region].onRender(this.el)
32137             }
32138         },this);
32139         
32140         var size = this.getViewSize();
32141         var w = size.width;
32142         var h = size.height;
32143         var centerW = w;
32144         var centerH = h;
32145         var centerY = 0;
32146         var centerX = 0;
32147         //var x = 0, y = 0;
32148
32149         var rs = this.regions;
32150         var north = rs["north"];
32151         var south = rs["south"]; 
32152         var west = rs["west"];
32153         var east = rs["east"];
32154         var center = rs["center"];
32155         //if(this.hideOnLayout){ // not supported anymore
32156             //c.el.setStyle("display", "none");
32157         //}
32158         if(north && north.isVisible()){
32159             var b = north.getBox();
32160             var m = north.getMargins();
32161             b.width = w - (m.left+m.right);
32162             b.x = m.left;
32163             b.y = m.top;
32164             centerY = b.height + b.y + m.bottom;
32165             centerH -= centerY;
32166             north.updateBox(this.safeBox(b));
32167         }
32168         if(south && south.isVisible()){
32169             var b = south.getBox();
32170             var m = south.getMargins();
32171             b.width = w - (m.left+m.right);
32172             b.x = m.left;
32173             var totalHeight = (b.height + m.top + m.bottom);
32174             b.y = h - totalHeight + m.top;
32175             centerH -= totalHeight;
32176             south.updateBox(this.safeBox(b));
32177         }
32178         if(west && west.isVisible()){
32179             var b = west.getBox();
32180             var m = west.getMargins();
32181             b.height = centerH - (m.top+m.bottom);
32182             b.x = m.left;
32183             b.y = centerY + m.top;
32184             var totalWidth = (b.width + m.left + m.right);
32185             centerX += totalWidth;
32186             centerW -= totalWidth;
32187             west.updateBox(this.safeBox(b));
32188         }
32189         if(east && east.isVisible()){
32190             var b = east.getBox();
32191             var m = east.getMargins();
32192             b.height = centerH - (m.top+m.bottom);
32193             var totalWidth = (b.width + m.left + m.right);
32194             b.x = w - totalWidth + m.left;
32195             b.y = centerY + m.top;
32196             centerW -= totalWidth;
32197             east.updateBox(this.safeBox(b));
32198         }
32199         if(center){
32200             var m = center.getMargins();
32201             var centerBox = {
32202                 x: centerX + m.left,
32203                 y: centerY + m.top,
32204                 width: centerW - (m.left+m.right),
32205                 height: centerH - (m.top+m.bottom)
32206             };
32207             //if(this.hideOnLayout){
32208                 //center.el.setStyle("display", "block");
32209             //}
32210             center.updateBox(this.safeBox(centerBox));
32211         }
32212         this.el.repaint();
32213         this.fireEvent("layout", this);
32214     },
32215
32216     // private
32217     safeBox : function(box){
32218         box.width = Math.max(0, box.width);
32219         box.height = Math.max(0, box.height);
32220         return box;
32221     },
32222
32223     /**
32224      * Adds a ContentPanel (or subclass) to this layout.
32225      * @param {String} target The target region key (north, south, east, west or center).
32226      * @param {Roo.ContentPanel} panel The panel to add
32227      * @return {Roo.ContentPanel} The added panel
32228      */
32229     add : function(target, panel){
32230          
32231         target = target.toLowerCase();
32232         return this.regions[target].add(panel);
32233     },
32234
32235     /**
32236      * Remove a ContentPanel (or subclass) to this layout.
32237      * @param {String} target The target region key (north, south, east, west or center).
32238      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
32239      * @return {Roo.ContentPanel} The removed panel
32240      */
32241     remove : function(target, panel){
32242         target = target.toLowerCase();
32243         return this.regions[target].remove(panel);
32244     },
32245
32246     /**
32247      * Searches all regions for a panel with the specified id
32248      * @param {String} panelId
32249      * @return {Roo.ContentPanel} The panel or null if it wasn't found
32250      */
32251     findPanel : function(panelId){
32252         var rs = this.regions;
32253         for(var target in rs){
32254             if(typeof rs[target] != "function"){
32255                 var p = rs[target].getPanel(panelId);
32256                 if(p){
32257                     return p;
32258                 }
32259             }
32260         }
32261         return null;
32262     },
32263
32264     /**
32265      * Searches all regions for a panel with the specified id and activates (shows) it.
32266      * @param {String/ContentPanel} panelId The panels id or the panel itself
32267      * @return {Roo.ContentPanel} The shown panel or null
32268      */
32269     showPanel : function(panelId) {
32270       var rs = this.regions;
32271       for(var target in rs){
32272          var r = rs[target];
32273          if(typeof r != "function"){
32274             if(r.hasPanel(panelId)){
32275                return r.showPanel(panelId);
32276             }
32277          }
32278       }
32279       return null;
32280    },
32281
32282    /**
32283      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
32284      * @param {Roo.state.Provider} provider (optional) An alternate state provider
32285      */
32286    /*
32287     restoreState : function(provider){
32288         if(!provider){
32289             provider = Roo.state.Manager;
32290         }
32291         var sm = new Roo.LayoutStateManager();
32292         sm.init(this, provider);
32293     },
32294 */
32295  
32296  
32297     /**
32298      * Adds a xtype elements to the layout.
32299      * <pre><code>
32300
32301 layout.addxtype({
32302        xtype : 'ContentPanel',
32303        region: 'west',
32304        items: [ .... ]
32305    }
32306 );
32307
32308 layout.addxtype({
32309         xtype : 'NestedLayoutPanel',
32310         region: 'west',
32311         layout: {
32312            center: { },
32313            west: { }   
32314         },
32315         items : [ ... list of content panels or nested layout panels.. ]
32316    }
32317 );
32318 </code></pre>
32319      * @param {Object} cfg Xtype definition of item to add.
32320      */
32321     addxtype : function(cfg)
32322     {
32323         // basically accepts a pannel...
32324         // can accept a layout region..!?!?
32325         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
32326         
32327         
32328         // theory?  children can only be panels??
32329         
32330         //if (!cfg.xtype.match(/Panel$/)) {
32331         //    return false;
32332         //}
32333         var ret = false;
32334         
32335         if (typeof(cfg.region) == 'undefined') {
32336             Roo.log("Failed to add Panel, region was not set");
32337             Roo.log(cfg);
32338             return false;
32339         }
32340         var region = cfg.region;
32341         delete cfg.region;
32342         
32343           
32344         var xitems = [];
32345         if (cfg.items) {
32346             xitems = cfg.items;
32347             delete cfg.items;
32348         }
32349         var nb = false;
32350         
32351         switch(cfg.xtype) 
32352         {
32353             case 'Content':  // ContentPanel (el, cfg)
32354             case 'Scroll':  // ContentPanel (el, cfg)
32355             case 'View': 
32356                 cfg.autoCreate = true;
32357                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32358                 //} else {
32359                 //    var el = this.el.createChild();
32360                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
32361                 //}
32362                 
32363                 this.add(region, ret);
32364                 break;
32365             
32366             /*
32367             case 'TreePanel': // our new panel!
32368                 cfg.el = this.el.createChild();
32369                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32370                 this.add(region, ret);
32371                 break;
32372             */
32373             
32374             case 'Nest': 
32375                 // create a new Layout (which is  a Border Layout...
32376                 
32377                 var clayout = cfg.layout;
32378                 clayout.el  = this.el.createChild();
32379                 clayout.items   = clayout.items  || [];
32380                 
32381                 delete cfg.layout;
32382                 
32383                 // replace this exitems with the clayout ones..
32384                 xitems = clayout.items;
32385                  
32386                 // force background off if it's in center...
32387                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
32388                     cfg.background = false;
32389                 }
32390                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
32391                 
32392                 
32393                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32394                 //console.log('adding nested layout panel '  + cfg.toSource());
32395                 this.add(region, ret);
32396                 nb = {}; /// find first...
32397                 break;
32398             
32399             case 'Grid':
32400                 
32401                 // needs grid and region
32402                 
32403                 //var el = this.getRegion(region).el.createChild();
32404                 /*
32405                  *var el = this.el.createChild();
32406                 // create the grid first...
32407                 cfg.grid.container = el;
32408                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
32409                 */
32410                 
32411                 if (region == 'center' && this.active ) {
32412                     cfg.background = false;
32413                 }
32414                 
32415                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
32416                 
32417                 this.add(region, ret);
32418                 /*
32419                 if (cfg.background) {
32420                     // render grid on panel activation (if panel background)
32421                     ret.on('activate', function(gp) {
32422                         if (!gp.grid.rendered) {
32423                     //        gp.grid.render(el);
32424                         }
32425                     });
32426                 } else {
32427                   //  cfg.grid.render(el);
32428                 }
32429                 */
32430                 break;
32431            
32432            
32433             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
32434                 // it was the old xcomponent building that caused this before.
32435                 // espeically if border is the top element in the tree.
32436                 ret = this;
32437                 break; 
32438                 
32439                     
32440                 
32441                 
32442                 
32443             default:
32444                 /*
32445                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
32446                     
32447                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32448                     this.add(region, ret);
32449                 } else {
32450                 */
32451                     Roo.log(cfg);
32452                     throw "Can not add '" + cfg.xtype + "' to Border";
32453                     return null;
32454              
32455                                 
32456              
32457         }
32458         this.beginUpdate();
32459         // add children..
32460         var region = '';
32461         var abn = {};
32462         Roo.each(xitems, function(i)  {
32463             region = nb && i.region ? i.region : false;
32464             
32465             var add = ret.addxtype(i);
32466            
32467             if (region) {
32468                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
32469                 if (!i.background) {
32470                     abn[region] = nb[region] ;
32471                 }
32472             }
32473             
32474         });
32475         this.endUpdate();
32476
32477         // make the last non-background panel active..
32478         //if (nb) { Roo.log(abn); }
32479         if (nb) {
32480             
32481             for(var r in abn) {
32482                 region = this.getRegion(r);
32483                 if (region) {
32484                     // tried using nb[r], but it does not work..
32485                      
32486                     region.showPanel(abn[r]);
32487                    
32488                 }
32489             }
32490         }
32491         return ret;
32492         
32493     },
32494     
32495     
32496 // private
32497     factory : function(cfg)
32498     {
32499         
32500         var validRegions = Roo.bootstrap.layout.Border.regions;
32501
32502         var target = cfg.region;
32503         cfg.mgr = this;
32504         
32505         var r = Roo.bootstrap.layout;
32506         Roo.log(target);
32507         switch(target){
32508             case "north":
32509                 return new r.North(cfg);
32510             case "south":
32511                 return new r.South(cfg);
32512             case "east":
32513                 return new r.East(cfg);
32514             case "west":
32515                 return new r.West(cfg);
32516             case "center":
32517                 return new r.Center(cfg);
32518         }
32519         throw 'Layout region "'+target+'" not supported.';
32520     }
32521     
32522     
32523 });
32524  /*
32525  * Based on:
32526  * Ext JS Library 1.1.1
32527  * Copyright(c) 2006-2007, Ext JS, LLC.
32528  *
32529  * Originally Released Under LGPL - original licence link has changed is not relivant.
32530  *
32531  * Fork - LGPL
32532  * <script type="text/javascript">
32533  */
32534  
32535 /**
32536  * @class Roo.bootstrap.layout.Basic
32537  * @extends Roo.util.Observable
32538  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
32539  * and does not have a titlebar, tabs or any other features. All it does is size and position 
32540  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
32541  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
32542  * @cfg {string}   region  the region that it inhabits..
32543  * @cfg {bool}   skipConfig skip config?
32544  * 
32545
32546  */
32547 Roo.bootstrap.layout.Basic = function(config){
32548     
32549     this.mgr = config.mgr;
32550     
32551     this.position = config.region;
32552     
32553     var skipConfig = config.skipConfig;
32554     
32555     this.events = {
32556         /**
32557          * @scope Roo.BasicLayoutRegion
32558          */
32559         
32560         /**
32561          * @event beforeremove
32562          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
32563          * @param {Roo.LayoutRegion} this
32564          * @param {Roo.ContentPanel} panel The panel
32565          * @param {Object} e The cancel event object
32566          */
32567         "beforeremove" : true,
32568         /**
32569          * @event invalidated
32570          * Fires when the layout for this region is changed.
32571          * @param {Roo.LayoutRegion} this
32572          */
32573         "invalidated" : true,
32574         /**
32575          * @event visibilitychange
32576          * Fires when this region is shown or hidden 
32577          * @param {Roo.LayoutRegion} this
32578          * @param {Boolean} visibility true or false
32579          */
32580         "visibilitychange" : true,
32581         /**
32582          * @event paneladded
32583          * Fires when a panel is added. 
32584          * @param {Roo.LayoutRegion} this
32585          * @param {Roo.ContentPanel} panel The panel
32586          */
32587         "paneladded" : true,
32588         /**
32589          * @event panelremoved
32590          * Fires when a panel is removed. 
32591          * @param {Roo.LayoutRegion} this
32592          * @param {Roo.ContentPanel} panel The panel
32593          */
32594         "panelremoved" : true,
32595         /**
32596          * @event beforecollapse
32597          * Fires when this region before collapse.
32598          * @param {Roo.LayoutRegion} this
32599          */
32600         "beforecollapse" : true,
32601         /**
32602          * @event collapsed
32603          * Fires when this region is collapsed.
32604          * @param {Roo.LayoutRegion} this
32605          */
32606         "collapsed" : true,
32607         /**
32608          * @event expanded
32609          * Fires when this region is expanded.
32610          * @param {Roo.LayoutRegion} this
32611          */
32612         "expanded" : true,
32613         /**
32614          * @event slideshow
32615          * Fires when this region is slid into view.
32616          * @param {Roo.LayoutRegion} this
32617          */
32618         "slideshow" : true,
32619         /**
32620          * @event slidehide
32621          * Fires when this region slides out of view. 
32622          * @param {Roo.LayoutRegion} this
32623          */
32624         "slidehide" : true,
32625         /**
32626          * @event panelactivated
32627          * Fires when a panel is activated. 
32628          * @param {Roo.LayoutRegion} this
32629          * @param {Roo.ContentPanel} panel The activated panel
32630          */
32631         "panelactivated" : true,
32632         /**
32633          * @event resized
32634          * Fires when the user resizes this region. 
32635          * @param {Roo.LayoutRegion} this
32636          * @param {Number} newSize The new size (width for east/west, height for north/south)
32637          */
32638         "resized" : true
32639     };
32640     /** A collection of panels in this region. @type Roo.util.MixedCollection */
32641     this.panels = new Roo.util.MixedCollection();
32642     this.panels.getKey = this.getPanelId.createDelegate(this);
32643     this.box = null;
32644     this.activePanel = null;
32645     // ensure listeners are added...
32646     
32647     if (config.listeners || config.events) {
32648         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
32649             listeners : config.listeners || {},
32650             events : config.events || {}
32651         });
32652     }
32653     
32654     if(skipConfig !== true){
32655         this.applyConfig(config);
32656     }
32657 };
32658
32659 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
32660 {
32661     getPanelId : function(p){
32662         return p.getId();
32663     },
32664     
32665     applyConfig : function(config){
32666         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32667         this.config = config;
32668         
32669     },
32670     
32671     /**
32672      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
32673      * the width, for horizontal (north, south) the height.
32674      * @param {Number} newSize The new width or height
32675      */
32676     resizeTo : function(newSize){
32677         var el = this.el ? this.el :
32678                  (this.activePanel ? this.activePanel.getEl() : null);
32679         if(el){
32680             switch(this.position){
32681                 case "east":
32682                 case "west":
32683                     el.setWidth(newSize);
32684                     this.fireEvent("resized", this, newSize);
32685                 break;
32686                 case "north":
32687                 case "south":
32688                     el.setHeight(newSize);
32689                     this.fireEvent("resized", this, newSize);
32690                 break;                
32691             }
32692         }
32693     },
32694     
32695     getBox : function(){
32696         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
32697     },
32698     
32699     getMargins : function(){
32700         return this.margins;
32701     },
32702     
32703     updateBox : function(box){
32704         this.box = box;
32705         var el = this.activePanel.getEl();
32706         el.dom.style.left = box.x + "px";
32707         el.dom.style.top = box.y + "px";
32708         this.activePanel.setSize(box.width, box.height);
32709     },
32710     
32711     /**
32712      * Returns the container element for this region.
32713      * @return {Roo.Element}
32714      */
32715     getEl : function(){
32716         return this.activePanel;
32717     },
32718     
32719     /**
32720      * Returns true if this region is currently visible.
32721      * @return {Boolean}
32722      */
32723     isVisible : function(){
32724         return this.activePanel ? true : false;
32725     },
32726     
32727     setActivePanel : function(panel){
32728         panel = this.getPanel(panel);
32729         if(this.activePanel && this.activePanel != panel){
32730             this.activePanel.setActiveState(false);
32731             this.activePanel.getEl().setLeftTop(-10000,-10000);
32732         }
32733         this.activePanel = panel;
32734         panel.setActiveState(true);
32735         if(this.box){
32736             panel.setSize(this.box.width, this.box.height);
32737         }
32738         this.fireEvent("panelactivated", this, panel);
32739         this.fireEvent("invalidated");
32740     },
32741     
32742     /**
32743      * Show the specified panel.
32744      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
32745      * @return {Roo.ContentPanel} The shown panel or null
32746      */
32747     showPanel : function(panel){
32748         panel = this.getPanel(panel);
32749         if(panel){
32750             this.setActivePanel(panel);
32751         }
32752         return panel;
32753     },
32754     
32755     /**
32756      * Get the active panel for this region.
32757      * @return {Roo.ContentPanel} The active panel or null
32758      */
32759     getActivePanel : function(){
32760         return this.activePanel;
32761     },
32762     
32763     /**
32764      * Add the passed ContentPanel(s)
32765      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32766      * @return {Roo.ContentPanel} The panel added (if only one was added)
32767      */
32768     add : function(panel){
32769         if(arguments.length > 1){
32770             for(var i = 0, len = arguments.length; i < len; i++) {
32771                 this.add(arguments[i]);
32772             }
32773             return null;
32774         }
32775         if(this.hasPanel(panel)){
32776             this.showPanel(panel);
32777             return panel;
32778         }
32779         var el = panel.getEl();
32780         if(el.dom.parentNode != this.mgr.el.dom){
32781             this.mgr.el.dom.appendChild(el.dom);
32782         }
32783         if(panel.setRegion){
32784             panel.setRegion(this);
32785         }
32786         this.panels.add(panel);
32787         el.setStyle("position", "absolute");
32788         if(!panel.background){
32789             this.setActivePanel(panel);
32790             if(this.config.initialSize && this.panels.getCount()==1){
32791                 this.resizeTo(this.config.initialSize);
32792             }
32793         }
32794         this.fireEvent("paneladded", this, panel);
32795         return panel;
32796     },
32797     
32798     /**
32799      * Returns true if the panel is in this region.
32800      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32801      * @return {Boolean}
32802      */
32803     hasPanel : function(panel){
32804         if(typeof panel == "object"){ // must be panel obj
32805             panel = panel.getId();
32806         }
32807         return this.getPanel(panel) ? true : false;
32808     },
32809     
32810     /**
32811      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32812      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32813      * @param {Boolean} preservePanel Overrides the config preservePanel option
32814      * @return {Roo.ContentPanel} The panel that was removed
32815      */
32816     remove : function(panel, preservePanel){
32817         panel = this.getPanel(panel);
32818         if(!panel){
32819             return null;
32820         }
32821         var e = {};
32822         this.fireEvent("beforeremove", this, panel, e);
32823         if(e.cancel === true){
32824             return null;
32825         }
32826         var panelId = panel.getId();
32827         this.panels.removeKey(panelId);
32828         return panel;
32829     },
32830     
32831     /**
32832      * Returns the panel specified or null if it's not in this region.
32833      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32834      * @return {Roo.ContentPanel}
32835      */
32836     getPanel : function(id){
32837         if(typeof id == "object"){ // must be panel obj
32838             return id;
32839         }
32840         return this.panels.get(id);
32841     },
32842     
32843     /**
32844      * Returns this regions position (north/south/east/west/center).
32845      * @return {String} 
32846      */
32847     getPosition: function(){
32848         return this.position;    
32849     }
32850 });/*
32851  * Based on:
32852  * Ext JS Library 1.1.1
32853  * Copyright(c) 2006-2007, Ext JS, LLC.
32854  *
32855  * Originally Released Under LGPL - original licence link has changed is not relivant.
32856  *
32857  * Fork - LGPL
32858  * <script type="text/javascript">
32859  */
32860  
32861 /**
32862  * @class Roo.bootstrap.layout.Region
32863  * @extends Roo.bootstrap.layout.Basic
32864  * This class represents a region in a layout manager.
32865  
32866  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
32867  * @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})
32868  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
32869  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
32870  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
32871  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
32872  * @cfg {String}    title           The title for the region (overrides panel titles)
32873  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
32874  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
32875  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
32876  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
32877  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
32878  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
32879  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
32880  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
32881  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
32882  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
32883
32884  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
32885  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
32886  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
32887  * @cfg {Number}    width           For East/West panels
32888  * @cfg {Number}    height          For North/South panels
32889  * @cfg {Boolean}   split           To show the splitter
32890  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
32891  * 
32892  * @cfg {string}   cls             Extra CSS classes to add to region
32893  * 
32894  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
32895  * @cfg {string}   region  the region that it inhabits..
32896  *
32897
32898  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
32899  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
32900
32901  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
32902  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
32903  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
32904  */
32905 Roo.bootstrap.layout.Region = function(config)
32906 {
32907     this.applyConfig(config);
32908
32909     var mgr = config.mgr;
32910     var pos = config.region;
32911     config.skipConfig = true;
32912     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
32913     
32914     if (mgr.el) {
32915         this.onRender(mgr.el);   
32916     }
32917      
32918     this.visible = true;
32919     this.collapsed = false;
32920     this.unrendered_panels = [];
32921 };
32922
32923 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
32924
32925     position: '', // set by wrapper (eg. north/south etc..)
32926     unrendered_panels : null,  // unrendered panels.
32927     createBody : function(){
32928         /** This region's body element 
32929         * @type Roo.Element */
32930         this.bodyEl = this.el.createChild({
32931                 tag: "div",
32932                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
32933         });
32934     },
32935
32936     onRender: function(ctr, pos)
32937     {
32938         var dh = Roo.DomHelper;
32939         /** This region's container element 
32940         * @type Roo.Element */
32941         this.el = dh.append(ctr.dom, {
32942                 tag: "div",
32943                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
32944             }, true);
32945         /** This region's title element 
32946         * @type Roo.Element */
32947     
32948         this.titleEl = dh.append(this.el.dom,
32949             {
32950                     tag: "div",
32951                     unselectable: "on",
32952                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
32953                     children:[
32954                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
32955                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
32956                     ]}, true);
32957         
32958         this.titleEl.enableDisplayMode();
32959         /** This region's title text element 
32960         * @type HTMLElement */
32961         this.titleTextEl = this.titleEl.dom.firstChild;
32962         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
32963         /*
32964         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
32965         this.closeBtn.enableDisplayMode();
32966         this.closeBtn.on("click", this.closeClicked, this);
32967         this.closeBtn.hide();
32968     */
32969         this.createBody(this.config);
32970         if(this.config.hideWhenEmpty){
32971             this.hide();
32972             this.on("paneladded", this.validateVisibility, this);
32973             this.on("panelremoved", this.validateVisibility, this);
32974         }
32975         if(this.autoScroll){
32976             this.bodyEl.setStyle("overflow", "auto");
32977         }else{
32978             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
32979         }
32980         //if(c.titlebar !== false){
32981             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
32982                 this.titleEl.hide();
32983             }else{
32984                 this.titleEl.show();
32985                 if(this.config.title){
32986                     this.titleTextEl.innerHTML = this.config.title;
32987                 }
32988             }
32989         //}
32990         if(this.config.collapsed){
32991             this.collapse(true);
32992         }
32993         if(this.config.hidden){
32994             this.hide();
32995         }
32996         
32997         if (this.unrendered_panels && this.unrendered_panels.length) {
32998             for (var i =0;i< this.unrendered_panels.length; i++) {
32999                 this.add(this.unrendered_panels[i]);
33000             }
33001             this.unrendered_panels = null;
33002             
33003         }
33004         
33005     },
33006     
33007     applyConfig : function(c)
33008     {
33009         /*
33010          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
33011             var dh = Roo.DomHelper;
33012             if(c.titlebar !== false){
33013                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
33014                 this.collapseBtn.on("click", this.collapse, this);
33015                 this.collapseBtn.enableDisplayMode();
33016                 /*
33017                 if(c.showPin === true || this.showPin){
33018                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
33019                     this.stickBtn.enableDisplayMode();
33020                     this.stickBtn.on("click", this.expand, this);
33021                     this.stickBtn.hide();
33022                 }
33023                 
33024             }
33025             */
33026             /** This region's collapsed element
33027             * @type Roo.Element */
33028             /*
33029              *
33030             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33031                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33032             ]}, true);
33033             
33034             if(c.floatable !== false){
33035                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33036                this.collapsedEl.on("click", this.collapseClick, this);
33037             }
33038
33039             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33040                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33041                    id: "message", unselectable: "on", style:{"float":"left"}});
33042                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33043              }
33044             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33045             this.expandBtn.on("click", this.expand, this);
33046             
33047         }
33048         
33049         if(this.collapseBtn){
33050             this.collapseBtn.setVisible(c.collapsible == true);
33051         }
33052         
33053         this.cmargins = c.cmargins || this.cmargins ||
33054                          (this.position == "west" || this.position == "east" ?
33055                              {top: 0, left: 2, right:2, bottom: 0} :
33056                              {top: 2, left: 0, right:0, bottom: 2});
33057         */
33058         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33059         
33060         
33061         this.bottomTabs = c.tabPosition != "top";
33062         
33063         this.autoScroll = c.autoScroll || false;
33064         
33065         
33066        
33067         
33068         this.duration = c.duration || .30;
33069         this.slideDuration = c.slideDuration || .45;
33070         this.config = c;
33071        
33072     },
33073     /**
33074      * Returns true if this region is currently visible.
33075      * @return {Boolean}
33076      */
33077     isVisible : function(){
33078         return this.visible;
33079     },
33080
33081     /**
33082      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33083      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
33084      */
33085     //setCollapsedTitle : function(title){
33086     //    title = title || "&#160;";
33087      //   if(this.collapsedTitleTextEl){
33088       //      this.collapsedTitleTextEl.innerHTML = title;
33089        // }
33090     //},
33091
33092     getBox : function(){
33093         var b;
33094       //  if(!this.collapsed){
33095             b = this.el.getBox(false, true);
33096        // }else{
33097           //  b = this.collapsedEl.getBox(false, true);
33098         //}
33099         return b;
33100     },
33101
33102     getMargins : function(){
33103         return this.margins;
33104         //return this.collapsed ? this.cmargins : this.margins;
33105     },
33106 /*
33107     highlight : function(){
33108         this.el.addClass("x-layout-panel-dragover");
33109     },
33110
33111     unhighlight : function(){
33112         this.el.removeClass("x-layout-panel-dragover");
33113     },
33114 */
33115     updateBox : function(box)
33116     {
33117         if (!this.bodyEl) {
33118             return; // not rendered yet..
33119         }
33120         
33121         this.box = box;
33122         if(!this.collapsed){
33123             this.el.dom.style.left = box.x + "px";
33124             this.el.dom.style.top = box.y + "px";
33125             this.updateBody(box.width, box.height);
33126         }else{
33127             this.collapsedEl.dom.style.left = box.x + "px";
33128             this.collapsedEl.dom.style.top = box.y + "px";
33129             this.collapsedEl.setSize(box.width, box.height);
33130         }
33131         if(this.tabs){
33132             this.tabs.autoSizeTabs();
33133         }
33134     },
33135
33136     updateBody : function(w, h)
33137     {
33138         if(w !== null){
33139             this.el.setWidth(w);
33140             w -= this.el.getBorderWidth("rl");
33141             if(this.config.adjustments){
33142                 w += this.config.adjustments[0];
33143             }
33144         }
33145         if(h !== null && h > 0){
33146             this.el.setHeight(h);
33147             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
33148             h -= this.el.getBorderWidth("tb");
33149             if(this.config.adjustments){
33150                 h += this.config.adjustments[1];
33151             }
33152             this.bodyEl.setHeight(h);
33153             if(this.tabs){
33154                 h = this.tabs.syncHeight(h);
33155             }
33156         }
33157         if(this.panelSize){
33158             w = w !== null ? w : this.panelSize.width;
33159             h = h !== null ? h : this.panelSize.height;
33160         }
33161         if(this.activePanel){
33162             var el = this.activePanel.getEl();
33163             w = w !== null ? w : el.getWidth();
33164             h = h !== null ? h : el.getHeight();
33165             this.panelSize = {width: w, height: h};
33166             this.activePanel.setSize(w, h);
33167         }
33168         if(Roo.isIE && this.tabs){
33169             this.tabs.el.repaint();
33170         }
33171     },
33172
33173     /**
33174      * Returns the container element for this region.
33175      * @return {Roo.Element}
33176      */
33177     getEl : function(){
33178         return this.el;
33179     },
33180
33181     /**
33182      * Hides this region.
33183      */
33184     hide : function(){
33185         //if(!this.collapsed){
33186             this.el.dom.style.left = "-2000px";
33187             this.el.hide();
33188         //}else{
33189          //   this.collapsedEl.dom.style.left = "-2000px";
33190          //   this.collapsedEl.hide();
33191        // }
33192         this.visible = false;
33193         this.fireEvent("visibilitychange", this, false);
33194     },
33195
33196     /**
33197      * Shows this region if it was previously hidden.
33198      */
33199     show : function(){
33200         //if(!this.collapsed){
33201             this.el.show();
33202         //}else{
33203         //    this.collapsedEl.show();
33204        // }
33205         this.visible = true;
33206         this.fireEvent("visibilitychange", this, true);
33207     },
33208 /*
33209     closeClicked : function(){
33210         if(this.activePanel){
33211             this.remove(this.activePanel);
33212         }
33213     },
33214
33215     collapseClick : function(e){
33216         if(this.isSlid){
33217            e.stopPropagation();
33218            this.slideIn();
33219         }else{
33220            e.stopPropagation();
33221            this.slideOut();
33222         }
33223     },
33224 */
33225     /**
33226      * Collapses this region.
33227      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
33228      */
33229     /*
33230     collapse : function(skipAnim, skipCheck = false){
33231         if(this.collapsed) {
33232             return;
33233         }
33234         
33235         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
33236             
33237             this.collapsed = true;
33238             if(this.split){
33239                 this.split.el.hide();
33240             }
33241             if(this.config.animate && skipAnim !== true){
33242                 this.fireEvent("invalidated", this);
33243                 this.animateCollapse();
33244             }else{
33245                 this.el.setLocation(-20000,-20000);
33246                 this.el.hide();
33247                 this.collapsedEl.show();
33248                 this.fireEvent("collapsed", this);
33249                 this.fireEvent("invalidated", this);
33250             }
33251         }
33252         
33253     },
33254 */
33255     animateCollapse : function(){
33256         // overridden
33257     },
33258
33259     /**
33260      * Expands this region if it was previously collapsed.
33261      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
33262      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
33263      */
33264     /*
33265     expand : function(e, skipAnim){
33266         if(e) {
33267             e.stopPropagation();
33268         }
33269         if(!this.collapsed || this.el.hasActiveFx()) {
33270             return;
33271         }
33272         if(this.isSlid){
33273             this.afterSlideIn();
33274             skipAnim = true;
33275         }
33276         this.collapsed = false;
33277         if(this.config.animate && skipAnim !== true){
33278             this.animateExpand();
33279         }else{
33280             this.el.show();
33281             if(this.split){
33282                 this.split.el.show();
33283             }
33284             this.collapsedEl.setLocation(-2000,-2000);
33285             this.collapsedEl.hide();
33286             this.fireEvent("invalidated", this);
33287             this.fireEvent("expanded", this);
33288         }
33289     },
33290 */
33291     animateExpand : function(){
33292         // overridden
33293     },
33294
33295     initTabs : function()
33296     {
33297         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
33298         
33299         var ts = new Roo.bootstrap.panel.Tabs({
33300                 el: this.bodyEl.dom,
33301                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
33302                 disableTooltips: this.config.disableTabTips,
33303                 toolbar : this.config.toolbar
33304             });
33305         
33306         if(this.config.hideTabs){
33307             ts.stripWrap.setDisplayed(false);
33308         }
33309         this.tabs = ts;
33310         ts.resizeTabs = this.config.resizeTabs === true;
33311         ts.minTabWidth = this.config.minTabWidth || 40;
33312         ts.maxTabWidth = this.config.maxTabWidth || 250;
33313         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
33314         ts.monitorResize = false;
33315         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
33316         ts.bodyEl.addClass('roo-layout-tabs-body');
33317         this.panels.each(this.initPanelAsTab, this);
33318     },
33319
33320     initPanelAsTab : function(panel){
33321         var ti = this.tabs.addTab(
33322                     panel.getEl().id,
33323                     panel.getTitle(),
33324                     null,
33325                     this.config.closeOnTab && panel.isClosable()
33326             );
33327         if(panel.tabTip !== undefined){
33328             ti.setTooltip(panel.tabTip);
33329         }
33330         ti.on("activate", function(){
33331               this.setActivePanel(panel);
33332         }, this);
33333         
33334         if(this.config.closeOnTab){
33335             ti.on("beforeclose", function(t, e){
33336                 e.cancel = true;
33337                 this.remove(panel);
33338             }, this);
33339         }
33340         return ti;
33341     },
33342
33343     updatePanelTitle : function(panel, title)
33344     {
33345         if(this.activePanel == panel){
33346             this.updateTitle(title);
33347         }
33348         if(this.tabs){
33349             var ti = this.tabs.getTab(panel.getEl().id);
33350             ti.setText(title);
33351             if(panel.tabTip !== undefined){
33352                 ti.setTooltip(panel.tabTip);
33353             }
33354         }
33355     },
33356
33357     updateTitle : function(title){
33358         if(this.titleTextEl && !this.config.title){
33359             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
33360         }
33361     },
33362
33363     setActivePanel : function(panel)
33364     {
33365         panel = this.getPanel(panel);
33366         if(this.activePanel && this.activePanel != panel){
33367             this.activePanel.setActiveState(false);
33368         }
33369         this.activePanel = panel;
33370         panel.setActiveState(true);
33371         if(this.panelSize){
33372             panel.setSize(this.panelSize.width, this.panelSize.height);
33373         }
33374         if(this.closeBtn){
33375             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
33376         }
33377         this.updateTitle(panel.getTitle());
33378         if(this.tabs){
33379             this.fireEvent("invalidated", this);
33380         }
33381         this.fireEvent("panelactivated", this, panel);
33382     },
33383
33384     /**
33385      * Shows the specified panel.
33386      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
33387      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
33388      */
33389     showPanel : function(panel)
33390     {
33391         panel = this.getPanel(panel);
33392         if(panel){
33393             if(this.tabs){
33394                 var tab = this.tabs.getTab(panel.getEl().id);
33395                 if(tab.isHidden()){
33396                     this.tabs.unhideTab(tab.id);
33397                 }
33398                 tab.activate();
33399             }else{
33400                 this.setActivePanel(panel);
33401             }
33402         }
33403         return panel;
33404     },
33405
33406     /**
33407      * Get the active panel for this region.
33408      * @return {Roo.ContentPanel} The active panel or null
33409      */
33410     getActivePanel : function(){
33411         return this.activePanel;
33412     },
33413
33414     validateVisibility : function(){
33415         if(this.panels.getCount() < 1){
33416             this.updateTitle("&#160;");
33417             this.closeBtn.hide();
33418             this.hide();
33419         }else{
33420             if(!this.isVisible()){
33421                 this.show();
33422             }
33423         }
33424     },
33425
33426     /**
33427      * Adds the passed ContentPanel(s) to this region.
33428      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33429      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
33430      */
33431     add : function(panel)
33432     {
33433         if(arguments.length > 1){
33434             for(var i = 0, len = arguments.length; i < len; i++) {
33435                 this.add(arguments[i]);
33436             }
33437             return null;
33438         }
33439         
33440         // if we have not been rendered yet, then we can not really do much of this..
33441         if (!this.bodyEl) {
33442             this.unrendered_panels.push(panel);
33443             return panel;
33444         }
33445         
33446         
33447         
33448         
33449         if(this.hasPanel(panel)){
33450             this.showPanel(panel);
33451             return panel;
33452         }
33453         panel.setRegion(this);
33454         this.panels.add(panel);
33455        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
33456             // sinle panel - no tab...?? would it not be better to render it with the tabs,
33457             // and hide them... ???
33458             this.bodyEl.dom.appendChild(panel.getEl().dom);
33459             if(panel.background !== true){
33460                 this.setActivePanel(panel);
33461             }
33462             this.fireEvent("paneladded", this, panel);
33463             return panel;
33464         }
33465         */
33466         if(!this.tabs){
33467             this.initTabs();
33468         }else{
33469             this.initPanelAsTab(panel);
33470         }
33471         
33472         
33473         if(panel.background !== true){
33474             this.tabs.activate(panel.getEl().id);
33475         }
33476         this.fireEvent("paneladded", this, panel);
33477         return panel;
33478     },
33479
33480     /**
33481      * Hides the tab for the specified panel.
33482      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33483      */
33484     hidePanel : function(panel){
33485         if(this.tabs && (panel = this.getPanel(panel))){
33486             this.tabs.hideTab(panel.getEl().id);
33487         }
33488     },
33489
33490     /**
33491      * Unhides the tab for a previously hidden panel.
33492      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33493      */
33494     unhidePanel : function(panel){
33495         if(this.tabs && (panel = this.getPanel(panel))){
33496             this.tabs.unhideTab(panel.getEl().id);
33497         }
33498     },
33499
33500     clearPanels : function(){
33501         while(this.panels.getCount() > 0){
33502              this.remove(this.panels.first());
33503         }
33504     },
33505
33506     /**
33507      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33508      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33509      * @param {Boolean} preservePanel Overrides the config preservePanel option
33510      * @return {Roo.ContentPanel} The panel that was removed
33511      */
33512     remove : function(panel, preservePanel)
33513     {
33514         panel = this.getPanel(panel);
33515         if(!panel){
33516             return null;
33517         }
33518         var e = {};
33519         this.fireEvent("beforeremove", this, panel, e);
33520         if(e.cancel === true){
33521             return null;
33522         }
33523         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
33524         var panelId = panel.getId();
33525         this.panels.removeKey(panelId);
33526         if(preservePanel){
33527             document.body.appendChild(panel.getEl().dom);
33528         }
33529         if(this.tabs){
33530             this.tabs.removeTab(panel.getEl().id);
33531         }else if (!preservePanel){
33532             this.bodyEl.dom.removeChild(panel.getEl().dom);
33533         }
33534         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
33535             var p = this.panels.first();
33536             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
33537             tempEl.appendChild(p.getEl().dom);
33538             this.bodyEl.update("");
33539             this.bodyEl.dom.appendChild(p.getEl().dom);
33540             tempEl = null;
33541             this.updateTitle(p.getTitle());
33542             this.tabs = null;
33543             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33544             this.setActivePanel(p);
33545         }
33546         panel.setRegion(null);
33547         if(this.activePanel == panel){
33548             this.activePanel = null;
33549         }
33550         if(this.config.autoDestroy !== false && preservePanel !== true){
33551             try{panel.destroy();}catch(e){}
33552         }
33553         this.fireEvent("panelremoved", this, panel);
33554         return panel;
33555     },
33556
33557     /**
33558      * Returns the TabPanel component used by this region
33559      * @return {Roo.TabPanel}
33560      */
33561     getTabs : function(){
33562         return this.tabs;
33563     },
33564
33565     createTool : function(parentEl, className){
33566         var btn = Roo.DomHelper.append(parentEl, {
33567             tag: "div",
33568             cls: "x-layout-tools-button",
33569             children: [ {
33570                 tag: "div",
33571                 cls: "roo-layout-tools-button-inner " + className,
33572                 html: "&#160;"
33573             }]
33574         }, true);
33575         btn.addClassOnOver("roo-layout-tools-button-over");
33576         return btn;
33577     }
33578 });/*
33579  * Based on:
33580  * Ext JS Library 1.1.1
33581  * Copyright(c) 2006-2007, Ext JS, LLC.
33582  *
33583  * Originally Released Under LGPL - original licence link has changed is not relivant.
33584  *
33585  * Fork - LGPL
33586  * <script type="text/javascript">
33587  */
33588  
33589
33590
33591 /**
33592  * @class Roo.SplitLayoutRegion
33593  * @extends Roo.LayoutRegion
33594  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
33595  */
33596 Roo.bootstrap.layout.Split = function(config){
33597     this.cursor = config.cursor;
33598     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
33599 };
33600
33601 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
33602 {
33603     splitTip : "Drag to resize.",
33604     collapsibleSplitTip : "Drag to resize. Double click to hide.",
33605     useSplitTips : false,
33606
33607     applyConfig : function(config){
33608         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
33609     },
33610     
33611     onRender : function(ctr,pos) {
33612         
33613         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
33614         if(!this.config.split){
33615             return;
33616         }
33617         if(!this.split){
33618             
33619             var splitEl = Roo.DomHelper.append(ctr.dom,  {
33620                             tag: "div",
33621                             id: this.el.id + "-split",
33622                             cls: "roo-layout-split roo-layout-split-"+this.position,
33623                             html: "&#160;"
33624             });
33625             /** The SplitBar for this region 
33626             * @type Roo.SplitBar */
33627             // does not exist yet...
33628             Roo.log([this.position, this.orientation]);
33629             
33630             this.split = new Roo.bootstrap.SplitBar({
33631                 dragElement : splitEl,
33632                 resizingElement: this.el,
33633                 orientation : this.orientation
33634             });
33635             
33636             this.split.on("moved", this.onSplitMove, this);
33637             this.split.useShim = this.config.useShim === true;
33638             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
33639             if(this.useSplitTips){
33640                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
33641             }
33642             //if(config.collapsible){
33643             //    this.split.el.on("dblclick", this.collapse,  this);
33644             //}
33645         }
33646         if(typeof this.config.minSize != "undefined"){
33647             this.split.minSize = this.config.minSize;
33648         }
33649         if(typeof this.config.maxSize != "undefined"){
33650             this.split.maxSize = this.config.maxSize;
33651         }
33652         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
33653             this.hideSplitter();
33654         }
33655         
33656     },
33657
33658     getHMaxSize : function(){
33659          var cmax = this.config.maxSize || 10000;
33660          var center = this.mgr.getRegion("center");
33661          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
33662     },
33663
33664     getVMaxSize : function(){
33665          var cmax = this.config.maxSize || 10000;
33666          var center = this.mgr.getRegion("center");
33667          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
33668     },
33669
33670     onSplitMove : function(split, newSize){
33671         this.fireEvent("resized", this, newSize);
33672     },
33673     
33674     /** 
33675      * Returns the {@link Roo.SplitBar} for this region.
33676      * @return {Roo.SplitBar}
33677      */
33678     getSplitBar : function(){
33679         return this.split;
33680     },
33681     
33682     hide : function(){
33683         this.hideSplitter();
33684         Roo.bootstrap.layout.Split.superclass.hide.call(this);
33685     },
33686
33687     hideSplitter : function(){
33688         if(this.split){
33689             this.split.el.setLocation(-2000,-2000);
33690             this.split.el.hide();
33691         }
33692     },
33693
33694     show : function(){
33695         if(this.split){
33696             this.split.el.show();
33697         }
33698         Roo.bootstrap.layout.Split.superclass.show.call(this);
33699     },
33700     
33701     beforeSlide: function(){
33702         if(Roo.isGecko){// firefox overflow auto bug workaround
33703             this.bodyEl.clip();
33704             if(this.tabs) {
33705                 this.tabs.bodyEl.clip();
33706             }
33707             if(this.activePanel){
33708                 this.activePanel.getEl().clip();
33709                 
33710                 if(this.activePanel.beforeSlide){
33711                     this.activePanel.beforeSlide();
33712                 }
33713             }
33714         }
33715     },
33716     
33717     afterSlide : function(){
33718         if(Roo.isGecko){// firefox overflow auto bug workaround
33719             this.bodyEl.unclip();
33720             if(this.tabs) {
33721                 this.tabs.bodyEl.unclip();
33722             }
33723             if(this.activePanel){
33724                 this.activePanel.getEl().unclip();
33725                 if(this.activePanel.afterSlide){
33726                     this.activePanel.afterSlide();
33727                 }
33728             }
33729         }
33730     },
33731
33732     initAutoHide : function(){
33733         if(this.autoHide !== false){
33734             if(!this.autoHideHd){
33735                 var st = new Roo.util.DelayedTask(this.slideIn, this);
33736                 this.autoHideHd = {
33737                     "mouseout": function(e){
33738                         if(!e.within(this.el, true)){
33739                             st.delay(500);
33740                         }
33741                     },
33742                     "mouseover" : function(e){
33743                         st.cancel();
33744                     },
33745                     scope : this
33746                 };
33747             }
33748             this.el.on(this.autoHideHd);
33749         }
33750     },
33751
33752     clearAutoHide : function(){
33753         if(this.autoHide !== false){
33754             this.el.un("mouseout", this.autoHideHd.mouseout);
33755             this.el.un("mouseover", this.autoHideHd.mouseover);
33756         }
33757     },
33758
33759     clearMonitor : function(){
33760         Roo.get(document).un("click", this.slideInIf, this);
33761     },
33762
33763     // these names are backwards but not changed for compat
33764     slideOut : function(){
33765         if(this.isSlid || this.el.hasActiveFx()){
33766             return;
33767         }
33768         this.isSlid = true;
33769         if(this.collapseBtn){
33770             this.collapseBtn.hide();
33771         }
33772         this.closeBtnState = this.closeBtn.getStyle('display');
33773         this.closeBtn.hide();
33774         if(this.stickBtn){
33775             this.stickBtn.show();
33776         }
33777         this.el.show();
33778         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
33779         this.beforeSlide();
33780         this.el.setStyle("z-index", 10001);
33781         this.el.slideIn(this.getSlideAnchor(), {
33782             callback: function(){
33783                 this.afterSlide();
33784                 this.initAutoHide();
33785                 Roo.get(document).on("click", this.slideInIf, this);
33786                 this.fireEvent("slideshow", this);
33787             },
33788             scope: this,
33789             block: true
33790         });
33791     },
33792
33793     afterSlideIn : function(){
33794         this.clearAutoHide();
33795         this.isSlid = false;
33796         this.clearMonitor();
33797         this.el.setStyle("z-index", "");
33798         if(this.collapseBtn){
33799             this.collapseBtn.show();
33800         }
33801         this.closeBtn.setStyle('display', this.closeBtnState);
33802         if(this.stickBtn){
33803             this.stickBtn.hide();
33804         }
33805         this.fireEvent("slidehide", this);
33806     },
33807
33808     slideIn : function(cb){
33809         if(!this.isSlid || this.el.hasActiveFx()){
33810             Roo.callback(cb);
33811             return;
33812         }
33813         this.isSlid = false;
33814         this.beforeSlide();
33815         this.el.slideOut(this.getSlideAnchor(), {
33816             callback: function(){
33817                 this.el.setLeftTop(-10000, -10000);
33818                 this.afterSlide();
33819                 this.afterSlideIn();
33820                 Roo.callback(cb);
33821             },
33822             scope: this,
33823             block: true
33824         });
33825     },
33826     
33827     slideInIf : function(e){
33828         if(!e.within(this.el)){
33829             this.slideIn();
33830         }
33831     },
33832
33833     animateCollapse : function(){
33834         this.beforeSlide();
33835         this.el.setStyle("z-index", 20000);
33836         var anchor = this.getSlideAnchor();
33837         this.el.slideOut(anchor, {
33838             callback : function(){
33839                 this.el.setStyle("z-index", "");
33840                 this.collapsedEl.slideIn(anchor, {duration:.3});
33841                 this.afterSlide();
33842                 this.el.setLocation(-10000,-10000);
33843                 this.el.hide();
33844                 this.fireEvent("collapsed", this);
33845             },
33846             scope: this,
33847             block: true
33848         });
33849     },
33850
33851     animateExpand : function(){
33852         this.beforeSlide();
33853         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
33854         this.el.setStyle("z-index", 20000);
33855         this.collapsedEl.hide({
33856             duration:.1
33857         });
33858         this.el.slideIn(this.getSlideAnchor(), {
33859             callback : function(){
33860                 this.el.setStyle("z-index", "");
33861                 this.afterSlide();
33862                 if(this.split){
33863                     this.split.el.show();
33864                 }
33865                 this.fireEvent("invalidated", this);
33866                 this.fireEvent("expanded", this);
33867             },
33868             scope: this,
33869             block: true
33870         });
33871     },
33872
33873     anchors : {
33874         "west" : "left",
33875         "east" : "right",
33876         "north" : "top",
33877         "south" : "bottom"
33878     },
33879
33880     sanchors : {
33881         "west" : "l",
33882         "east" : "r",
33883         "north" : "t",
33884         "south" : "b"
33885     },
33886
33887     canchors : {
33888         "west" : "tl-tr",
33889         "east" : "tr-tl",
33890         "north" : "tl-bl",
33891         "south" : "bl-tl"
33892     },
33893
33894     getAnchor : function(){
33895         return this.anchors[this.position];
33896     },
33897
33898     getCollapseAnchor : function(){
33899         return this.canchors[this.position];
33900     },
33901
33902     getSlideAnchor : function(){
33903         return this.sanchors[this.position];
33904     },
33905
33906     getAlignAdj : function(){
33907         var cm = this.cmargins;
33908         switch(this.position){
33909             case "west":
33910                 return [0, 0];
33911             break;
33912             case "east":
33913                 return [0, 0];
33914             break;
33915             case "north":
33916                 return [0, 0];
33917             break;
33918             case "south":
33919                 return [0, 0];
33920             break;
33921         }
33922     },
33923
33924     getExpandAdj : function(){
33925         var c = this.collapsedEl, cm = this.cmargins;
33926         switch(this.position){
33927             case "west":
33928                 return [-(cm.right+c.getWidth()+cm.left), 0];
33929             break;
33930             case "east":
33931                 return [cm.right+c.getWidth()+cm.left, 0];
33932             break;
33933             case "north":
33934                 return [0, -(cm.top+cm.bottom+c.getHeight())];
33935             break;
33936             case "south":
33937                 return [0, cm.top+cm.bottom+c.getHeight()];
33938             break;
33939         }
33940     }
33941 });/*
33942  * Based on:
33943  * Ext JS Library 1.1.1
33944  * Copyright(c) 2006-2007, Ext JS, LLC.
33945  *
33946  * Originally Released Under LGPL - original licence link has changed is not relivant.
33947  *
33948  * Fork - LGPL
33949  * <script type="text/javascript">
33950  */
33951 /*
33952  * These classes are private internal classes
33953  */
33954 Roo.bootstrap.layout.Center = function(config){
33955     config.region = "center";
33956     Roo.bootstrap.layout.Region.call(this, config);
33957     this.visible = true;
33958     this.minWidth = config.minWidth || 20;
33959     this.minHeight = config.minHeight || 20;
33960 };
33961
33962 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
33963     hide : function(){
33964         // center panel can't be hidden
33965     },
33966     
33967     show : function(){
33968         // center panel can't be hidden
33969     },
33970     
33971     getMinWidth: function(){
33972         return this.minWidth;
33973     },
33974     
33975     getMinHeight: function(){
33976         return this.minHeight;
33977     }
33978 });
33979
33980
33981
33982
33983  
33984
33985
33986
33987
33988
33989 Roo.bootstrap.layout.North = function(config)
33990 {
33991     config.region = 'north';
33992     config.cursor = 'n-resize';
33993     
33994     Roo.bootstrap.layout.Split.call(this, config);
33995     
33996     
33997     if(this.split){
33998         this.split.placement = Roo.bootstrap.SplitBar.TOP;
33999         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
34000         this.split.el.addClass("roo-layout-split-v");
34001     }
34002     var size = config.initialSize || config.height;
34003     if(typeof size != "undefined"){
34004         this.el.setHeight(size);
34005     }
34006 };
34007 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
34008 {
34009     orientation: Roo.bootstrap.SplitBar.VERTICAL,
34010     
34011     
34012     
34013     getBox : function(){
34014         if(this.collapsed){
34015             return this.collapsedEl.getBox();
34016         }
34017         var box = this.el.getBox();
34018         if(this.split){
34019             box.height += this.split.el.getHeight();
34020         }
34021         return box;
34022     },
34023     
34024     updateBox : function(box){
34025         if(this.split && !this.collapsed){
34026             box.height -= this.split.el.getHeight();
34027             this.split.el.setLeft(box.x);
34028             this.split.el.setTop(box.y+box.height);
34029             this.split.el.setWidth(box.width);
34030         }
34031         if(this.collapsed){
34032             this.updateBody(box.width, null);
34033         }
34034         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34035     }
34036 });
34037
34038
34039
34040
34041
34042 Roo.bootstrap.layout.South = function(config){
34043     config.region = 'south';
34044     config.cursor = 's-resize';
34045     Roo.bootstrap.layout.Split.call(this, config);
34046     if(this.split){
34047         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
34048         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
34049         this.split.el.addClass("roo-layout-split-v");
34050     }
34051     var size = config.initialSize || config.height;
34052     if(typeof size != "undefined"){
34053         this.el.setHeight(size);
34054     }
34055 };
34056
34057 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
34058     orientation: Roo.bootstrap.SplitBar.VERTICAL,
34059     getBox : function(){
34060         if(this.collapsed){
34061             return this.collapsedEl.getBox();
34062         }
34063         var box = this.el.getBox();
34064         if(this.split){
34065             var sh = this.split.el.getHeight();
34066             box.height += sh;
34067             box.y -= sh;
34068         }
34069         return box;
34070     },
34071     
34072     updateBox : function(box){
34073         if(this.split && !this.collapsed){
34074             var sh = this.split.el.getHeight();
34075             box.height -= sh;
34076             box.y += sh;
34077             this.split.el.setLeft(box.x);
34078             this.split.el.setTop(box.y-sh);
34079             this.split.el.setWidth(box.width);
34080         }
34081         if(this.collapsed){
34082             this.updateBody(box.width, null);
34083         }
34084         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34085     }
34086 });
34087
34088 Roo.bootstrap.layout.East = function(config){
34089     config.region = "east";
34090     config.cursor = "e-resize";
34091     Roo.bootstrap.layout.Split.call(this, config);
34092     if(this.split){
34093         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
34094         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
34095         this.split.el.addClass("roo-layout-split-h");
34096     }
34097     var size = config.initialSize || config.width;
34098     if(typeof size != "undefined"){
34099         this.el.setWidth(size);
34100     }
34101 };
34102 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
34103     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
34104     getBox : function(){
34105         if(this.collapsed){
34106             return this.collapsedEl.getBox();
34107         }
34108         var box = this.el.getBox();
34109         if(this.split){
34110             var sw = this.split.el.getWidth();
34111             box.width += sw;
34112             box.x -= sw;
34113         }
34114         return box;
34115     },
34116
34117     updateBox : function(box){
34118         if(this.split && !this.collapsed){
34119             var sw = this.split.el.getWidth();
34120             box.width -= sw;
34121             this.split.el.setLeft(box.x);
34122             this.split.el.setTop(box.y);
34123             this.split.el.setHeight(box.height);
34124             box.x += sw;
34125         }
34126         if(this.collapsed){
34127             this.updateBody(null, box.height);
34128         }
34129         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34130     }
34131 });
34132
34133 Roo.bootstrap.layout.West = function(config){
34134     config.region = "west";
34135     config.cursor = "w-resize";
34136     
34137     Roo.bootstrap.layout.Split.call(this, config);
34138     if(this.split){
34139         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
34140         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
34141         this.split.el.addClass("roo-layout-split-h");
34142     }
34143     
34144 };
34145 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
34146     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
34147     
34148     onRender: function(ctr, pos)
34149     {
34150         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
34151         var size = this.config.initialSize || this.config.width;
34152         if(typeof size != "undefined"){
34153             this.el.setWidth(size);
34154         }
34155     },
34156     
34157     getBox : function(){
34158         if(this.collapsed){
34159             return this.collapsedEl.getBox();
34160         }
34161         var box = this.el.getBox();
34162         if(this.split){
34163             box.width += this.split.el.getWidth();
34164         }
34165         return box;
34166     },
34167     
34168     updateBox : function(box){
34169         if(this.split && !this.collapsed){
34170             var sw = this.split.el.getWidth();
34171             box.width -= sw;
34172             this.split.el.setLeft(box.x+box.width);
34173             this.split.el.setTop(box.y);
34174             this.split.el.setHeight(box.height);
34175         }
34176         if(this.collapsed){
34177             this.updateBody(null, box.height);
34178         }
34179         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34180     }
34181 });
34182 Roo.namespace("Roo.bootstrap.panel");/*
34183  * Based on:
34184  * Ext JS Library 1.1.1
34185  * Copyright(c) 2006-2007, Ext JS, LLC.
34186  *
34187  * Originally Released Under LGPL - original licence link has changed is not relivant.
34188  *
34189  * Fork - LGPL
34190  * <script type="text/javascript">
34191  */
34192 /**
34193  * @class Roo.ContentPanel
34194  * @extends Roo.util.Observable
34195  * A basic ContentPanel element.
34196  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
34197  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
34198  * @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
34199  * @cfg {Boolean}   closable      True if the panel can be closed/removed
34200  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
34201  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
34202  * @cfg {Toolbar}   toolbar       A toolbar for this panel
34203  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
34204  * @cfg {String} title          The title for this panel
34205  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
34206  * @cfg {String} url            Calls {@link #setUrl} with this value
34207  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
34208  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
34209  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
34210  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
34211
34212  * @constructor
34213  * Create a new ContentPanel.
34214  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
34215  * @param {String/Object} config A string to set only the title or a config object
34216  * @param {String} content (optional) Set the HTML content for this panel
34217  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
34218  */
34219 Roo.bootstrap.panel.Content = function( config){
34220     
34221     var el = config.el;
34222     var content = config.content;
34223
34224     if(config.autoCreate){ // xtype is available if this is called from factory
34225         el = Roo.id();
34226     }
34227     this.el = Roo.get(el);
34228     if(!this.el && config && config.autoCreate){
34229         if(typeof config.autoCreate == "object"){
34230             if(!config.autoCreate.id){
34231                 config.autoCreate.id = config.id||el;
34232             }
34233             this.el = Roo.DomHelper.append(document.body,
34234                         config.autoCreate, true);
34235         }else{
34236             var elcfg =  {   tag: "div",
34237                             cls: "roo-layout-inactive-content",
34238                             id: config.id||el
34239                             };
34240             if (config.html) {
34241                 elcfg.html = config.html;
34242                 
34243             }
34244                         
34245             this.el = Roo.DomHelper.append(document.body, elcfg , true);
34246         }
34247     } 
34248     this.closable = false;
34249     this.loaded = false;
34250     this.active = false;
34251    
34252       
34253     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
34254         
34255         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
34256         
34257         this.wrapEl = this.el.wrap();
34258         var ti = [];
34259         if (config.toolbar.items) {
34260             ti = config.toolbar.items ;
34261             delete config.toolbar.items ;
34262         }
34263         
34264         var nitems = [];
34265         this.toolbar.render(this.wrapEl, 'before');
34266         for(var i =0;i < ti.length;i++) {
34267           //  Roo.log(['add child', items[i]]);
34268             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34269         }
34270         this.toolbar.items = nitems;
34271         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
34272         delete config.toolbar;
34273         
34274     }
34275     /*
34276     // xtype created footer. - not sure if will work as we normally have to render first..
34277     if (this.footer && !this.footer.el && this.footer.xtype) {
34278         if (!this.wrapEl) {
34279             this.wrapEl = this.el.wrap();
34280         }
34281     
34282         this.footer.container = this.wrapEl.createChild();
34283          
34284         this.footer = Roo.factory(this.footer, Roo);
34285         
34286     }
34287     */
34288     
34289      if(typeof config == "string"){
34290         this.title = config;
34291     }else{
34292         Roo.apply(this, config);
34293     }
34294     
34295     if(this.resizeEl){
34296         this.resizeEl = Roo.get(this.resizeEl, true);
34297     }else{
34298         this.resizeEl = this.el;
34299     }
34300     // handle view.xtype
34301     
34302  
34303     
34304     
34305     this.addEvents({
34306         /**
34307          * @event activate
34308          * Fires when this panel is activated. 
34309          * @param {Roo.ContentPanel} this
34310          */
34311         "activate" : true,
34312         /**
34313          * @event deactivate
34314          * Fires when this panel is activated. 
34315          * @param {Roo.ContentPanel} this
34316          */
34317         "deactivate" : true,
34318
34319         /**
34320          * @event resize
34321          * Fires when this panel is resized if fitToFrame is true.
34322          * @param {Roo.ContentPanel} this
34323          * @param {Number} width The width after any component adjustments
34324          * @param {Number} height The height after any component adjustments
34325          */
34326         "resize" : true,
34327         
34328          /**
34329          * @event render
34330          * Fires when this tab is created
34331          * @param {Roo.ContentPanel} this
34332          */
34333         "render" : true
34334         
34335         
34336         
34337     });
34338     
34339
34340     
34341     
34342     if(this.autoScroll){
34343         this.resizeEl.setStyle("overflow", "auto");
34344     } else {
34345         // fix randome scrolling
34346         //this.el.on('scroll', function() {
34347         //    Roo.log('fix random scolling');
34348         //    this.scrollTo('top',0); 
34349         //});
34350     }
34351     content = content || this.content;
34352     if(content){
34353         this.setContent(content);
34354     }
34355     if(config && config.url){
34356         this.setUrl(this.url, this.params, this.loadOnce);
34357     }
34358     
34359     
34360     
34361     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
34362     
34363     if (this.view && typeof(this.view.xtype) != 'undefined') {
34364         this.view.el = this.el.appendChild(document.createElement("div"));
34365         this.view = Roo.factory(this.view); 
34366         this.view.render  &&  this.view.render(false, '');  
34367     }
34368     
34369     
34370     this.fireEvent('render', this);
34371 };
34372
34373 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
34374     tabTip:'',
34375     setRegion : function(region){
34376         this.region = region;
34377         this.setActiveClass(region && !this.background);
34378     },
34379     
34380     
34381     setActiveClass: function(state)
34382     {
34383         if(state){
34384            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
34385            this.el.setStyle('position','relative');
34386         }else{
34387            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
34388            this.el.setStyle('position', 'absolute');
34389         } 
34390     },
34391     
34392     /**
34393      * Returns the toolbar for this Panel if one was configured. 
34394      * @return {Roo.Toolbar} 
34395      */
34396     getToolbar : function(){
34397         return this.toolbar;
34398     },
34399     
34400     setActiveState : function(active)
34401     {
34402         this.active = active;
34403         this.setActiveClass(active);
34404         if(!active){
34405             this.fireEvent("deactivate", this);
34406         }else{
34407             this.fireEvent("activate", this);
34408         }
34409     },
34410     /**
34411      * Updates this panel's element
34412      * @param {String} content The new content
34413      * @param {Boolean} loadScripts (optional) true to look for and process scripts
34414     */
34415     setContent : function(content, loadScripts){
34416         this.el.update(content, loadScripts);
34417     },
34418
34419     ignoreResize : function(w, h){
34420         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
34421             return true;
34422         }else{
34423             this.lastSize = {width: w, height: h};
34424             return false;
34425         }
34426     },
34427     /**
34428      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
34429      * @return {Roo.UpdateManager} The UpdateManager
34430      */
34431     getUpdateManager : function(){
34432         return this.el.getUpdateManager();
34433     },
34434      /**
34435      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
34436      * @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:
34437 <pre><code>
34438 panel.load({
34439     url: "your-url.php",
34440     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
34441     callback: yourFunction,
34442     scope: yourObject, //(optional scope)
34443     discardUrl: false,
34444     nocache: false,
34445     text: "Loading...",
34446     timeout: 30,
34447     scripts: false
34448 });
34449 </code></pre>
34450      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
34451      * 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.
34452      * @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}
34453      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
34454      * @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.
34455      * @return {Roo.ContentPanel} this
34456      */
34457     load : function(){
34458         var um = this.el.getUpdateManager();
34459         um.update.apply(um, arguments);
34460         return this;
34461     },
34462
34463
34464     /**
34465      * 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.
34466      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
34467      * @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)
34468      * @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)
34469      * @return {Roo.UpdateManager} The UpdateManager
34470      */
34471     setUrl : function(url, params, loadOnce){
34472         if(this.refreshDelegate){
34473             this.removeListener("activate", this.refreshDelegate);
34474         }
34475         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34476         this.on("activate", this.refreshDelegate);
34477         return this.el.getUpdateManager();
34478     },
34479     
34480     _handleRefresh : function(url, params, loadOnce){
34481         if(!loadOnce || !this.loaded){
34482             var updater = this.el.getUpdateManager();
34483             updater.update(url, params, this._setLoaded.createDelegate(this));
34484         }
34485     },
34486     
34487     _setLoaded : function(){
34488         this.loaded = true;
34489     }, 
34490     
34491     /**
34492      * Returns this panel's id
34493      * @return {String} 
34494      */
34495     getId : function(){
34496         return this.el.id;
34497     },
34498     
34499     /** 
34500      * Returns this panel's element - used by regiosn to add.
34501      * @return {Roo.Element} 
34502      */
34503     getEl : function(){
34504         return this.wrapEl || this.el;
34505     },
34506     
34507    
34508     
34509     adjustForComponents : function(width, height)
34510     {
34511         //Roo.log('adjustForComponents ');
34512         if(this.resizeEl != this.el){
34513             width -= this.el.getFrameWidth('lr');
34514             height -= this.el.getFrameWidth('tb');
34515         }
34516         if(this.toolbar){
34517             var te = this.toolbar.getEl();
34518             height -= te.getHeight();
34519             te.setWidth(width);
34520         }
34521         if(this.footer){
34522             var te = this.footer.getEl();
34523             Roo.log("footer:" + te.getHeight());
34524             
34525             height -= te.getHeight();
34526             te.setWidth(width);
34527         }
34528         
34529         
34530         if(this.adjustments){
34531             width += this.adjustments[0];
34532             height += this.adjustments[1];
34533         }
34534         return {"width": width, "height": height};
34535     },
34536     
34537     setSize : function(width, height){
34538         if(this.fitToFrame && !this.ignoreResize(width, height)){
34539             if(this.fitContainer && this.resizeEl != this.el){
34540                 this.el.setSize(width, height);
34541             }
34542             var size = this.adjustForComponents(width, height);
34543             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
34544             this.fireEvent('resize', this, size.width, size.height);
34545         }
34546     },
34547     
34548     /**
34549      * Returns this panel's title
34550      * @return {String} 
34551      */
34552     getTitle : function(){
34553         return this.title;
34554     },
34555     
34556     /**
34557      * Set this panel's title
34558      * @param {String} title
34559      */
34560     setTitle : function(title){
34561         this.title = title;
34562         if(this.region){
34563             this.region.updatePanelTitle(this, title);
34564         }
34565     },
34566     
34567     /**
34568      * Returns true is this panel was configured to be closable
34569      * @return {Boolean} 
34570      */
34571     isClosable : function(){
34572         return this.closable;
34573     },
34574     
34575     beforeSlide : function(){
34576         this.el.clip();
34577         this.resizeEl.clip();
34578     },
34579     
34580     afterSlide : function(){
34581         this.el.unclip();
34582         this.resizeEl.unclip();
34583     },
34584     
34585     /**
34586      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
34587      *   Will fail silently if the {@link #setUrl} method has not been called.
34588      *   This does not activate the panel, just updates its content.
34589      */
34590     refresh : function(){
34591         if(this.refreshDelegate){
34592            this.loaded = false;
34593            this.refreshDelegate();
34594         }
34595     },
34596     
34597     /**
34598      * Destroys this panel
34599      */
34600     destroy : function(){
34601         this.el.removeAllListeners();
34602         var tempEl = document.createElement("span");
34603         tempEl.appendChild(this.el.dom);
34604         tempEl.innerHTML = "";
34605         this.el.remove();
34606         this.el = null;
34607     },
34608     
34609     /**
34610      * form - if the content panel contains a form - this is a reference to it.
34611      * @type {Roo.form.Form}
34612      */
34613     form : false,
34614     /**
34615      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
34616      *    This contains a reference to it.
34617      * @type {Roo.View}
34618      */
34619     view : false,
34620     
34621       /**
34622      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
34623      * <pre><code>
34624
34625 layout.addxtype({
34626        xtype : 'Form',
34627        items: [ .... ]
34628    }
34629 );
34630
34631 </code></pre>
34632      * @param {Object} cfg Xtype definition of item to add.
34633      */
34634     
34635     
34636     getChildContainer: function () {
34637         return this.getEl();
34638     }
34639     
34640     
34641     /*
34642         var  ret = new Roo.factory(cfg);
34643         return ret;
34644         
34645         
34646         // add form..
34647         if (cfg.xtype.match(/^Form$/)) {
34648             
34649             var el;
34650             //if (this.footer) {
34651             //    el = this.footer.container.insertSibling(false, 'before');
34652             //} else {
34653                 el = this.el.createChild();
34654             //}
34655
34656             this.form = new  Roo.form.Form(cfg);
34657             
34658             
34659             if ( this.form.allItems.length) {
34660                 this.form.render(el.dom);
34661             }
34662             return this.form;
34663         }
34664         // should only have one of theses..
34665         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
34666             // views.. should not be just added - used named prop 'view''
34667             
34668             cfg.el = this.el.appendChild(document.createElement("div"));
34669             // factory?
34670             
34671             var ret = new Roo.factory(cfg);
34672              
34673              ret.render && ret.render(false, ''); // render blank..
34674             this.view = ret;
34675             return ret;
34676         }
34677         return false;
34678     }
34679     \*/
34680 });
34681  
34682 /**
34683  * @class Roo.bootstrap.panel.Grid
34684  * @extends Roo.bootstrap.panel.Content
34685  * @constructor
34686  * Create a new GridPanel.
34687  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
34688  * @param {Object} config A the config object
34689   
34690  */
34691
34692
34693
34694 Roo.bootstrap.panel.Grid = function(config)
34695 {
34696     
34697       
34698     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
34699         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
34700
34701     config.el = this.wrapper;
34702     //this.el = this.wrapper;
34703     
34704       if (config.container) {
34705         // ctor'ed from a Border/panel.grid
34706         
34707         
34708         this.wrapper.setStyle("overflow", "hidden");
34709         this.wrapper.addClass('roo-grid-container');
34710
34711     }
34712     
34713     
34714     if(config.toolbar){
34715         var tool_el = this.wrapper.createChild();    
34716         this.toolbar = Roo.factory(config.toolbar);
34717         var ti = [];
34718         if (config.toolbar.items) {
34719             ti = config.toolbar.items ;
34720             delete config.toolbar.items ;
34721         }
34722         
34723         var nitems = [];
34724         this.toolbar.render(tool_el);
34725         for(var i =0;i < ti.length;i++) {
34726           //  Roo.log(['add child', items[i]]);
34727             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34728         }
34729         this.toolbar.items = nitems;
34730         
34731         delete config.toolbar;
34732     }
34733     
34734     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
34735     config.grid.scrollBody = true;;
34736     config.grid.monitorWindowResize = false; // turn off autosizing
34737     config.grid.autoHeight = false;
34738     config.grid.autoWidth = false;
34739     
34740     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
34741     
34742     if (config.background) {
34743         // render grid on panel activation (if panel background)
34744         this.on('activate', function(gp) {
34745             if (!gp.grid.rendered) {
34746                 gp.grid.render(this.wrapper);
34747                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
34748             }
34749         });
34750             
34751     } else {
34752         this.grid.render(this.wrapper);
34753         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
34754
34755     }
34756     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
34757     // ??? needed ??? config.el = this.wrapper;
34758     
34759     
34760     
34761   
34762     // xtype created footer. - not sure if will work as we normally have to render first..
34763     if (this.footer && !this.footer.el && this.footer.xtype) {
34764         
34765         var ctr = this.grid.getView().getFooterPanel(true);
34766         this.footer.dataSource = this.grid.dataSource;
34767         this.footer = Roo.factory(this.footer, Roo);
34768         this.footer.render(ctr);
34769         
34770     }
34771     
34772     
34773     
34774     
34775      
34776 };
34777
34778 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
34779     getId : function(){
34780         return this.grid.id;
34781     },
34782     
34783     /**
34784      * Returns the grid for this panel
34785      * @return {Roo.bootstrap.Table} 
34786      */
34787     getGrid : function(){
34788         return this.grid;    
34789     },
34790     
34791     setSize : function(width, height){
34792         if(!this.ignoreResize(width, height)){
34793             var grid = this.grid;
34794             var size = this.adjustForComponents(width, height);
34795             var gridel = grid.getGridEl();
34796             gridel.setSize(size.width, size.height);
34797             /*
34798             var thd = grid.getGridEl().select('thead',true).first();
34799             var tbd = grid.getGridEl().select('tbody', true).first();
34800             if (tbd) {
34801                 tbd.setSize(width, height - thd.getHeight());
34802             }
34803             */
34804             grid.autoSize();
34805         }
34806     },
34807      
34808     
34809     
34810     beforeSlide : function(){
34811         this.grid.getView().scroller.clip();
34812     },
34813     
34814     afterSlide : function(){
34815         this.grid.getView().scroller.unclip();
34816     },
34817     
34818     destroy : function(){
34819         this.grid.destroy();
34820         delete this.grid;
34821         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
34822     }
34823 });
34824
34825 /**
34826  * @class Roo.bootstrap.panel.Nest
34827  * @extends Roo.bootstrap.panel.Content
34828  * @constructor
34829  * Create a new Panel, that can contain a layout.Border.
34830  * 
34831  * 
34832  * @param {Roo.BorderLayout} layout The layout for this panel
34833  * @param {String/Object} config A string to set only the title or a config object
34834  */
34835 Roo.bootstrap.panel.Nest = function(config)
34836 {
34837     // construct with only one argument..
34838     /* FIXME - implement nicer consturctors
34839     if (layout.layout) {
34840         config = layout;
34841         layout = config.layout;
34842         delete config.layout;
34843     }
34844     if (layout.xtype && !layout.getEl) {
34845         // then layout needs constructing..
34846         layout = Roo.factory(layout, Roo);
34847     }
34848     */
34849     
34850     config.el =  config.layout.getEl();
34851     
34852     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
34853     
34854     config.layout.monitorWindowResize = false; // turn off autosizing
34855     this.layout = config.layout;
34856     this.layout.getEl().addClass("roo-layout-nested-layout");
34857     
34858     
34859     
34860     
34861 };
34862
34863 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
34864
34865     setSize : function(width, height){
34866         if(!this.ignoreResize(width, height)){
34867             var size = this.adjustForComponents(width, height);
34868             var el = this.layout.getEl();
34869             if (size.height < 1) {
34870                 el.setWidth(size.width);   
34871             } else {
34872                 el.setSize(size.width, size.height);
34873             }
34874             var touch = el.dom.offsetWidth;
34875             this.layout.layout();
34876             // ie requires a double layout on the first pass
34877             if(Roo.isIE && !this.initialized){
34878                 this.initialized = true;
34879                 this.layout.layout();
34880             }
34881         }
34882     },
34883     
34884     // activate all subpanels if not currently active..
34885     
34886     setActiveState : function(active){
34887         this.active = active;
34888         this.setActiveClass(active);
34889         
34890         if(!active){
34891             this.fireEvent("deactivate", this);
34892             return;
34893         }
34894         
34895         this.fireEvent("activate", this);
34896         // not sure if this should happen before or after..
34897         if (!this.layout) {
34898             return; // should not happen..
34899         }
34900         var reg = false;
34901         for (var r in this.layout.regions) {
34902             reg = this.layout.getRegion(r);
34903             if (reg.getActivePanel()) {
34904                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
34905                 reg.setActivePanel(reg.getActivePanel());
34906                 continue;
34907             }
34908             if (!reg.panels.length) {
34909                 continue;
34910             }
34911             reg.showPanel(reg.getPanel(0));
34912         }
34913         
34914         
34915         
34916         
34917     },
34918     
34919     /**
34920      * Returns the nested BorderLayout for this panel
34921      * @return {Roo.BorderLayout} 
34922      */
34923     getLayout : function(){
34924         return this.layout;
34925     },
34926     
34927      /**
34928      * Adds a xtype elements to the layout of the nested panel
34929      * <pre><code>
34930
34931 panel.addxtype({
34932        xtype : 'ContentPanel',
34933        region: 'west',
34934        items: [ .... ]
34935    }
34936 );
34937
34938 panel.addxtype({
34939         xtype : 'NestedLayoutPanel',
34940         region: 'west',
34941         layout: {
34942            center: { },
34943            west: { }   
34944         },
34945         items : [ ... list of content panels or nested layout panels.. ]
34946    }
34947 );
34948 </code></pre>
34949      * @param {Object} cfg Xtype definition of item to add.
34950      */
34951     addxtype : function(cfg) {
34952         return this.layout.addxtype(cfg);
34953     
34954     }
34955 });        /*
34956  * Based on:
34957  * Ext JS Library 1.1.1
34958  * Copyright(c) 2006-2007, Ext JS, LLC.
34959  *
34960  * Originally Released Under LGPL - original licence link has changed is not relivant.
34961  *
34962  * Fork - LGPL
34963  * <script type="text/javascript">
34964  */
34965 /**
34966  * @class Roo.TabPanel
34967  * @extends Roo.util.Observable
34968  * A lightweight tab container.
34969  * <br><br>
34970  * Usage:
34971  * <pre><code>
34972 // basic tabs 1, built from existing content
34973 var tabs = new Roo.TabPanel("tabs1");
34974 tabs.addTab("script", "View Script");
34975 tabs.addTab("markup", "View Markup");
34976 tabs.activate("script");
34977
34978 // more advanced tabs, built from javascript
34979 var jtabs = new Roo.TabPanel("jtabs");
34980 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
34981
34982 // set up the UpdateManager
34983 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
34984 var updater = tab2.getUpdateManager();
34985 updater.setDefaultUrl("ajax1.htm");
34986 tab2.on('activate', updater.refresh, updater, true);
34987
34988 // Use setUrl for Ajax loading
34989 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
34990 tab3.setUrl("ajax2.htm", null, true);
34991
34992 // Disabled tab
34993 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
34994 tab4.disable();
34995
34996 jtabs.activate("jtabs-1");
34997  * </code></pre>
34998  * @constructor
34999  * Create a new TabPanel.
35000  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
35001  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
35002  */
35003 Roo.bootstrap.panel.Tabs = function(config){
35004     /**
35005     * The container element for this TabPanel.
35006     * @type Roo.Element
35007     */
35008     this.el = Roo.get(config.el);
35009     delete config.el;
35010     if(config){
35011         if(typeof config == "boolean"){
35012             this.tabPosition = config ? "bottom" : "top";
35013         }else{
35014             Roo.apply(this, config);
35015         }
35016     }
35017     
35018     if(this.tabPosition == "bottom"){
35019         this.bodyEl = Roo.get(this.createBody(this.el.dom));
35020         this.el.addClass("roo-tabs-bottom");
35021     }
35022     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
35023     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
35024     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
35025     if(Roo.isIE){
35026         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
35027     }
35028     if(this.tabPosition != "bottom"){
35029         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
35030          * @type Roo.Element
35031          */
35032         this.bodyEl = Roo.get(this.createBody(this.el.dom));
35033         this.el.addClass("roo-tabs-top");
35034     }
35035     this.items = [];
35036
35037     this.bodyEl.setStyle("position", "relative");
35038
35039     this.active = null;
35040     this.activateDelegate = this.activate.createDelegate(this);
35041
35042     this.addEvents({
35043         /**
35044          * @event tabchange
35045          * Fires when the active tab changes
35046          * @param {Roo.TabPanel} this
35047          * @param {Roo.TabPanelItem} activePanel The new active tab
35048          */
35049         "tabchange": true,
35050         /**
35051          * @event beforetabchange
35052          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
35053          * @param {Roo.TabPanel} this
35054          * @param {Object} e Set cancel to true on this object to cancel the tab change
35055          * @param {Roo.TabPanelItem} tab The tab being changed to
35056          */
35057         "beforetabchange" : true
35058     });
35059
35060     Roo.EventManager.onWindowResize(this.onResize, this);
35061     this.cpad = this.el.getPadding("lr");
35062     this.hiddenCount = 0;
35063
35064
35065     // toolbar on the tabbar support...
35066     if (this.toolbar) {
35067         alert("no toolbar support yet");
35068         this.toolbar  = false;
35069         /*
35070         var tcfg = this.toolbar;
35071         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
35072         this.toolbar = new Roo.Toolbar(tcfg);
35073         if (Roo.isSafari) {
35074             var tbl = tcfg.container.child('table', true);
35075             tbl.setAttribute('width', '100%');
35076         }
35077         */
35078         
35079     }
35080    
35081
35082
35083     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
35084 };
35085
35086 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
35087     /*
35088      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
35089      */
35090     tabPosition : "top",
35091     /*
35092      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
35093      */
35094     currentTabWidth : 0,
35095     /*
35096      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
35097      */
35098     minTabWidth : 40,
35099     /*
35100      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
35101      */
35102     maxTabWidth : 250,
35103     /*
35104      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
35105      */
35106     preferredTabWidth : 175,
35107     /*
35108      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
35109      */
35110     resizeTabs : false,
35111     /*
35112      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
35113      */
35114     monitorResize : true,
35115     /*
35116      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
35117      */
35118     toolbar : false,
35119
35120     /**
35121      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
35122      * @param {String} id The id of the div to use <b>or create</b>
35123      * @param {String} text The text for the tab
35124      * @param {String} content (optional) Content to put in the TabPanelItem body
35125      * @param {Boolean} closable (optional) True to create a close icon on the tab
35126      * @return {Roo.TabPanelItem} The created TabPanelItem
35127      */
35128     addTab : function(id, text, content, closable)
35129     {
35130         var item = new Roo.bootstrap.panel.TabItem({
35131             panel: this,
35132             id : id,
35133             text : text,
35134             closable : closable
35135         });
35136         this.addTabItem(item);
35137         if(content){
35138             item.setContent(content);
35139         }
35140         return item;
35141     },
35142
35143     /**
35144      * Returns the {@link Roo.TabPanelItem} with the specified id/index
35145      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
35146      * @return {Roo.TabPanelItem}
35147      */
35148     getTab : function(id){
35149         return this.items[id];
35150     },
35151
35152     /**
35153      * Hides the {@link Roo.TabPanelItem} with the specified id/index
35154      * @param {String/Number} id The id or index of the TabPanelItem to hide.
35155      */
35156     hideTab : function(id){
35157         var t = this.items[id];
35158         if(!t.isHidden()){
35159            t.setHidden(true);
35160            this.hiddenCount++;
35161            this.autoSizeTabs();
35162         }
35163     },
35164
35165     /**
35166      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
35167      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
35168      */
35169     unhideTab : function(id){
35170         var t = this.items[id];
35171         if(t.isHidden()){
35172            t.setHidden(false);
35173            this.hiddenCount--;
35174            this.autoSizeTabs();
35175         }
35176     },
35177
35178     /**
35179      * Adds an existing {@link Roo.TabPanelItem}.
35180      * @param {Roo.TabPanelItem} item The TabPanelItem to add
35181      */
35182     addTabItem : function(item){
35183         this.items[item.id] = item;
35184         this.items.push(item);
35185       //  if(this.resizeTabs){
35186     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
35187   //         this.autoSizeTabs();
35188 //        }else{
35189 //            item.autoSize();
35190        // }
35191     },
35192
35193     /**
35194      * Removes a {@link Roo.TabPanelItem}.
35195      * @param {String/Number} id The id or index of the TabPanelItem to remove.
35196      */
35197     removeTab : function(id){
35198         var items = this.items;
35199         var tab = items[id];
35200         if(!tab) { return; }
35201         var index = items.indexOf(tab);
35202         if(this.active == tab && items.length > 1){
35203             var newTab = this.getNextAvailable(index);
35204             if(newTab) {
35205                 newTab.activate();
35206             }
35207         }
35208         this.stripEl.dom.removeChild(tab.pnode.dom);
35209         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
35210             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
35211         }
35212         items.splice(index, 1);
35213         delete this.items[tab.id];
35214         tab.fireEvent("close", tab);
35215         tab.purgeListeners();
35216         this.autoSizeTabs();
35217     },
35218
35219     getNextAvailable : function(start){
35220         var items = this.items;
35221         var index = start;
35222         // look for a next tab that will slide over to
35223         // replace the one being removed
35224         while(index < items.length){
35225             var item = items[++index];
35226             if(item && !item.isHidden()){
35227                 return item;
35228             }
35229         }
35230         // if one isn't found select the previous tab (on the left)
35231         index = start;
35232         while(index >= 0){
35233             var item = items[--index];
35234             if(item && !item.isHidden()){
35235                 return item;
35236             }
35237         }
35238         return null;
35239     },
35240
35241     /**
35242      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
35243      * @param {String/Number} id The id or index of the TabPanelItem to disable.
35244      */
35245     disableTab : function(id){
35246         var tab = this.items[id];
35247         if(tab && this.active != tab){
35248             tab.disable();
35249         }
35250     },
35251
35252     /**
35253      * Enables a {@link Roo.TabPanelItem} that is disabled.
35254      * @param {String/Number} id The id or index of the TabPanelItem to enable.
35255      */
35256     enableTab : function(id){
35257         var tab = this.items[id];
35258         tab.enable();
35259     },
35260
35261     /**
35262      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
35263      * @param {String/Number} id The id or index of the TabPanelItem to activate.
35264      * @return {Roo.TabPanelItem} The TabPanelItem.
35265      */
35266     activate : function(id){
35267         var tab = this.items[id];
35268         if(!tab){
35269             return null;
35270         }
35271         if(tab == this.active || tab.disabled){
35272             return tab;
35273         }
35274         var e = {};
35275         this.fireEvent("beforetabchange", this, e, tab);
35276         if(e.cancel !== true && !tab.disabled){
35277             if(this.active){
35278                 this.active.hide();
35279             }
35280             this.active = this.items[id];
35281             this.active.show();
35282             this.fireEvent("tabchange", this, this.active);
35283         }
35284         return tab;
35285     },
35286
35287     /**
35288      * Gets the active {@link Roo.TabPanelItem}.
35289      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
35290      */
35291     getActiveTab : function(){
35292         return this.active;
35293     },
35294
35295     /**
35296      * Updates the tab body element to fit the height of the container element
35297      * for overflow scrolling
35298      * @param {Number} targetHeight (optional) Override the starting height from the elements height
35299      */
35300     syncHeight : function(targetHeight){
35301         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35302         var bm = this.bodyEl.getMargins();
35303         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
35304         this.bodyEl.setHeight(newHeight);
35305         return newHeight;
35306     },
35307
35308     onResize : function(){
35309         if(this.monitorResize){
35310             this.autoSizeTabs();
35311         }
35312     },
35313
35314     /**
35315      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
35316      */
35317     beginUpdate : function(){
35318         this.updating = true;
35319     },
35320
35321     /**
35322      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
35323      */
35324     endUpdate : function(){
35325         this.updating = false;
35326         this.autoSizeTabs();
35327     },
35328
35329     /**
35330      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
35331      */
35332     autoSizeTabs : function(){
35333         var count = this.items.length;
35334         var vcount = count - this.hiddenCount;
35335         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
35336             return;
35337         }
35338         var w = Math.max(this.el.getWidth() - this.cpad, 10);
35339         var availWidth = Math.floor(w / vcount);
35340         var b = this.stripBody;
35341         if(b.getWidth() > w){
35342             var tabs = this.items;
35343             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
35344             if(availWidth < this.minTabWidth){
35345                 /*if(!this.sleft){    // incomplete scrolling code
35346                     this.createScrollButtons();
35347                 }
35348                 this.showScroll();
35349                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
35350             }
35351         }else{
35352             if(this.currentTabWidth < this.preferredTabWidth){
35353                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
35354             }
35355         }
35356     },
35357
35358     /**
35359      * Returns the number of tabs in this TabPanel.
35360      * @return {Number}
35361      */
35362      getCount : function(){
35363          return this.items.length;
35364      },
35365
35366     /**
35367      * Resizes all the tabs to the passed width
35368      * @param {Number} The new width
35369      */
35370     setTabWidth : function(width){
35371         this.currentTabWidth = width;
35372         for(var i = 0, len = this.items.length; i < len; i++) {
35373                 if(!this.items[i].isHidden()) {
35374                 this.items[i].setWidth(width);
35375             }
35376         }
35377     },
35378
35379     /**
35380      * Destroys this TabPanel
35381      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
35382      */
35383     destroy : function(removeEl){
35384         Roo.EventManager.removeResizeListener(this.onResize, this);
35385         for(var i = 0, len = this.items.length; i < len; i++){
35386             this.items[i].purgeListeners();
35387         }
35388         if(removeEl === true){
35389             this.el.update("");
35390             this.el.remove();
35391         }
35392     },
35393     
35394     createStrip : function(container)
35395     {
35396         var strip = document.createElement("nav");
35397         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
35398         container.appendChild(strip);
35399         return strip;
35400     },
35401     
35402     createStripList : function(strip)
35403     {
35404         // div wrapper for retard IE
35405         // returns the "tr" element.
35406         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
35407         //'<div class="x-tabs-strip-wrap">'+
35408           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
35409           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
35410         return strip.firstChild; //.firstChild.firstChild.firstChild;
35411     },
35412     createBody : function(container)
35413     {
35414         var body = document.createElement("div");
35415         Roo.id(body, "tab-body");
35416         //Roo.fly(body).addClass("x-tabs-body");
35417         Roo.fly(body).addClass("tab-content");
35418         container.appendChild(body);
35419         return body;
35420     },
35421     createItemBody :function(bodyEl, id){
35422         var body = Roo.getDom(id);
35423         if(!body){
35424             body = document.createElement("div");
35425             body.id = id;
35426         }
35427         //Roo.fly(body).addClass("x-tabs-item-body");
35428         Roo.fly(body).addClass("tab-pane");
35429          bodyEl.insertBefore(body, bodyEl.firstChild);
35430         return body;
35431     },
35432     /** @private */
35433     createStripElements :  function(stripEl, text, closable)
35434     {
35435         var td = document.createElement("li"); // was td..
35436         
35437         
35438         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
35439         
35440         
35441         stripEl.appendChild(td);
35442         /*if(closable){
35443             td.className = "x-tabs-closable";
35444             if(!this.closeTpl){
35445                 this.closeTpl = new Roo.Template(
35446                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
35447                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
35448                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
35449                 );
35450             }
35451             var el = this.closeTpl.overwrite(td, {"text": text});
35452             var close = el.getElementsByTagName("div")[0];
35453             var inner = el.getElementsByTagName("em")[0];
35454             return {"el": el, "close": close, "inner": inner};
35455         } else {
35456         */
35457         // not sure what this is..
35458             if(!this.tabTpl){
35459                 //this.tabTpl = new Roo.Template(
35460                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
35461                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
35462                 //);
35463                 this.tabTpl = new Roo.Template(
35464                    '<a href="#">' +
35465                    '<span unselectable="on"' +
35466                             (this.disableTooltips ? '' : ' title="{text}"') +
35467                             ' >{text}</span></span></a>'
35468                 );
35469                 
35470             }
35471             var el = this.tabTpl.overwrite(td, {"text": text});
35472             var inner = el.getElementsByTagName("span")[0];
35473             return {"el": el, "inner": inner};
35474         //}
35475     }
35476         
35477     
35478 });
35479
35480 /**
35481  * @class Roo.TabPanelItem
35482  * @extends Roo.util.Observable
35483  * Represents an individual item (tab plus body) in a TabPanel.
35484  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
35485  * @param {String} id The id of this TabPanelItem
35486  * @param {String} text The text for the tab of this TabPanelItem
35487  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
35488  */
35489 Roo.bootstrap.panel.TabItem = function(config){
35490     /**
35491      * The {@link Roo.TabPanel} this TabPanelItem belongs to
35492      * @type Roo.TabPanel
35493      */
35494     this.tabPanel = config.panel;
35495     /**
35496      * The id for this TabPanelItem
35497      * @type String
35498      */
35499     this.id = config.id;
35500     /** @private */
35501     this.disabled = false;
35502     /** @private */
35503     this.text = config.text;
35504     /** @private */
35505     this.loaded = false;
35506     this.closable = config.closable;
35507
35508     /**
35509      * The body element for this TabPanelItem.
35510      * @type Roo.Element
35511      */
35512     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
35513     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
35514     this.bodyEl.setStyle("display", "block");
35515     this.bodyEl.setStyle("zoom", "1");
35516     //this.hideAction();
35517
35518     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable);
35519     /** @private */
35520     this.el = Roo.get(els.el);
35521     this.inner = Roo.get(els.inner, true);
35522     this.textEl = Roo.get(this.el.dom.firstChild, true);
35523     this.pnode = Roo.get(els.el.parentNode, true);
35524     this.el.on("mousedown", this.onTabMouseDown, this);
35525     this.el.on("click", this.onTabClick, this);
35526     /** @private */
35527     if(config.closable){
35528         var c = Roo.get(els.close, true);
35529         c.dom.title = this.closeText;
35530         c.addClassOnOver("close-over");
35531         c.on("click", this.closeClick, this);
35532      }
35533
35534     this.addEvents({
35535          /**
35536          * @event activate
35537          * Fires when this tab becomes the active tab.
35538          * @param {Roo.TabPanel} tabPanel The parent TabPanel
35539          * @param {Roo.TabPanelItem} this
35540          */
35541         "activate": true,
35542         /**
35543          * @event beforeclose
35544          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
35545          * @param {Roo.TabPanelItem} this
35546          * @param {Object} e Set cancel to true on this object to cancel the close.
35547          */
35548         "beforeclose": true,
35549         /**
35550          * @event close
35551          * Fires when this tab is closed.
35552          * @param {Roo.TabPanelItem} this
35553          */
35554          "close": true,
35555         /**
35556          * @event deactivate
35557          * Fires when this tab is no longer the active tab.
35558          * @param {Roo.TabPanel} tabPanel The parent TabPanel
35559          * @param {Roo.TabPanelItem} this
35560          */
35561          "deactivate" : true
35562     });
35563     this.hidden = false;
35564
35565     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
35566 };
35567
35568 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
35569            {
35570     purgeListeners : function(){
35571        Roo.util.Observable.prototype.purgeListeners.call(this);
35572        this.el.removeAllListeners();
35573     },
35574     /**
35575      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
35576      */
35577     show : function(){
35578         this.pnode.addClass("active");
35579         this.showAction();
35580         if(Roo.isOpera){
35581             this.tabPanel.stripWrap.repaint();
35582         }
35583         this.fireEvent("activate", this.tabPanel, this);
35584     },
35585
35586     /**
35587      * Returns true if this tab is the active tab.
35588      * @return {Boolean}
35589      */
35590     isActive : function(){
35591         return this.tabPanel.getActiveTab() == this;
35592     },
35593
35594     /**
35595      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
35596      */
35597     hide : function(){
35598         this.pnode.removeClass("active");
35599         this.hideAction();
35600         this.fireEvent("deactivate", this.tabPanel, this);
35601     },
35602
35603     hideAction : function(){
35604         this.bodyEl.hide();
35605         this.bodyEl.setStyle("position", "absolute");
35606         this.bodyEl.setLeft("-20000px");
35607         this.bodyEl.setTop("-20000px");
35608     },
35609
35610     showAction : function(){
35611         this.bodyEl.setStyle("position", "relative");
35612         this.bodyEl.setTop("");
35613         this.bodyEl.setLeft("");
35614         this.bodyEl.show();
35615     },
35616
35617     /**
35618      * Set the tooltip for the tab.
35619      * @param {String} tooltip The tab's tooltip
35620      */
35621     setTooltip : function(text){
35622         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
35623             this.textEl.dom.qtip = text;
35624             this.textEl.dom.removeAttribute('title');
35625         }else{
35626             this.textEl.dom.title = text;
35627         }
35628     },
35629
35630     onTabClick : function(e){
35631         e.preventDefault();
35632         this.tabPanel.activate(this.id);
35633     },
35634
35635     onTabMouseDown : function(e){
35636         e.preventDefault();
35637         this.tabPanel.activate(this.id);
35638     },
35639 /*
35640     getWidth : function(){
35641         return this.inner.getWidth();
35642     },
35643
35644     setWidth : function(width){
35645         var iwidth = width - this.pnode.getPadding("lr");
35646         this.inner.setWidth(iwidth);
35647         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
35648         this.pnode.setWidth(width);
35649     },
35650 */
35651     /**
35652      * Show or hide the tab
35653      * @param {Boolean} hidden True to hide or false to show.
35654      */
35655     setHidden : function(hidden){
35656         this.hidden = hidden;
35657         this.pnode.setStyle("display", hidden ? "none" : "");
35658     },
35659
35660     /**
35661      * Returns true if this tab is "hidden"
35662      * @return {Boolean}
35663      */
35664     isHidden : function(){
35665         return this.hidden;
35666     },
35667
35668     /**
35669      * Returns the text for this tab
35670      * @return {String}
35671      */
35672     getText : function(){
35673         return this.text;
35674     },
35675     /*
35676     autoSize : function(){
35677         //this.el.beginMeasure();
35678         this.textEl.setWidth(1);
35679         /*
35680          *  #2804 [new] Tabs in Roojs
35681          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
35682          */
35683         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
35684         //this.el.endMeasure();
35685     //},
35686
35687     /**
35688      * Sets the text for the tab (Note: this also sets the tooltip text)
35689      * @param {String} text The tab's text and tooltip
35690      */
35691     setText : function(text){
35692         this.text = text;
35693         this.textEl.update(text);
35694         this.setTooltip(text);
35695         //if(!this.tabPanel.resizeTabs){
35696         //    this.autoSize();
35697         //}
35698     },
35699     /**
35700      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
35701      */
35702     activate : function(){
35703         this.tabPanel.activate(this.id);
35704     },
35705
35706     /**
35707      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
35708      */
35709     disable : function(){
35710         if(this.tabPanel.active != this){
35711             this.disabled = true;
35712             this.pnode.addClass("disabled");
35713         }
35714     },
35715
35716     /**
35717      * Enables this TabPanelItem if it was previously disabled.
35718      */
35719     enable : function(){
35720         this.disabled = false;
35721         this.pnode.removeClass("disabled");
35722     },
35723
35724     /**
35725      * Sets the content for this TabPanelItem.
35726      * @param {String} content The content
35727      * @param {Boolean} loadScripts true to look for and load scripts
35728      */
35729     setContent : function(content, loadScripts){
35730         this.bodyEl.update(content, loadScripts);
35731     },
35732
35733     /**
35734      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
35735      * @return {Roo.UpdateManager} The UpdateManager
35736      */
35737     getUpdateManager : function(){
35738         return this.bodyEl.getUpdateManager();
35739     },
35740
35741     /**
35742      * Set a URL to be used to load the content for this TabPanelItem.
35743      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
35744      * @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)
35745      * @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)
35746      * @return {Roo.UpdateManager} The UpdateManager
35747      */
35748     setUrl : function(url, params, loadOnce){
35749         if(this.refreshDelegate){
35750             this.un('activate', this.refreshDelegate);
35751         }
35752         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35753         this.on("activate", this.refreshDelegate);
35754         return this.bodyEl.getUpdateManager();
35755     },
35756
35757     /** @private */
35758     _handleRefresh : function(url, params, loadOnce){
35759         if(!loadOnce || !this.loaded){
35760             var updater = this.bodyEl.getUpdateManager();
35761             updater.update(url, params, this._setLoaded.createDelegate(this));
35762         }
35763     },
35764
35765     /**
35766      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
35767      *   Will fail silently if the setUrl method has not been called.
35768      *   This does not activate the panel, just updates its content.
35769      */
35770     refresh : function(){
35771         if(this.refreshDelegate){
35772            this.loaded = false;
35773            this.refreshDelegate();
35774         }
35775     },
35776
35777     /** @private */
35778     _setLoaded : function(){
35779         this.loaded = true;
35780     },
35781
35782     /** @private */
35783     closeClick : function(e){
35784         var o = {};
35785         e.stopEvent();
35786         this.fireEvent("beforeclose", this, o);
35787         if(o.cancel !== true){
35788             this.tabPanel.removeTab(this.id);
35789         }
35790     },
35791     /**
35792      * The text displayed in the tooltip for the close icon.
35793      * @type String
35794      */
35795     closeText : "Close this tab"
35796 });